diff --git a/Events.py b/Events.py index 574d83f3193b89cb6c06c0443988023125163b7d..c52d4efc2b5dbd08d01d7211b12a5c8b852a80f4 100644 --- a/Events.py +++ b/Events.py @@ -1,26 +1,29 @@ -# -*- coding: utf-8 -*- -import os, sys, time, subprocess +# -*- coding: utf-8 -*- +import os, sys, time, subprocess,signal from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler +eventgtimexz=0 class MyFileSystemEventHander(FileSystemEventHandler): def __init__(self, fn): super(MyFileSystemEventHander, self).__init__() self.restart = fn def on_any_event(self, event): - if event.src_path.endswith('.py'): - print('* 更新文件:%s' % event.src_path) - self.restart() + if '.py' in event.src_path and event.src_path.endswith('.py'): + global eventgtimexz + if time.time()-eventgtimexz > 3: + eventgtimexz=time.time() + # print('* 更新文件:%s' % event.src_path) + # time.sleep(2) + time.sleep(0.2) + self.restart() class Events: command = ['echo', 'ok'] process = None def __init__(self,argv): - argv=argv - # print(argv) - if 'python' not in argv[0]: - argv.insert(0, 'python3') + if '--server' not in argv and 'python' not in argv[0] or 'kcw.py' in argv: + argv.insert(0, 'python') self.command = argv - # print(self.command) paths = os.path.abspath('.') self.start_watch(paths, None) @@ -38,6 +41,7 @@ class Events: def restart_process(self): "重启" self.kill_process() + time.sleep(0.1) self.start_process() def start_watch(self,path, callback): @@ -48,7 +52,7 @@ class Events: self.start_process() try: while True: - time.sleep(0.5) + time.sleep(0.1) except KeyboardInterrupt: self.kill_process() # observer.stop() diff --git a/app.py b/app.py index 53cb0759a7ab085228a95c7cd752f7f929215c40..ebf10f982bce01a59420027a2bcac8c796167aff 100644 --- a/app.py +++ b/app.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import socket,time,re,os,sys,traceback,threading,urllib +import socket,time,re,os,sys,traceback,threading,urllib,cgi from . Events import Events from . common import * from . import config @@ -8,6 +8,9 @@ from datetime import datetime from threading import local from .utill import filetype from kcweb.utill.cache import cache as kcwcache +from wsgiref.simple_server import make_server +from kcweb.utill.db import mysql as kcwmysql +from kcweb.utill.db import sqlite as kcwsqlite class web: __name=None __appname=None @@ -17,120 +20,92 @@ class web: self.__appname=appname if self.__name != '__main__': def apps(env, start_response): - # REQUEST_METHOD=env['REQUEST_METHOD'] #GET - # QUERY_STRING=env['QUERY_STRING'] #a=1&b=1 - # RAW_URI=env['RAW_URI'] #/aa/bb/cc?a=1&b=1 - # SERVER_PROTOCOL=env['SERVER_PROTOCOL'] #HTTP/1.1 - # HTTP_HOST=env['HTTP_HOST'] #212.129.149.238:39010 - # HTTP_COOKIE=env['HTTP_COOKIE'] #cookie - # REMOTE_ADDR=env['REMOTE_ADDR'] #27.156.27.201 - # PATH_INFO=env['PATH_INFO'] #/aa/bb/cc - # HTTP_USER_AGENT=env['HTTP_USER_AGENT'] #Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0 - try: - env['BODY_DATA']=str(env['wsgi.input'].next(), encoding = "utf-8") - except: - env['BODY_DATA']="" p=(config.app['staticpath']+env['RAW_URI'].replace(' ','')) status='200 ok' if os.path.isfile(p): - kind = filetype.guess(p) - if kind is None: - f=open(p,"rb") - body=f.read() - f.close() - resheader=[ - ("Cache-Control","public, max-age=43200"), - ] + resheader=[("Cache-Control","public, max-age=2592000"),] + ETag=md5(p) + try: + HTTP_IF_NONE_MATCH=env['HTTP_IF_NONE_MATCH'] + except KeyError: + HTTP_IF_NONE_MATCH=None + if HTTP_IF_NONE_MATCH: + status="304 Not Modified" + body='' else: - f=open(p,"rb") - body=f.read() - f.close() - resheader=[ - ("Content-Type",kind.mime), - ("Cache-Control","public, max-age=43200"), - ("Accept-Ranges","bytes"), - # ("Content-Length",len(body)) - ] + kind = filetype.guess(p) + if kind is None: + f=open(p,"rb") + body=f.read() + f.close() + else: + f=open(p,"rb") + body=f.read() + f.close() + resheader=[ + ("Content-Type",kind.mime), + ("Cache-Control","public, max-age=43200"), + ("Accept-Ranges","bytes"), + ] + resheader.append(('Content-Type',kind.mime)) + resheader.append(("Accept-Ranges","bytes")) + dateArray = datetime.utcfromtimestamp(times()-86400) + otherStyleTime = dateArray.strftime('%a, %d %b %Y %H:%M:%S GMT') + resheader.append(("Last-Modified",otherStyleTime)) + resheader.append(("ETag",ETag)) else: + files=None + try: + CONTENT_TYPE=env['CONTENT_TYPE'] + except: + pass + else: + if 'multipart/form-data; boundary' in CONTENT_TYPE:#上传文件 + files = cgi.FieldStorage(fp=env['wsgi.input'], environ=env, keep_blank_values=True) + else: + files=None + env['files']=files + try: + env['BODY_DATA']=str(env['wsgi.input'].next(), encoding = "utf-8") + except: + env['BODY_DATA']="" status,resheader,body=self.__routes(self,env) if type(body) is bytes: pass else: body=bytes(body, encoding='utf-8') - # print(env['bodydata']) - # print("\n\nwsgi.input",env['wsgi.input']) - # print("\n\ndir(env['wsgi.input'])",dir(env['wsgi.input'])) - # print("\n\nenv['wsgi.input'].__dict__",env['wsgi.input'].__dict__) - # try: - # print("\n\nwsgi.input.buf()",env['wsgi.input'].buf()) - # except Exception as e: - # print("\n\nwsgi.input.buf() error:",e) - # try: - # print("\n\nwsgi.input.next()",env['wsgi.input'].next()) - # except Exception as e: - # print("\n\nwsgi.input.next() error:",e) - # try: - # print("\n\nwsgi.input.read()",env['wsgi.input'].read()) - # except Exception as e: - # print("\n\nwsgi.input.read() error:",e) - # try: - # print("\n\nwsgi.input.reader()",env['wsgi.input'].reader()) - # except Exception as e: - # print("\n\nwsgi.input.reader() error:",e) - # try: - # print("\n\nwsgi.input.readline()",env['wsgi.input'].readline()) - # except Exception as e: - # print("\n\nwsgi.input.readline() error:",e) - # try: - # print("\n\nwsgi.input.readlines()",env['wsgi.input'].readlines()) - # except Exception as e: - # print("\n\nwsgi.input.readlines() error:",e) - # try: - # print("wsgi.input.aa",env['wsgi.input'].get("SCRIPT_NAME", "")) - # except Exception as e: - # print("wsgi.input.get('aa') error:",e) - # try: - # print("wsgi.input.aa",env['wsgi.input']['aa']) - # except Exception as e: - # print("wsgi.input['aa'] error:",e) - # print(dir(env['wsgi.input']).getsize) - # from io import StringIO - # stdout = StringIO() - # print("Hello world!", file=stdout) - # print(file=stdout) - # h = sorted(env.items()) - # for k,v in h: - # print(k,'=',repr(v), file=stdout) - # print(stdout.getvalue().encode("utf-8")) start_response(status,resheader) return [body] return apps else: return super().__new__(self) - def run(self,host="127.0.0.1",port="39001",name='python'): + def run(self,host="127.0.0.1",port="39001",name=None): """运行开发环境 host: 监听地址 port: 端口 - name: python命令行解释机名字 默认python + name: python命令行解释机名字 """ - if len(sys.argv)==1 or (len(sys.argv)==2 and sys.argv[1]=='eventlog'): + if 'eventlog' in sys.argv or len(sys.argv)==1: filename=sys.argv[0][:-3] if self.__config.app['app_debug']: arg=sys.argv - if len(arg)==2 and arg[1]=='eventlog': + if 'eventlog' in arg: self.__impl(host=host,port=port,filename=filename) else: - Events([name,str(filename)+'.py','eventlog']) + if name: + arg.insert(0,name) + arg.append('eventlog') + Events(arg) else: self.__impl( host=host, port=port, filename=filename ) - else: + elif len(sys.argv)==3 and sys.argv[2]=='--cli': try: RAW_URI=sys.argv[1] except:pass @@ -158,26 +133,33 @@ class web: def __impl(self,host,port,filename): "运行测试服务器" try: - self.__http_server( - host=host, - port=port, - filename=filename - ) + if config.app['http_server']=='wsgiref': + self.__http_wsgi( + host=host, + port=port, + filename=filename + ) + elif config.app['http_server']=='kcweb': + self.__http_sever( + host=host, + port=port, + filename=filename + ) + elif config.app['http_server']=='kcweb_server': + self.__http_server( + host=host, + port=port, + filename=filename + ) + else: + self.__http_wsgi( + host=host, + port=port, + filename=filename + ) except KeyboardInterrupt: pass - def __get_modular(self,header): - "获取模块" - modular='' - route=self.__config.route - if route['modular']: - if isinstance(route['modular'],str): - modular=route['modular'] - else: - HTTP_HOST=header['HTTP_HOST'].split(".")[0] - for mk in route['modular']: - if HTTP_HOST in mk: - modular=mk[HTTP_HOST] - return modular + def __getconfigroute(self,PATH_INFO,header): "使用配置路由" route=self.__config.route @@ -215,29 +197,61 @@ class web: break except:pass return routedefault,PATH_INFO + def __get_modular(self,header): + "获取模块" + modular='' + route=self.__config.route + if route['modular']: + if isinstance(route['modular'],str): + modular=route['modular'] + else: + HTTP_HOST=header['HTTP_HOST'].split(".")[0] + for mk in route['modular']: + if HTTP_HOST in mk: + modular=mk[HTTP_HOST] + return modular + def __get_plug(self,header): + "获取插件" + plug='' + route=self.__config.route + if route['plug']: + if isinstance(route['plug'],str): + plug=route['plug'] + else: + HTTP_HOST=header['HTTP_HOST'].split(".")[0] + for mk in route['plug']: + if HTTP_HOST in mk: + plug=mk[HTTP_HOST] + return plug def defaultroute(self,header,PATH_INFO): "路由匹配" route=self.__config.route modular=web.__get_modular(self,header) + if route['plug']: + getplug=web.__get_plug(self,header) + else: + getplug='' routedefault=route['default'] methods=route['methods'] if routedefault: - edition='index' + plug=route['defplug'] files=route['files'] funct=route['funct'] else: - edition='' + plug='' files='' funct='' param=[] urls='' i=0 - HTTP_HOST=header['HTTP_HOST'].split(".")[0] + # HTTP_HOST=header['HTTP_HOST'].split(".")[0] ##默认路由start ################################################################################# if modular: - if route['edition']: #匹配模块并且匹配了版本 - edition=route['edition'] + # if route['plug']: #匹配模块并且匹配了插件 + # plug=route['plug'] + if getplug: + plug=getplug routedefault,PATH_INFO=web.__getconfigroute( self, PATH_INFO, @@ -255,7 +269,7 @@ class web: else: param.append(urllib.parse.unquote(path)) i+=1 - else: #配置模块没有配置版本 + else: #配置模块没有配置插件 routedefault,PATH_INFO=web.__getconfigroute( self, PATH_INFO, @@ -265,7 +279,7 @@ class web: for path in PATH_INFO: if path: if i==0: - edition=path + plug=path elif i==1: files=path urls=urls+"/"+str(path) @@ -275,8 +289,10 @@ class web: else: param.append(urllib.parse.unquote(path)) i+=1 - elif route['edition']: #配置版本的但没有匹配模块 - edition=route['edition'] + # elif route['plug']: #配置模块但没有匹配插件 + # plug=route['plug'] + elif getplug: + plug=getplug routedefault,PATH_INFO=web.__getconfigroute( self, PATH_INFO, @@ -304,7 +320,7 @@ class web: if i==0: modular=path elif i==1: - edition=path + plug=path elif i==2: files=path urls=urls+"/"+str(path) @@ -315,7 +331,10 @@ class web: param.append(urllib.parse.unquote(path)) i+=1 #默认路由end ############################################################ - return methods,modular,edition,files,funct,tuple(param) + if not modular: + modular=route['defmodular'] + + return methods,modular,plug,files,funct,tuple(param) def __tran(self,data,status,resheader): "转换控制器返回的内容" if isinstance(data,tuple): @@ -338,6 +357,7 @@ class web: body=data return body,status,resheader def __set_globals(self,header): + globals.HEADER.GET=header globals.HEADER.Method=header['REQUEST_METHOD'] globals.HEADER.URL=header['RAW_URI'] globals.HEADER.PATH_INFO=header['PATH_INFO'] @@ -345,6 +365,10 @@ class web: globals.HEADER.SERVER_PROTOCOL=header['SERVER_PROTOCOL'] globals.HEADER.HTTP_HOST=header['HTTP_HOST'] globals.HEADER.BODY_DATA=header['BODY_DATA'] + try: + globals.HEADER.files=header['files'] + except: + globals.HEADER.files=None try: globals.HEADER.HTTP_COOKIE=header['HTTP_COOKIE'] except: @@ -362,7 +386,8 @@ class web: PATH_INFO=header['PATH_INFO'].split('/') if PATH_INFO[0]==' ' or PATH_INFO[0]=='': del PATH_INFO[0] - methods,modular,edition,files,funct,param=web.defaultroute(self,header,PATH_INFO) + methods,modular,plug,files,funct,param=web.defaultroute(self,header,PATH_INFO) + globals.VAR.component=(methods,modular,plug,files,funct,param) if header['REQUEST_METHOD'] in methods: try: obj=getattr(web.__appname,modular) @@ -371,7 +396,7 @@ class web: body=web.__tpl( title = status, e=status, - data="无法找到目录:"+str(modular)+"/" + data="路由不存在:"+str(modular)+"/" ) else: try: @@ -381,13 +406,13 @@ class web: body=web.__tpl( title = status, e=status, - data="无法找到目录:"+str(modular)+"/controller/" + data="路由不存在:"+str(modular)+"/controller/" ) else: try: - obj=getattr(obj,edition) + obj=getattr(obj,plug) except (AttributeError,UnboundLocalError) as e: - con="无法找到目录:"+str(modular)+"/controller/"+str(edition)+"/" + con="无法找到目录:"+str(modular)+"/controller/"+str(plug)+"/" try: data=getattr(obj,"error")(e,con) body,status,resheader=web.__tran( @@ -416,7 +441,7 @@ class web: try: obj=getattr(obj,files) except (AttributeError,UnboundLocalError) as e: - con="无法找到文件:"+str(modular)+"/controller/"+str(edition)+"/"+str(files)+".py" + con="无法找到文件:"+str(modular)+"/controller/"+str(plug)+"/"+str(files)+".py" try: data=getattr(obj,"error")(e,con) body,status,resheader=web.__tran( @@ -576,6 +601,14 @@ class web: for key in resheader: resheaders.append((key,resheader[key])) web.__del_globals() + + #关闭数据库连接 + dbs=kcwmysql.mysql() + dbs.close() + # dbs=kcwsqlite.sqlite() + # dbs.close() + + if isinstance(resheaders,list): if not body: body='' @@ -594,9 +627,107 @@ class web: t=Template(content) body=t.render(**context) return body - - + def __application(self,env, start_response): + # print(env) + try: + request_body_size = int(env.get('CONTENT_LENGTH', '0')) + except (ValueError): + request_body_size = 0 + if 'multipart/form-data; boundary' in env['CONTENT_TYPE']:#上传文件 + files = cgi.FieldStorage(fp=env['wsgi.input'], environ=env, keep_blank_values=True) + # open(form["file"].filename, 'wb').write(form["file"].value) + # print(files["file"].name) + # print(files["name"].filename) + # print(files["name"].value) + env['BODY_DATA']=None + else: + files=None + BODY_DATA=env['wsgi.input'].read(request_body_size) + env['BODY_DATA']=BODY_DATA.decode() + env['RAW_URI']=env['PATH_INFO'] + if env.get('QUERY_STRING'): + env['RAW_URI']=str(env['PATH_INFO'])+"?"+str(env.get('QUERY_STRING')) + try: + env['REMOTE_ADDR'] + except: + env['REMOTE_ADDR']='' + try: + env['HTTP_COOKIE'] + except: + env['HTTP_COOKIE']='' + try: + env['REMOTE_ADDR'] + except: + env['REMOTE_ADDR']='' + try: + env['HTTP_USER_AGENT'] + except: + env['HTTP_USER_AGENT']='' + reqheader={ + 'REQUEST_METHOD':env['REQUEST_METHOD'], + 'RAW_URI':env['RAW_URI'], + 'PATH_INFO':env['PATH_INFO'], + 'QUERY_STRING':env['QUERY_STRING'], + 'SERVER_PROTOCOL':env['SERVER_PROTOCOL'], + 'HTTP_HOST':env['HTTP_HOST'], + 'HTTP_COOKIE':env['HTTP_COOKIE'], + 'REMOTE_ADDR':env['REMOTE_ADDR'], + 'HTTP_USER_AGENT':env['HTTP_USER_AGENT'], + 'BODY_DATA':env['BODY_DATA'], + 'files':files + } + p=(config.app['staticpath']+reqheader['RAW_URI'].replace(' ','')) + status='200 ok' + if os.path.isfile(p): + resheader=[("Cache-Control","public, max-age=2592000"),("Accept-Ranges","bytes")] + ETag=md5(p) + try: + HTTP_IF_NONE_MATCH=globals.HEADER.GET['HTTP_IF_NONE_MATCH'] + except: + HTTP_IF_NONE_MATCH=None + if HTTP_IF_NONE_MATCH: + status="304 Not Modified" + else: + kind = filetype.guess(p) + if kind is None: + f=open(p,"rb") + body=f.read() + f.close() + if '.css' in p[-5:]: + resheader.append(("Content-Type","text/css")) + elif '.js' in p[-5:]: + resheader.append(("Content-Type","application/javascript")) + else: + f=open(p,"rb") + body=f.read() + f.close() + resheader.append(("Content-Type",kind.mime)) + dateArray = datetime.utcfromtimestamp(times()-86400) + otherStyleTime = dateArray.strftime('%a, %d %b %Y %H:%M:%S GMT') + resheader.append(("Last-Modified",otherStyleTime)) + resheader.append(("ETag",ETag)) + else: + status,resheader,body=web.__routes(self,reqheader) + resheader.append(('Server', "kcweb"+config.kcweb['version'])) + if type(body) is bytes: + pass + else: + body=bytes(body, encoding='utf-8') + start_response(status,resheader) + return [body] + def __http_wsgi(self,host,port,filename): + "http——wsgi测试服务" + print("* \033[1;31;40m! 警告:\033[0m这是一个wsgiref开发服务器。不要在生产环境中部署使用它") + print('* 生产环境中建议使用gunicorn,gunicorn运行命令如:gunicorn -b '+host+':'+str(port)+' '+str(filename)+':app') + if self.__config.app['app_debug']: + print('* 调试器:开启') + else: + print('* 调试器:已关闭') + print("* 运行在http://"+host+":"+str(port)+"/ (按CTRL+C退出)") + httpd = make_server(host, int(port), self.__application) + httpd.serve_forever() def __http_server(self,host,port,filename): + "http测试服务" tcp_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM) try: tcp_socket.bind((host,int(port))) @@ -604,7 +735,7 @@ class web: print("通常每个套接字地址(协议/网络地址/端口)只允许使用一次(按CTRL+C退出)") else: tcp_socket.listen(1024) - print('! 警告:这是开发服务器。不要在生产环境中部署使用它') + print('! \033[1;31;40m警告:\033[0m这是一个kcweb_server开发服务器。不要在生产环境中部署使用它') print('* 生产环境中建议使用gunicorn,gunicorn运行命令如:gunicorn -b '+host+':'+str(port)+' '+str(filename)+':app') if self.__config.app['app_debug']: print('* 调试器:开启') @@ -697,7 +828,7 @@ class web: print(HTTP_HOST+' -- ['+str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))+'] "'+REQUEST_METHOD+" "+RAW_URI +" "+SERVER_PROTOCOL + '" '+status+"-") t=time.time() - header+="Server:kcweb\n" + header+="Server:kcweb"+str(config.kcweb['version'])+"\n" header+="Date:%s\n" % datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT') for t in resheader: header+="%s:%s\n" % (t[0],t[1]) @@ -708,8 +839,10 @@ class web: except Exception as e: pass new_socket.close() - def __http_sever(self,host,port): + def __http_sever(self,host,port,filename): #http测试服务器 + print('\033[1;31;40m! 警告:\033[0m这是一个kcweb开发服务器。不要在生产环境中部署使用它') + print('* 生产环境中建议使用gunicorn,gunicorn运行命令如:gunicorn -b '+host+':'+str(port)+' '+str(filename)+':app') if self.__config.app['app_debug']: print('* 调试器:开启') else: @@ -721,8 +854,6 @@ class web: pack_length=1024 tcp_socket.setblocking(False) tcp_socket_list=list() - - while True: try: new_tcp_socket,client_info=tcp_socket.accept() @@ -767,6 +898,7 @@ class web: else: HTTP_USER_AGENT='' BODY_DATA=datas[len(datas)-1] + # print(data) #reqsest reqheader={ 'REQUEST_METHOD':REQUEST_METHOD, @@ -781,11 +913,13 @@ class web: 'BODY_DATA':BODY_DATA } p=(config.app['staticpath']+RAW_URI.replace(' ','')) - + # print("目录",p) status='200 ok' if os.path.isfile(p): + # print('静态文件',p) kind = filetype.guess(p) if kind is None: + f=open(p,"rb") body=f.read() f.close() @@ -802,13 +936,16 @@ class web: header+="Content-Length:%d\n" % len(body) else: status,resheader,body=self.__routes(reqheader) - body=body.encode() + if type(body) is bytes: + pass + else: + body=body.encode() header="HTTP/1.1 %s \n" % status header+="Content-Length:%d\n" % len(body) print(HTTP_HOST+' -- ['+str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))+'] "'+REQUEST_METHOD+" "+RAW_URI +" "+SERVER_PROTOCOL + '" '+status+"-") t=time.time() - header+="Server:kcweb\n" + header+="Server:kcweb"+str(config.kcweb['version'])+"\n" header+="Date:%s\n" % datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT') for t in resheader: header+="%s:%s\n" % (t[0],t[1]) diff --git a/application/__init__.py b/application/__init__.py deleted file mode 100644 index 915fbee6f8e0fe4aba791f6dcdb01b9addaccef4..0000000000000000000000000000000000000000 --- a/application/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# -*- coding: utf-8 -*- -#导入模块 -% for i in tuple_modular: -from . import ${i} -% endfor diff --git a/application/api/__init__.py b/application/api/__init__.py deleted file mode 100644 index 0f9c4342de58d48fd2406c92b8181402d3fa9c2b..0000000000000000000000000000000000000000 --- a/application/api/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# -*- coding: utf-8 -*- -from . import controller \ No newline at end of file diff --git a/application/api/common/__init__.py b/application/api/common/__init__.py deleted file mode 100644 index 0917cdf745c2cfe170f5abb0824e4a7e856bb560..0000000000000000000000000000000000000000 --- a/application/api/common/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from .autoload import * -#下面的方法在当前模块中有效 -def before_request(): - G.userinfo=get_session("userinfo") - print('${modular}模块在请求前执行,我是要在配置文件配置后才能生效哦!',G.userinfo) -def after_request(): - print('${modular}模块在请求后执行,我是要在配置文件配置后才能生效哦!') -def set_session(name,value,expire=None): - "设置session" - return session.set("${appname}${modular}"+str(name),value,expire) -def get_session(name): - "获取session" - return session.get("${appname}${modular}"+str(name)) -def del_session(name): - "删除session" - return session.rm("${appname}${modular}"+str(name)) -def tpl(path,**context): - return Template("/${modular}/tpl/"+str(path),**context) diff --git a/application/api/common/autoload.py b/application/api/common/autoload.py deleted file mode 100644 index 05965c2340f62dc7443ee92d6f93b682ff11a160..0000000000000000000000000000000000000000 --- a/application/api/common/autoload.py +++ /dev/null @@ -1,2 +0,0 @@ -# -*- coding: utf-8 -*- -from ${appname}.common import * \ No newline at end of file diff --git a/application/api/controller/__init__.py b/application/api/controller/__init__.py deleted file mode 100644 index 5ab9848aeba2b851c0f2d0adcc0dacf5376c3b36..0000000000000000000000000000000000000000 --- a/application/api/controller/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# -*- coding: utf-8 -*- -from . import v1,v2 -# def error(err,data): -# "该函数在当前目录下无法匹配时被调用" -# return data,"200",{"Content-Type":"text/json; charset=utf-8"} \ No newline at end of file diff --git a/application/api/controller/v1/__init__.py b/application/api/controller/v1/__init__.py deleted file mode 100644 index ab68b081b0d2543a1f2a279f1a0757d3286c4708..0000000000000000000000000000000000000000 --- a/application/api/controller/v1/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from . import index -# def error(err,data): -# "该函数在找不到模块时执行函数时和框架报错时被调用" -# return str(err) \ No newline at end of file diff --git a/application/api/controller/v1/index.py b/application/api/controller/v1/index.py deleted file mode 100644 index 80b47d4d1e77462ab4291fd19b5aebe8e6bac811..0000000000000000000000000000000000000000 --- a/application/api/controller/v1/index.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- -from ${appname}.${modular}.common import * -def index(): - return tpl("/v1/index/index.html",title="欢迎使用kcweb框架",data=['这是${appname}应用${modular}模块下v1版本的一个模板渲染测试效果']) -def inter(id='',title=""): - data={ - 'title':title, - 'id':id - } - return successjson(data) -def home(id='',title=""): - data={ - "title":"标题是"+title, - "id":"id是"+id - } - return successjson(data) \ No newline at end of file diff --git a/application/api/controller/v2/__init__.py b/application/api/controller/v2/__init__.py deleted file mode 100644 index ab68b081b0d2543a1f2a279f1a0757d3286c4708..0000000000000000000000000000000000000000 --- a/application/api/controller/v2/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from . import index -# def error(err,data): -# "该函数在找不到模块时执行函数时和框架报错时被调用" -# return str(err) \ No newline at end of file diff --git a/application/api/controller/v2/index.py b/application/api/controller/v2/index.py deleted file mode 100644 index 25d4d39c816a4162ac39cafd4096c2fa1cbc8937..0000000000000000000000000000000000000000 --- a/application/api/controller/v2/index.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -from ${appname}.${modular}.common import * -def index(): - return tpl("/v2/index/index.html",title="欢迎使用kcweb框架",data=['这是${appname}应用${modular}模块下v2版本的一个模板渲染测试效果']) -def inter(): - data={ - 'title':'欢迎使用kcweb框架', - 'desc':'这是${appname}应用${modular}模块下v2版本的json输出效果' - } - return successjson(data) \ No newline at end of file diff --git a/application/api/tpl/v1/index/index.html b/application/api/tpl/v1/index/index.html deleted file mode 100644 index f4818f5fb709a64cb371c11a5eec657de8152327..0000000000000000000000000000000000000000 --- a/application/api/tpl/v1/index/index.html +++ /dev/null @@ -1,29 +0,0 @@ - - - -${title} - - - - - - - -
-

这是v1模板文件

- % if title: -

${title}

- % endif - % if isinstance(data,str): - ${data} - % elif isinstance(data,list): - - % endif -
- - - \ No newline at end of file diff --git a/application/api/tpl/v2/index/index.html b/application/api/tpl/v2/index/index.html deleted file mode 100644 index f331dab5760ce7f600d5215f839207c48a531537..0000000000000000000000000000000000000000 --- a/application/api/tpl/v2/index/index.html +++ /dev/null @@ -1,29 +0,0 @@ - - - -${title} - - - - - - - -
-

这是v2模板文件

- % if title: -

${title}

- % endif - % if isinstance(data,str): - ${data} - % elif isinstance(data,list): - - % endif -
- - - \ No newline at end of file diff --git a/application/common/__init__.py b/application/common/__init__.py deleted file mode 100644 index 05e1c79183f97ed6c558b5b16f304ec1b2b2028f..0000000000000000000000000000000000000000 --- a/application/common/__init__.py +++ /dev/null @@ -1,75 +0,0 @@ -# -*- coding: utf-8 -*- -from .autoload import * -def return_list(lists,count,pagenow,pagesize): - """返回分页列表 - - lists 数据库列表数据 - - count 数据库总数量 - - pagenow 页码 - - pagesize 每页数量 - """ - data={ - 'count':count, - 'pagenow':pagenow, - 'pagesize':pagesize, - 'pagecount':math.ceil(int(count)/int(pagesize)), - 'lists':lists - } - return data -def successjson(data=[],code=0,msg="成功",status='200 ok'): - """成功说在浏览器输出包装过的json - - 参数 data 结果 默认[] - - 参数 code body状态码 默认0 - - 参数 msg body状态描述 默认 成功 - - 参数 status http状态码 默认 200 - - 返回 json字符串结果集 - """ - res={ - "code":code, - "msg":msg, - "time":times(), - "data":data - } - return json_encode(res),status,{"Content-Type":"application/json; charset=utf-8","Access-Control-Allow-Origin":"*"} -def errorjson(data=[],code=1,msg="失败",status='500 error'): - """错误时在浏览器输出包装过的json - - 参数 data 结果 默认[] - - 参数 code body状态码 默认0 - - 参数 msg body状态描述 默认 成功 - - 参数 status http状态码 默认 200 - - 返回 json字符串结果集 - """ - return successjson(data=data,code=code,msg=msg,status=status) -def randoms(lens=6,types=1): - """生成随机字符串 - - lens 长度 - - types 1数字 2字母 3字母加数字 - """ - strs="0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM,!@#$%^&*()_+=-;',./:<>?" - if types==1: - strs="0123456789" - elif types==2: - strs="qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM" - elif types==3: - strs="0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM" - k='' - i=0 - while i < lens: - k+=random.choice(strs) - i+=1 - return k \ No newline at end of file diff --git a/application/common/autoload.py b/application/common/autoload.py deleted file mode 100644 index 99a5719f40b0d4ac4e496f5ef6ed4707dc9593ab..0000000000000000000000000000000000000000 --- a/application/common/autoload.py +++ /dev/null @@ -1,5 +0,0 @@ -# -*- coding: utf-8 -*- -from kcweb.common import * -from ${appname} import config -import math,random -G=globals.G \ No newline at end of file diff --git a/application/config/__init__.py b/application/config/__init__.py deleted file mode 100644 index 3257846498945ab77c1376034dc01d7cac7c5717..0000000000000000000000000000000000000000 --- a/application/config/__init__.py +++ /dev/null @@ -1,40 +0,0 @@ -# -*- coding: utf-8 -*- -from .other import * -#下面的配置都是全局的 -# 应用配置 -app['app_debug']=True #是否开启调试模式 -app['tpl_folder']='./${appname}' #设置模板文件目录名 注意:所有的配置目录都是以您的运行文件所在目录开始 -app['before_request']='before_request' #设置请求前要执行的函数名 -app['after_request']='after_request' #设置请求后要执行的函数名 -app['staticpath']='${appname}/static' #静态主要目录 -# redis配置 -redis['host']='127.0.0.1' #服务器地址 -redis['port']=6379 #端口 -redis['password']='' #密码 -redis['db']=0 #Redis数据库 注:Redis用0或1或2等表示 -redis['pattern']=True # True连接池链接 False非连接池链接 -redis['ex']=0 #过期时间 (秒) -#缓存配置 -cache['type']='File' #驱动方式 支持 File Redis -cache['path']='./${appname}/runtime/cachepath' #缓存保存目录 -cache['expire']=120 #缓存有效期 0表示永久缓存 -cache['host']=redis['host'] #Redis服务器地址 -cache['port']=redis['port'] #Redis 端口 -cache['password']=redis['password'] #Redis登录密码 -cache['db']=1 #Redis数据库 注:Redis用1或2或3等表示 -# session配置 -session['type']='File' #session 存储类型 支持 file、Redis -session['path']='./${appname}/runtime/session/temp' #session缓存目录 -session['expire']=86400 #session默认有效期 该时间是指session在服务的保留时间,通常情况下浏览器上会保留该值的10倍 -session['prefix']="KCW" # SESSION 前缀 -session['host']=redis['host'] #Redis服务器地址 -session['port']=redis['port'] #Redis 端口 -session['password']=redis['password'] #Redis登录密码 -session['db']=2 #Redis数据库 注:Redis用1或2或3等表示 - -#email配置 -email['sender']='' #发件人邮箱账号 -email['pwd']='' #发件人邮箱密码(如申请的smtp给的口令) -email['sendNick']='' #发件人昵称 -email['theme']='' #默认主题 -email['recNick']='' #默认收件人昵称 \ No newline at end of file diff --git a/application/config/other.py b/application/config/other.py deleted file mode 100644 index 1e4e433a25ad69dc2cfcac1e8dc33b7bf52c1d06..0000000000000000000000000000000000000000 --- a/application/config/other.py +++ /dev/null @@ -1,15 +0,0 @@ -# -*- coding: utf-8 -*- -from kcweb.config import * -#路由配置 -route['default']=True #是否开启默认路由 默认路由开启后面不影响以下配置的路由,模块名/版本名/控制器文件名/方法名 作为路由地址 如:http://www.kcw.com/api/v1/index/index/ -route['modular']='${modular}' -route['edition']='v1' -route['files']='index' #默认路由文件(控制器) -route['funct']='index' #默认路由函数 (操作方法) -route['methods']=['POST','GET'] #默认请求方式 -route['children']=[ - {'title':'首页','path':'','component':'index/home','methods':['POST','GET']}, - {'title':'接口','path':'/inter/:id','component':'index/inter','methods':['POST','GET']} -] -#sqlite配置 -sqlite['db']='kcwlicuxweb' #sqlite数据库文件 diff --git a/common/autoload.py b/common/autoload.py index 4b917d1dab2a1e817b4a1288ea9008cb37743f05..974efdf2755b08a3729bf55122a6a76a922a286a 100644 --- a/common/autoload.py +++ b/common/autoload.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import time,hashlib,json,re,os,platform +import time,hashlib,json,re,os,platform,sys,shutil,requests,importlib,traceback,pip,gzip,tarfile,zipfile,random,copy import datetime as core_datetime from kcweb import config from kcweb.utill.dateutil.relativedelta import relativedelta as core_relativedelta @@ -12,11 +12,226 @@ from kcweb.utill.http import Http from kcweb.utill.queues import Queues from kcweb.utill.db import model from mako.template import Template as kcwTemplate +from mako.lookup import TemplateLookup import smtplib from email.mime.text import MIMEText from email.utils import formataddr +from kcweb.utill import filetype from . import globals +import asyncio,websockets,urllib,threading +class kcwebsocket: + "websocket服务端" + __clientlists=[] #所有客户端绑定的对象 + __lists=[] #所有客户端 + __group={} #组 + __uid={} #clientid绑定的uid + async def bindUid(self,clientid,uid): + """将clientid与uid绑定,以便通过sendToUid(uid)发送数据 + + clientid 客户端id + + uid uid与client_id是一对多的关系,系统允许一个uid下有多个client_id + """ + try: + self.__uid[uid] + except KeyError: + self.__uid[uid]=[] + self.__uid[uid].append(clientid) + def unbindUid(self,clientid,uid): + """将clientid与uid解绑 当clientid下线(连接断开)时会自动与uid解绑,开发者无需在onClose事件调用unbindUid + + clientid 客户端id + + uid 数字或者字符串 + """ + try: + self.__uid[uid] + except KeyError: + pass + else: + try: + self.__uid[uid].remove(clientid) + except KeyError: + pass + async def sendToUid(self,uid,message): + """向uid绑定的所有在线clientid发送数据 + + uid uid可以是字符串、数字、或者包含uid的列表。如果为列表,则是给列表内所有uid发送数据 + + message 要发送的数据(字符串类型) + """ + if isinstance(uid,str): + for clientid in self.__uid[uid]: + await self.send_client(clientid,message) + elif isinstance(uid,list): + for k in uid: + for clientid in self.__uid[k ]: + try: + await self.send_client(clientid,message) + except ValueError: + if config.app['app_debug']: + print("deluid",clientid) + self.__uid[uid].remove(clientid) + async def joinGroup(self,clientid,group): + """将clientid加入某个组 + + clientid 客户端id + + Group 组名 + """ + try: + self.__group[group] + except KeyError: + self.__group[group]=[] + self.__group[group].append(clientid) + async def leaveGroup(self,clientid,group): + """将clientid从某个组中删除 + + clientid 客户端id + + group 组名 + """ + try: + self.__group[group] + except KeyError: + pass + else: + try: + self.__group[group].remove(clientid) + except KeyError: + pass + async def ungroup(self,group): + """解散分组。 解散分组后所有属于这个分组的用户的连接将被移出分组,此分组将不再存在,除非再次调用 joinGroup + + group 组名 + """ + try: + self.__group[group] + except KeyError: + pass + else: + del self.__group[group] + async def sendToGroup(self,group,message,exclude_clientid=[]): + """向某个分组的所有在线clientid发送数据。 + + group 组名 + + message 要发送的数据(字符串类型) + + exclude_clientid clientid组成的列表。exclude_clientid列表中指定的clientid将被排除在外,不会收到本次发的消息 + """ + try: + self.__group[group] + except KeyError: + pass + else: + for client in self.__group[group]: + if exclude_clientid: + for clientid in exclude_clientid: + if client!=clientid: + try: + await self.send_client(client,message) + except ValueError: + if config.app['app_debug']: + print("delgroup",client) + self.__group[group].remove(client) + else: + try: + await self.send_client(client,message) + except ValueError: + if config.app['app_debug']: + print("delgroup",client) + self.__group[group].remove(client) + async def getClientIdCountByGroup(self,group): + """获取某分组当前在线成连接数(多少clientid在线) + + group 组名 + + return int 返回一个数字 + """ + try: + self.__group[group] + except KeyError: + return 0 + else: + return len(self.__group[group]) + def getAllClientIdCount(self): + """获取当前在线连接总数(多少client_id在线) + + return int 返回一个数字 + """ + return len(self.__lists) + async def send_all(self,message): + "给所有人发送消息,包括自己" + for l in self.__clientlists: + try: + await l['socket'].send(message) + except:pass + async def send_client(self,clientid,message): + "给所指定客户端发送消息" + xb=self.__lists.index(clientid) + websockets=self.__clientlists[xb]['socket'] + try: + await websockets.send(message) + except:pass + async def onConnect(self,clientid,params): + "客户端发来连接时" + if config.app['app_debug']: + print("连接成功",clientid) + async def onMessage(self,clientid,recv_text): + "当客户端发来数据" + await self.send_client(clientid,recv_text) #给当前用户发送消息 + async def onClose(self,clientid): + "客户端与websocket的连接断开时触发" + if config.app['app_debug']: + print("onClose",clientid) + async def __onClose(self,clientid): + xb=self.__lists.index(clientid) + del self.__lists[xb] + del self.__clientlists[xb] + if self.__uid: + for uid in self.__uid.keys(): + try: + self.__uid[uid] + except KeyError: + pass + else: + try: + self.__uid[uid]['clientid'] + except KeyError: + pass + else: + self.__uid[uid].remove(clientid) + async def __main2(self,clientid,websocket,path): + "服务器端主逻辑" + async for message in websocket: + await self.onMessage(clientid, message) + await self.__onClose(clientid) + await self.onClose(clientid) + async def __main1(self,clientid,websocket,path): + t = urllib.parse.parse_qs(urllib.parse.urlparse(path).query) + params={} + for key in t.keys(): + params[key]=t[key][0] + await self.onConnect(clientid, params) + async def __main(self,websocket,path): + "服务器端主逻辑" + clientid=md5(str(random.random())) + self.__clientlists.append({"clientid":clientid,"socket":websocket}) + self.__lists.append(clientid) + task1=asyncio.ensure_future(self.__main1(clientid,websocket,path)) + task2=asyncio.ensure_future(self.__main2(clientid,websocket,path)) + await task1 + await task2 + def start(self,ip='0.0.0.0',port='39020'): + "启动websoeket服务" + asyncio.set_event_loop(asyncio.new_event_loop()) # 防止出现RuntimeError + asyncio.get_event_loop().run_until_complete(websockets.serve(self.__main,ip,port)) + asyncio.get_event_loop().run_forever() +# def start(): +# kwebsocket=kcwebsocket() +# kwebsocket.start() redis=kcwredis() def send_mail(user,text="邮件内容",theme="邮件主题",recNick="收件人昵称"): """发送邮件 @@ -84,11 +299,16 @@ def Template(path,**context): return Templates(str(config.app['tpl_folder'])+str(path),**context) def Templates(path,**context): "模板渲染引擎函数,需要完整的模板目录文件" - body='' - with open(path, 'r',encoding='utf-8') as f: - content=f.read() - t=kcwTemplate(content) - body=t.render(**context) + lookup = TemplateLookup(directories=['']) + # body='' + # with open(path, 'r',encoding='utf-8') as f: + # contents=f.read() + # t=kcwTemplate(contents,lookup=lookup,module_directory=config.cache['path']+"/Template") + # body=t.render(**context) + + # t=kcwTemplate(filename=path,module_directory=config.cache['path']+"/Template",lookup=lookup) + t=lookup.get_template(path) + body=t.render(**context) return body def mysql(table=None,configss=None): """mysql数据库操作实例 @@ -216,7 +436,7 @@ def json_decode(strs): try: return json.loads(strs) except Exception: - return {} + return [] def json_encode(strs): """python列表或字典转成字符串""" try: @@ -257,14 +477,8 @@ def dateoperator(date,years=0,formats='%Y%m%d%H%M%S',months=0, days=0, hours=0, strs=strs.strftime(formats) return strs def get_folder(): - '获取当前框架所在目录' - path=os.path.split(os.path.realpath(__file__))[0] #当前文件目录 - framepath=path.split('\\') ##框架主目录 - s='' - for k in framepath: - s=s+'/'+k - framepath=s[1:] - return re.sub('/kcw/common','',framepath) #包所在目录 + '获取当前框架目录' + return os.path.split(os.path.realpath(__file__))[0][:-7] #当前框架目录 # aa=[] def get_file(folder='./',is_folder=True,suffix="*",lists=[],append=False): """获取文件夹下所有文件夹和文件 @@ -314,10 +528,742 @@ def list_to_tree(data, pk = 'id', pid = 'pid', child = 'lowerlist', root=0,child if v[pid] == root: kkkk=list_to_tree(data,pk,pid,child,v[pk],childstatus) if childstatus: - # print(kkkk) v[child]=kkkk else: if kkkk: v[child]=kkkk arr.append(v) - return arr \ No newline at end of file + return arr + + +class zip: + def packzip(src,dst): + "压缩" + filelist = [] + if os.path.isfile(src): + filelist.append(src) + for root, dirs, files in os.walk(src): + for name in files: + filelist.append(os.path.join(root, name)) + zf = zipfile.ZipFile(dst, "w", zipfile.zlib.DEFLATED) + for tar in filelist: + arcname = tar[len(src):] + zf.write(tar,arcname) + zf.close() + def unzip_file(dst, src): + "解压" + zf = zipfile.ZipFile(dst) + zf.extractall(src) + zf.close() +class kcwtar: + def targz(src,dst): + """ + 打包目录为tar.gz + :param src: 需要打包的目录 + :param dst: 压缩文件名 + :return: bool + """ + with tarfile.open(dst, "w:gz") as tar: + tar.add(src, arcname=os.path.basename(src)) + return True + def untar(dst, src): + """ + 解压tar.gz文件 + :param dst: 压缩文件名 + :param src: 解压后的存放路径 + :return: bool + """ + try: + t = tarfile.open(dst) + t.extractall(path = src) + return True + except Exception as e: + return False + +class response: + def tpl(path=None,status='200 ok',response_cache=False,ETag=None,header={"Content-Type":"text/html; charset=utf-8"},**context): + """返回模板内容 + + path 文件地址 + + status 响应状态码 + + response_cache 是否启用浏览器缓存 响应状态码200 ok时有效 + + ETag 缓存标识 响应状态码200 ok时有效 + + header 响应头 + """ + headers=copy.deepcopy(header) + getroutecomponent=globals.VAR.component + if path: + if path[:1]=="/": + Temppath=path + else: + Temppath="/"+getroutecomponent[1]+"/controller/"+getroutecomponent[2]+"/tpl/"+path+".html" + else: + Temppath="/"+getroutecomponent[1]+"/controller/"+getroutecomponent[2]+"/tpl/"+getroutecomponent[3]+"/"+getroutecomponent[4]+".html" + if status=='200 ok' and response_cache: + if not ETag: + ttt='' + for k in context.keys(): + ttt+=k+str(context[k]) + ETag=md5(Temppath+ttt+globals.HEADER.URL) + try: + HTTP_IF_NONE_MATCH=globals.HEADER.GET['HTTP_IF_NONE_MATCH'] + except: + HTTP_IF_NONE_MATCH=None + if HTTP_IF_NONE_MATCH and get_cache(ETag): + status="304 Not Modified" + body='' + else: + if isinstance(response_cache,int) and response_cache>1: + headers['response_cache']=str(response_cache)+" s" + set_cache(ETag,1,response_cache) + else: + headers['response_cache']="default" + set_cache(ETag,1) + body=Template(Temppath,config=config,**context) + dateArray = core_datetime.datetime.utcfromtimestamp(times()-86400) + otherStyleTime = dateArray.strftime('%a, %d %b %Y %H:%M:%S GMT') + headers['Last-Modified']=otherStyleTime + headers['ETag']=ETag + return body,status,headers + elif status: + return Template(Temppath,config=config,**context),status,headers + else: + return Template(Temppath,config=config,**context),'200 ok',headers + def json(res=[],status='200 ok',response_cache=False,ETag=None,header={"Content-Type":"application/json; charset=utf-8","Access-Control-Allow-Origin":"*"}): + """响应json内容 + + res body内容 + + status 响应状态码 + + response_cache 是否启用浏览器缓存 响应状态码200 ok时有效 + + ETag 缓存标识 响应状态码200 ok时有效 + + header 响应头 + """ + headers=copy.deepcopy(header) + if status=='200 ok' and response_cache: + if not ETag: + ETag=md5(globals.HEADER.URL) + try: + HTTP_IF_NONE_MATCH=globals.HEADER.GET['HTTP_IF_NONE_MATCH'] + except: + HTTP_IF_NONE_MATCH=None + if(HTTP_IF_NONE_MATCH and get_cache(ETag)): + status="304 Not Modified" + body='' + else: + if isinstance(response_cache,int) and response_cache>1: + set_cache(ETag,1,response_cache) + headers['response_cache']=str(response_cache)+" s" + else: + set_cache(ETag,1) + headers['response_cache']="default" + body=json_encode(res) + dateArray = core_datetime.datetime.utcfromtimestamp(times()-86400) + otherStyleTime = dateArray.strftime('%a, %d %b %Y %H:%M:%S GMT') + headers['Last-Modified']=otherStyleTime + headers['ETag']=ETag + + else: + body=json_encode(res) + return body,status,headers + def pic(body,response_cache=True,ETag=None): + """输出图片 + + body 图片二进制内容或图片路径 建议使用图片路径 + + response_cache 是否启用浏览器缓存 body使用图片路径时有效 + + ETag 缓存标识 + """ + status='200 ok' + header={"Cache-Control":"public, max-age=2592000"} + if isinstance(body,str): + if response_cache: + if not ETag: + ETag=md5(body+globals.HEADER.URL) + try: + HTTP_IF_NONE_MATCH=globals.HEADER.GET['HTTP_IF_NONE_MATCH'] + except: + HTTP_IF_NONE_MATCH=None + if(HTTP_IF_NONE_MATCH and get_cache(ETag)): + status="304 Not Modified" + body='' + else: + if isinstance(response_cache,int) and response_cache>1: + set_cache(ETag,1,response_cache) + else: + set_cache(ETag,1) + filename=body + f=open(filename,"rb") + body=f.read() + f.close() + kind = filetype.guess(filename) + try: + header['Content-Type']=kind.mime + except: + header['Content-Type']="image/png" + else: + filename=body + f=open(filename,"rb") + body=f.read() + f.close() + kind = filetype.guess(filename) + try: + header['Content-Type']=kind.mime + except: + header['Content-Type']="image/png" + dateArray = core_datetime.datetime.utcfromtimestamp(times()-86400) + otherStyleTime = dateArray.strftime('%a, %d %b %Y %H:%M:%S GMT') + header['Last-Modified']=otherStyleTime + header['ETag']=ETag + else: + header['Content-Type']="image/png" + return body,status,header + def video(body): + """输出音频 + + body 音频二进制内容或音频路径 + """ + status='200 ok' + header={"Cache-Control":"public, max-age=2592000"} + if isinstance(body,str): + ETag=md5(body) + try: + HTTP_IF_NONE_MATCH=globals.HEADER.GET['HTTP_IF_NONE_MATCH'] + except: + HTTP_IF_NONE_MATCH=None + if(HTTP_IF_NONE_MATCH and get_cache(ETag)): + status="304 Not Modified" + body='' + else: + set_cache(ETag,1,2592000) + filename=body + f=open(filename,"rb") + body=f.read() + f.close() + kind = filetype.guess(filename) + try: + header['Content-Type']=kind.mime + except: + header['Content-Type']="video/mp4" + dateArray = core_datetime.datetime.utcfromtimestamp(times()-86400) + otherStyleTime = dateArray.strftime('%a, %d %b %Y %H:%M:%S GMT') + header['Last-Modified']=otherStyleTime + header['ETag']=ETag + else: + header['Content-Type']="video/mp4" + return body,status,header + # def video(body): + # """输出音频 + + # body 音频二进制内容或音频路径 + # """ + # if isinstance(body,str): + # filename=body + # f=open(filename,"rb") + # body=f.read() + # f.close() + # kind = filetype.guess(filename) + # try: + # ContentType=kind.mime + # except: + # ContentType="video/mp4" + # else: + # ContentType="video/mp4" + # return body,'200 ok',{"Content-Type":ContentType,"Cache-Control":"public, max-age=600"} + def download(pathname): + """下载文件 + + pathname 文件路径 + """ + if os.path.isfile(pathname): + f=open(pathname,"rb") + body=f.read() + f.close() + kind = filetype.guess(pathname) + try: + return body,"200 ok",{"Content-Type":"application/"+kind.mime,"Accept-Ranges":"bytes"} + except: + return body,"200 ok",{"Content-Type":"application/text","Accept-Ranges":"bytes"} + else: + return Templates('E:\doc\python\kcwebplus\kcweb/tpl/err.html',title="文件不存在",content="文件不存在",imgsrc=config.domain['kcwebimg']+"/icon/error.png",config=config) + def redirect(url,status="302 Found",html='',header={"Content-Type":"application/html; charset=utf-8"}): + """重定向 + + 参数 url 重定向地址 必须 + + 参数 status 响应码 可选 + + 参数 html body响应内容 可选 + + 参数 header 响应头 可选 + """ + header['Location']=url + return html,status,header +class create: + appname=None + modular=None + path=get_folder() #当前框架目录 + def __init__(self,appname="app",modular="api"): + self.appname=str(appname) + self.modular=str(modular) + def uninstallplug(self,plug): + """卸载插件 + + plug 插件名 + """ + f=open(self.appname+"/"+self.modular+"/controller/__init__.py","r",encoding='utf-8') + text=f.read() + f.close() + text=re.sub("\nfrom . import "+plug,"",text) + text=re.sub("from . import "+plug,"",text) + f=open(self.appname+"/"+self.modular+"/controller/__init__.py","w",encoding='utf-8') + f.write(text) + f.close() + shutil.rmtree(self.appname+"/"+self.modular+"/controller/"+plug) + return True,"成功" + def packplug(self,plug): + """打包插件 + + plug 插件名 + """ + """打包模块""" + if os.path.exists(self.appname+"/"+self.modular+"/controller/"+plug): + zip.packzip(self.appname+"/"+self.modular+"/controller/"+plug,self.appname+"/"+self.modular+"/controller/"+plug+".zip") + return True,"成功" + else: + return False,"失败" + def uploadplug(self,plug,username='',password='',cli=False): + "上传一个插件" + if not os.path.isfile(self.appname+"/"+self.modular+"/controller/"+plug+".zip"): + self.packplug(plug=plug) + i=0 + http=Http() + http.set_timeout=300 + while True: + timestamp=times() + sign=md5(str(username)+str(timestamp)+md5(md5(password))) + # http.set_header['username']=username + # http.set_header['timestamp']=str(timestamp) + # http.set_header['sign']=sign + http.openurl(config.domain['kcwebapi']+"/user/userinfo/?username="+username+"×tamp="+str(timestamp)+"&sign="+sign) + arr=json_decode(http.get_text) + if not arr: + os.remove(self.appname+"/"+self.modular+"/controller/"+plug+".zip") + if config.app['app_debug']: + print(http.get_text) + return False,"用户身份验证失败,服务器暂时无法处理" + if (arr['code']==-1 or arr['code']==2) and cli: + if i >= 3: + os.remove(self.appname+"/"+self.modular+"/controller/"+plug+".zip") + return False,"用户名或密码错误" + elif i: + print("用户名或密码错误,请重新输入") + username = input("请输入用户名(手机号)\n") + password = input("请输入密码\n") + else: + username = input("请输入用户名(手机号)\n") + password = input("请输入密码\n") + i+=1 + elif arr['code']==0: + break + else: + os.remove(self.appname+"/"+self.modular+"/controller/"+plug+".zip") + return False,arr['msg'] + http.openurl(config.domain['kcwebapi']+"/user/uploadplug/?username="+username+"×tamp="+str(timestamp)+"&sign="+sign,'POST', + data={'name':str(plug),'describes':'','modular':self.modular}, + files={'file':open(self.appname+"/"+self.modular+"/controller/"+plug+".zip", 'rb')}) + arr=json_decode(http.get_text) + if not arr: + os.remove(self.appname+"/"+self.modular+"/controller/"+plug+".zip") + if config.app['app_debug']: + print(http.get_text) + return False,"上传失败,服务器暂时无法处理上传" + elif arr['code']==-1 or arr['code']==2: + os.remove(self.appname+"/"+self.modular+"/controller/"+plug+".zip") + return False,"用户名或密码错误" + elif arr['code']==0: + os.remove(self.appname+"/"+self.modular+"/controller/"+plug+".zip") + return True,arr['msg'] + else: + os.remove(self.appname+"/"+self.modular+"/controller/"+plug+".zip") + return False,arr['msg'] + def installplug(self,plug,edition='',token='',cli=False,mandatory=False): + """创建一个插件,如果您的模块目录下没有插件包,则创建默认插件文件 + + plug 插件名 + """ + plug=str(plug) + if os.path.exists(self.appname+"/"+self.modular+"/controller/"+plug) and not mandatory: + return False,"该插件已存在" + else: + http=Http() + i=0 + tplug=plug + modular=self.modular + while True: + http.openurl(config.domain['kcwebapi']+"/pub/plug","GET",params={"modular":modular,"name":str(tplug),"edition":str(edition),"token":token}) + arr=json_decode(http.get_text) + if arr: + if arr['code']==-1 and cli: + if i >= 3: + return False,plug+"插件授权码错误" + elif i: + token = input("授权码错误,请重新输入授权码,从而获得该插件\n") + else: + token = input("请输入授权码,从而获得该插件\n") + i+=1 + elif arr['code']==-1: + return False,plug+"插件授权码错误" + elif not arr['data']: + modular="api" + tplug="index" #默认插件 + elif arr['code']==0 and arr['data']: + arr=arr['data'] + tttttt=0 + while True: + if tttttt<5: + r=requests.get(arr['dowurl']) + f = open(self.appname+"/"+self.modular+"/controller/"+plug+".zip", "wb") + for chunk in r.iter_content(chunk_size=512): + if chunk: + f.write(chunk) + f.close() + if zipfile.is_zipfile(self.appname+"/"+self.modular+"/controller/"+plug+".zip"): + break + else: + tttttt+=1 + else: + return False,plug+"插件下载失败" + if os.path.isfile(self.appname+"/"+self.modular+"/controller/"+plug+".zip"):#安装打包好的插件 + zip.unzip_file(self.appname+"/"+self.modular+"/controller/"+plug+".zip",self.appname+"/"+self.modular+"/controller/"+plug+"/") + os.remove(self.appname+"/"+self.modular+"/controller/"+plug+".zip") + if os.path.isfile(self.appname+"/"+self.modular+"/controller/"+plug+"/install.txt"): #安装依赖包 + install_requires=[] + try: + f=open(self.appname+"/"+self.modular+"/controller/"+plug+"/install.txt") + while True: + line = f.readline() + if not line: + break + elif len(line) > 2: + install_requires.append(line) + f.close() + except: + shutil.rmtree(self.appname+"."+self.modular+"/controller/"+plug) + return False,"error" + if len(install_requires): + try: + install_requires.insert(0,"install") + if 0 != pip.main(install_requires): + shutil.rmtree(self.appname+"/"+self.modular+"/controller/"+plug) + return False,"error" + except AttributeError as e: + shutil.rmtree(self.appname+"/"+self.modular+"/controller/"+plug) + if config.app['app_debug']: + print("建议更新您的pip版本。参考命令:Python -m pip install --user --upgrade pip -i https://mirrors.aliyun.com/pypi/simple/") + return False,str(e) + # try: + # m=importlib.import_module(self.appname+"."+self.modular+"/controller/"+plug+".install") + # except: + # shutil.rmtree(self.appname+"."+self.modular+"/controller/"+plug) + # print(traceback.format_exc()) + # return False,"插件依赖包文件不存在或依赖包文件格式错误" + # else: + # try: + # a=m.install() + # except: + # shutil.rmtree(self.appname+"."+self.modular+"/controller/"+plug) + # return False,"插件依赖包install函数被破坏" + # if not a[0]: + # shutil.rmtree(self.appname+"."+self.modular+"/controller/"+plug) + # return False,str(a[1]) + + f=open(self.appname+"/"+self.modular+"/controller/__init__.py","r",encoding='utf-8') + text=f.read() + f.close() + text=re.sub("\nfrom . import "+plug,"",text) + text=re.sub("from . import "+plug,"",text) + f=open(self.appname+"/"+self.modular+"/controller/__init__.py","w",encoding='utf-8') + text+="\nfrom . import "+plug + f.write(text) + f.close() + + f=open(self.appname+"/"+self.modular+"/controller/"+plug+"/common/autoload.py","r",encoding='utf-8') + text=f.read() + f.close() + text=re.sub("app.api",self.appname+"."+self.modular,text) + f=open(self.appname+"/"+self.modular+"/controller/"+plug+"/common/autoload.py","w",encoding='utf-8') + f.write(text) + f.close() + + return True,"插件安装成功,"+plug+"=="+str(arr['edition']) + else: + return False,str(plug)+"插件下载失败" + else: + return False,str(plug)+"插件下载失败" + else: + return False,self.modular+"模块下找不到"+str(plug)+"插件" + def uninstallmodular(self): + "卸载模块" + f=open(self.appname+"/__init__.py","r") + text=f.read() + f.close() + text=re.sub("\nfrom . import "+self.modular,"",text) + text=re.sub("from . import "+self.modular,"",text) + f=open(self.appname+"/__init__.py","w") + f.write(text) + f.close() + shutil.rmtree(self.appname+"/"+self.modular) + return True,"成功" + def packmodular(self): + """打包模块""" + if os.path.exists(self.appname+"/"+self.modular): + zip.packzip(self.appname+"/"+self.modular,self.appname+"/"+self.modular+".zip") + return True,"成功" + else: + return False,"失败" + def uploadmodular(self,username='',password='',cli=False): + "上传模块" + if not os.path.isfile(self.appname+"/"+self.modular+".zip"): + self.packmodular() + i=0 + http=Http() + http.set_timeout=300 + while True: + timestamp=times() + sign=md5(str(username)+str(timestamp)+md5(md5(password))) + # http.set_header['username']=username + # http.set_header['timestamp']=str(timestamp) + # http.set_header['sign']=sign + http.openurl(config.domain['kcwebapi']+"/user/uploadmodular/?username="+username+"×tamp="+str(timestamp)+"&sign="+sign,'POST', + data={'name':str(self.modular),'describes':''}, + files={'file':open(self.appname+"/"+self.modular+".zip", 'rb')}) + arr=json_decode(http.get_text) + if not arr: + os.remove(self.appname+"/"+self.modular+".zip") + if config.app['app_debug']: + print(http.get_text) + return False,"用户身份验证失败,服务器暂时无法处理" + if (arr['code']==-1 or arr['code']==2) and cli: + if i >= 3: + os.remove(self.appname+"/"+self.modular+".zip") + return False,"用户名或密码错误" + elif i: + print("用户名或密码错误,请重新输入") + username = input("请输入用户名(手机号)\n") + password = input("请输入密码\n") + else: + username = input("请输入用户名(手机号)\n") + password = input("请输入密码\n") + i+=1 + elif arr['code']==0: + break + elif arr['code']==-1: + os.remove(self.appname+"/"+self.modular+".zip") + return False,"用户名或密码错误" + else: + os.remove(self.appname+"/"+self.modular+".zip") + return False,arr['msg'] + + http.openurl(config.domain['kcwebapi']+"/user/uploadmodular/?username="+username+"×tamp="+str(timestamp)+"&sign="+sign,'POST', + data={'name':str(self.modular),'describes':''}, + files={'file':open(self.appname+"/"+self.modular+".zip", 'rb')}) + arr=json_decode(http.get_text) + if not arr: + os.remove(self.appname+"/"+self.modular+".zip") + if config.app['app_debug']: + print(http.get_text) + return False,"上传失败,服务器暂时无法处理上传" + elif arr['code']==-1 or arr['code']==2: + os.remove(self.appname+"/"+self.modular+".zip") + return False,"用户名或密码错误" + elif arr['code']==0: + os.remove(self.appname+"/"+self.modular+".zip") + return True,arr['msg'] + else: + os.remove(self.appname+"/"+self.modular+".zip") + return False,arr['msg'] + def installmodular(self,token='',cli=False): + "创建模块,如果应用不存,则创建默认应用,如果在您的应用目录下没有模块包,则创建默认模块文件" + if not os.path.exists(self.appname): + # os.makedirs(self.appname) + r=requests.get(config.domain['kcwebfile']+"/kcweb/app.zip") + f = open("./app.zip", "wb") + for chunk in r.iter_content(chunk_size=512): + if chunk: + f.write(chunk) + f.close() + zip.unzip_file("./app.zip","./"+self.appname) + os.remove("./app.zip") + if not os.path.isfile("./server.py"): + if "Windows" in platform.platform(): + pythonname="python" + else: + pythonname="python3" + servertext=('# -*- coding: utf-8 -*-\n #gunicorn -b 0.0.0.0:39010 '+self.appname+':app\n'+ + 'from kcweb import web\n'+ + 'import '+self.appname+' as application\n'+ + 'app=web(__name__,application)\n'+ + 'if __name__ == "__main__":\n'+ + ' #host监听ip port端口 name python解释器名字 (windows一般是python linux一般是python3)\n'+ + ' app.run(host="0.0.0.0",port="39001",name="'+pythonname+'")') + f=open("./server.py","w+",encoding='utf-8') + f.write(servertext) + f.close() + f=open(self.appname+"/common/autoload.py","w",encoding='utf-8') + f.write("from kcweb.common import *\n"+ + "from "+self.appname+" import config\n"+ + "G=globals.G") + f.close() + f=open(self.appname+"/"+self.modular+"/common/autoload.py","w",encoding='utf-8') + f.write("from "+self.appname+".common import *") + f.close() + f=open(self.appname+"/"+self.modular+"/controller/index/common/autoload.py","w",encoding='utf-8') + f.write("from "+self.appname+"."+self.modular+".common import *") + f.close() + return True,"应用创建成功" + else: + if not os.path.isfile(self.appname+"/__init__.py") or not os.path.exists(self.appname+"/common"): + return False,self.appname+"不是kcweb应用" + if os.path.exists(self.appname+"/"+self.modular): + return False,self.appname+"/"+self.modular+"已存在" + else: + http=Http() + i=0 + modular=self.modular + while True: + http.openurl(config.domain['kcwebapi']+"/pub/modular","POST",params={"name":modular,"token":token}) + arr=json_decode(http.get_text) + if arr: + if arr['code']==-1 and cli: + if i >= 3: + return False,self.modular+"模块授权码错误" + elif i: + token = input("授权码错误,请重新输入授权码,从而获得该模块\n") + else: + token = input("请输入授权码,从而获得该模块\n") + i+=1 + elif arr['code']==-1: + return False,self.modular+"模块授权码错误" + elif not arr['data']: + modular="api" + elif arr['code']==0 and arr['data']: + arr=arr['data'] + r=requests.get(arr['dowurl']) + f = open(self.appname+"/"+self.modular+".zip", "wb") + for chunk in r.iter_content(chunk_size=1024*100): + if chunk: + f.write(chunk) + f.close() + if os.path.isfile(self.appname+"/"+self.modular+".zip"):#安装打包好的模块 + zip.unzip_file(self.appname+"/"+self.modular+".zip",self.appname+"/"+self.modular+"/") + os.remove(self.appname+"/"+self.modular+".zip") + + if os.path.isfile(self.appname+"/"+self.modular+"/install.txt"): #安装依赖包 + install_requires=[] + try: + f=open(self.appname+"/"+self.modular+"/install.txt") + while True: + line = f.readline() + if not line: + break + elif len(line) > 3: + install_requires.append(line) + f.close() + except: + shutil.rmtree(self.appname+"/"+self.modular) + return False,"error" + if len(install_requires): + try: + install_requires.insert(0,"install") + if 0 != pip.main(install_requires): + shutil.rmtree(self.appname+"/"+self.modular) + return False,"error" + except AttributeError as e: + shutil.rmtree(self.appname+"/"+self.modular) + if config.app['app_debug']: + print("建议更新您的pip版本。参考命令:Python -m pip install --user --upgrade pip -i https://mirrors.aliyun.com/pypi/simple/") + return False,str(e) + # try: + # m=importlib.import_module(self.appname+'.'+self.modular+'.install') + # except: + # shutil.rmtree(self.appname+"/"+self.modular) + # print(traceback.format_exc()) + # return False,"模块依赖包文件不存在或依赖包文件格式错误" + # else: + # try: + # a=m.install() + # except: + # shutil.rmtree(self.appname+"/"+self.modular) + # return False,"模块依赖包install方法被破坏" + # if not a[0]: + # shutil.rmtree(self.appname+"/"+self.modular) + # return False,str(a[1]) + content="\nfrom . import "+self.modular + f=open(self.appname+"/__init__.py","a",encoding='utf-8') + f.write(content) + f.close() + + + f.close() + f=open(self.appname+"/"+self.modular+"/common/autoload.py","w",encoding='utf-8') + f.write("from "+self.appname+".common import *") + f.close() + f=open(self.appname+"/"+self.modular+"/controller/index/common/autoload.py","w",encoding='utf-8') + f.write("from "+self.appname+"."+self.modular+".common import *") + f.close() + else: + return False,self.modular+"模块下载失败" + if not os.path.isfile("./server.py"): + if "Windows" in platform.platform(): + pythonname="python" + else: + pythonname="python3" + # sys.argv[0]=re.sub('.py','',sys.argv[0]) + servertext=('# -*- coding: utf-8 -*-\n#gunicorn -b 0.0.0.0:39010 '+self.appname+':app\n'+ + 'from kcweb import web\n'+ + 'import '+self.appname+' as application\n'+ + 'app=web(__name__,application)\n'+ + 'if __name__ == "__main__":\n'+ + ' #host监听ip port端口 name python解释器名字 (windows一般是python linux一般是python3)\n'+ + ' app.run(host="0.0.0.0",port="39001",name="'+pythonname+'")') + f=open("./server.py","w+",encoding='utf-8') + f.write(servertext) + f.close() + return True,"安装成功" + else: + + return False,"模块下载失败" + else: + return False,"找不到"+self.modular+"模块" + # def __zxmodular(self,sourcep): + # "处理模块文件" + # path1=self.path+"/application/api"+sourcep + # path2=self.appname+"/"+self.modular+sourcep + # lists=os.listdir(path1) + # for files in lists: + # if os.path.isfile(path1+"/"+files): + # if ".py" in files: + # content=Templates(path1+"/"+files,appname=self.appname,modular=self.modular) + # f=open(path2+"/"+files,"w+",encoding='utf-8') + # f.write(content) + # f.close() + # else: + # f=open(path1+"/"+files,"r",encoding='utf-8') + # content=f.read() + # f.close() + # f=open(path2+"/"+files,"w+",encoding='utf-8') + # f.write(content) + # f.close() + # elif files != '__pycache__': + # if not os.path.exists(path2+"/"+files): + # os.makedirs(path2+"/"+files) + # self.__zxmodular(sourcep+"/"+files) \ No newline at end of file diff --git a/common/globals.py b/common/globals.py index 64ee2a00b271fdbeb588a6a01bbff895a47836ee..ea553f00bd1b33969559e7334397be0f270b92ac 100644 --- a/common/globals.py +++ b/common/globals.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- from threading import local -##普通全局变量 请求结束后面删除 +##普通全局变量 请求结束后删除 VAR = local() HEADER = local() G = local() diff --git a/common/request.py b/common/request.py index 3b2582af424886dde7fb415cb0372149ef0c1272..4a65a1da2cf0d87911d532912817df0b22802c60 100644 --- a/common/request.py +++ b/common/request.py @@ -1,38 +1,123 @@ # -*- coding: utf-8 -*- from kcweb.common import globals as kcwglobals -from urllib import parse -import json +import json,urllib class args: "获取url" + def params(): + return urllib.parse.parse_qs(urllib.parse.urlparse(kcwglobals.HEADER.URL).query) def get(name): - params = parse.parse_qs(parse.urlparse(kcwglobals.HEADER.URL).query) + """获取键值对 + + name 键 + + return 值 + """ + params = args.params() try: k=params[name][0] except: - k=None + k='' return k + def get_json(): + """获取 json get内容 + + return json内容 + """ + try: + lists=kcwglobals.HEADER.URL.split("?")[1] + lists=lists.split("&") + data={} + for k in lists: + ar=k.split("=") + data[ar[0]]=ar[1] + return data + except:return None class froms: - "获取from" + "获取from application/x-www-form-urlencoded 的post内容" def get(name): + """获取键值对 + + name 键 + + return 值 + """ data=kcwglobals.HEADER.BODY_DATA - params = parse.parse_qs(parse.urlparse("?"+str(data)).query) - # print(params) + params = urllib.parse.parse_qs(urllib.parse.urlparse("?"+str(data)).query) try: - k=parse.unquote(params[name][0]) + k=urllib.parse.unquote(params[name][0]) except: k=None return k + def get_json(): + """获取post json post内容 + + return json内容 + """ + try: + lists=kcwglobals.HEADER.BODY_DATA.split("&") + data={} + for k in lists: + ar=k.split("=") + data[ar[0]]=urllib.parse.unquote(ar[1]) + return data + except:return None +class binary: + "二进制或文件处理" + def get(name): + """获取文件二进制 + + name 文件标识 + + return 文件二进制 + """ + return kcwglobals.HEADER.files[name].value + def filename(name): + """获取文件名,上传文件时有效,其他情况返回空 + + name 文件标识 + + return 文件名 + """ + return kcwglobals.HEADER.files[name].filename + def filesuffix(name): + """获取文件后缀,上传文件时有效,其他情况返回空 + + name 文件标识 + + return 文件名 + """ + return kcwglobals.HEADER.files[name].filename.split('.')[-1] + def save(name,filename): + """保存二进制文件 + + name 文件标识 + + filename 文件位置 + + return 完整文件名 + """ + if kcwglobals.HEADER.files[name].value: + open(filename, 'wb').write(kcwglobals.HEADER.files[name].value) + return filename + else: + return None class HEADER: + def GET(): + return kcwglobals.HEADER.GET def Method(): return kcwglobals.HEADER.Method def URL(): - return kcwglobals.HEADER.URL + return kcwglobals.HEADER.URL.lstrip() def PATH_INFO(): - return kcwglobals.HEADER.PATH_INFO + return kcwglobals.HEADER.PATH_INFO.lstrip() def SERVER_PROTOCOL(): return kcwglobals.HEADER.SERVER_PROTOCOL def HTTP_HOST(): return kcwglobals.HEADER.HTTP_HOST + def HTTP_COOKIE(): + return kcwglobals.HEADER.HTTP_COOKIE + def HTTP_USER_AGENT(): + return kcwglobals.HEADER.HTTP_USER_AGENT def get_data(): "获取请求参数体" return kcwglobals.HEADER.BODY_DATA @@ -42,3 +127,6 @@ def get_json(): return json.loads(kcwglobals.HEADER.BODY_DATA) except: return None +def getroutecomponent(): + "获取路由" + return kcwglobals.VAR.component diff --git a/common/session.py b/common/session.py index 373ca2f7faee22bf2ecbafaca9214f55d82a1c15..13bb78a4fe9c718f94402b269011a00e029c4069 100644 --- a/common/session.py +++ b/common/session.py @@ -26,10 +26,8 @@ def set(name,value,expire=None): if not token: strs="kcw"+str(time.time())+str(random.randint(0,9)) token=__md5(strs) - # print(token) kcwglobals.set_cookie=SESSIONID+"="+token+";expires="+datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')+"; Max-Age=%d;Path=/" % (int(expire)*10) - kcwcache.cache().set_config(kcwsession).set_cache(token,value,expire) - return True + return kcwcache.cache().set_config(kcwsession).set_cache(token,value,expire) def get(name): "获取session" HTTP_COOKIE=kcwglobals.HEADER.HTTP_COOKIE diff --git a/config/__init__.py b/config/__init__.py index 554752af77a9ef8b0aee6586fbc202a7553e321e..830b36307e2d0d2da6e94b29ba4ed10e693679b2 100644 --- a/config/__init__.py +++ b/config/__init__.py @@ -1,11 +1,20 @@ # -*- coding: utf-8 -*- # 应用配置 +import sys app={} app['app_debug']=True #是否开启调试模式 app['tpl_folder']='./tpl' #设置模板文件目录名 注意:不能配置目录路径 app['before_request']='' #设置请求前执行的函数 app['after_request']='' #设置请求后执行的函数 app['staticpath']='static' +app['http_server']='wsgiref' #使用的开发服务器 支持 wsgiref、kcweb、kcweb_server +try: + if sys.argv[2]=='--cli': + app['cli']=True + else: + app['cli']=False +except: + app['cli']=False # redis配置 redis={} redis['host']='127.0.0.1' #服务器地址 @@ -40,14 +49,15 @@ session['db']=2 #Redis数据库 注:Redis用1或2或3等表示 # 默认数据库配置 database={} database['type']='mysql' # 数据库类型 目前支持mysql和sqlite +database['debug']=False #是否开启数据库调试描述 database['host']=['127.0.0.1']#服务器地址 [地址1,地址2,地址3...] 多个地址分布式(主从服务器)下有效 database['port']=[3306] #端口 [端口1,端口2,端口3...] database['user']=['root'] #用户名 [用户名1,用户名2,用户名3...] database['password']=['root'] #密码 [密码1,密码2,密码3...] database['db']=['test'] #数据库名 [数据库名1,数据库名2,数据库名3...] -database['charset']='utf8' #数据库编码默认采用utf8 -database['pattern']=False # True数据库长连接模式 False数据库短连接模式 注:建议web应用使用短连接,cli应用使用长连接 -database['cli']=False # 是否以cli方式运行 +database['charset']='utf8mb4' #数据库编码默认采用utf8mb4 +database['pattern']=False # True数据库长连接模式 False数据库短连接模式 注:建议web应用有效,cli应用方式下,如果长时间运行建议使用mysql().close()关闭 +database['cli']=app['cli'] # 是否以cli方式运行 database['dbObjcount']=1 # 连接池数量(单个数据库地址链接数量),数据库链接实例数量 mysql长链接模式下有效 database['deploy']=0 # 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) mysql数据库有效 database['master_num']=1 #主服务器数量 不能超过host服务器数量 (等于服务器数量表示读写不分离:主主复制。 小于服务器表示读写分离:主从复制。) mysql数据库有效 @@ -71,11 +81,13 @@ mongo['retryWrites']=False #是否支持重新写入 #路由配置 route={} -route['default']=True -route['modular']='' -route['edition']='' -route['files']='index' #默认路由文件 -route['funct']='index' #默认路由函数 +route['default']=True #是否开启默认路由 默认路由开启后面不影响以下配置的路由,模块名/版本名/控制器文件名/方法名 作为路由地址 如:http://www.kcw.com/modular/plug/index/index/ +route['modular']='' #指定访问配置固定模块 (如果配置了该值,将无法通过改变url访问不同模块) +route['plug']='' #指定访问固定插件 (如果配置了该值,将无法通过改变url访问不同插件) +route['defmodular']='index' #默认模块 当url不包括模块名时 +route['defplug']='index' #默认插件 当url不包括插件名时 +route['files']='index' #默认路由文件(控制器) 当url不包括控制器名时 +route['funct']='index' #默认路由函数 (操作方法) 当url不包括操作方法名时 route['methods']=['POST','GET'] #默认请求方式 route['children']=[] #email配置 @@ -88,15 +100,24 @@ email['recNick']='' #默认收件人昵称 kcweb={} kcweb['name']='kcweb' #项目的名称 -kcweb['version']='2.40.7' #项目版本 -kcweb['description']='基于python后端开发框架' #项目的简单描述 +kcweb['version']='4.12.2' #项目版本 +kcweb['description']='' #项目的简单描述 kcweb['long_description']='' #项目详细描述 kcweb['license']='MIT' #开源协议 mit开源 -kcweb['url']='http://intapp.kwebapp.cn/index/index/doc/docde/1' +kcweb['url']='' kcweb['author']='禄可集团-坤坤' #名字 kcweb['author_email']='fk1402936534@qq.com' #邮件地址 kcweb['maintainer']='坤坤' #维护人员的名字 kcweb['maintainer_email']='fk1402936534@qq.com' #维护人员的邮件地址 +kcweb['username']='' +kcweb['password']='' + +domain={} +domain['kcwebfile']="https://file.kwebapp.cn" +domain['kcwebstatic']="https://static.kwebapp.cn" +domain['kcwebimg']="https://img.kwebapp.cn" +# domain['kcwebapi']="https://kcweb.kwebapp.cn" +domain['kcwebapi']="https://kcweb.kwebapp.cn" #其他配置 other={} diff --git a/create.py b/create.py deleted file mode 100644 index 036c0609b53146b54dd9fdf8f6e660720d95b0b6..0000000000000000000000000000000000000000 --- a/create.py +++ /dev/null @@ -1,95 +0,0 @@ -# -*- coding: utf-8 -*- -import os,re,traceback,shutil,platform,sys -from mako.template import Template as kcwTemplate -def Template(path,**context): - body='' - with open(str(path), 'r',encoding='utf-8') as f: - content=f.read() - t=kcwTemplate(content) - body=t.render(**context) - return body -class create: - appname=None - modular=None - path=os.path.split(os.path.realpath(__file__))[0] #当前文件目录 - def __init__(self,appname="application",modular="api"): - self.appname=appname - self.modular=modular - if not os.path.exists(self.appname): - os.makedirs(self.appname) - if not os.path.exists(self.appname+"/common"): - os.makedirs(self.appname+"/common") - f=open(self.appname+"/common/__init__.py","w+",encoding='utf-8') - content=Template(self.path+"/application/common/__init__.py",appname=appname,modular=modular) - f.write(content) - f.close() - f=open(self.appname+"/common/autoload.py","w+",encoding='utf-8') - content=Template(self.path+"/application/common/autoload.py",appname=appname,modular=modular) - f.write(content) - f.close() - if not os.path.exists(self.appname+"/config"): - os.makedirs(self.appname+"/config") - f=open(self.appname+"/config/__init__.py","w+",encoding='utf-8') - content=Template(self.path+"/application/config/__init__.py",appname=appname,modular=modular) - f.write(content) - f.close() - f=open(self.appname+"/config/other.py","w+",encoding='utf-8') - content=Template(self.path+"/application/config/other.py",appname=appname,modular=modular) - f.write(content) - f.close() - if not os.path.exists(self.appname+"/"+self.modular): #创建模块 - os.makedirs(self.appname+"/"+self.modular) - self.zxmodular("") - #在应用目录下创建初始化文件 - lists=os.listdir(self.appname) - modulars=[] - filters=['__init__','__pycache__','common','config','runtime','log'] - for files in lists: - if not os.path.isfile(self.appname+"/"+files): - if files not in filters: - modulars.append(files) - f=open(self.appname+"/__init__.py","w+",encoding='utf-8') - content=Template(self.path+"/application/__init__.py",appname=appname,tuple_modular=modulars) - f.write(content) - f.close() - if "Windows" in platform.platform(): - pythonname="python" - else: - pythonname="python3" - sys.argv[0]=re.sub('.py','',sys.argv[0]) - content=('# #gunicorn -b 0.0.0.0:39010 '+self.appname+':app\n'+ - 'from kcweb import web\n'+ - 'import '+self.appname+' as application\n'+ - 'from '+self.appname+'.common import *\n'+ - 'Queues.start() #开启队列监听\n'+ - 'app=web(__name__,application)\n'+ - 'if __name__ == "__main__":\n'+ - ' #host监听ip port端口 name python解释器名字 (windows一般是python linux一般是python3)\n'+ - ' app.run(host="0.0.0.0",port="39001",name="'+pythonname+'")') - f=open("./"+sys.argv[0]+".py","w+",encoding='utf-8') - f.write(content) - f.close() - def zxmodular(self,sourcep): - "处理模块文件" - path1=self.path+"/application/api"+sourcep - path2=self.appname+"/"+self.modular+sourcep - lists=os.listdir(path1) - for files in lists: - if os.path.isfile(path1+"/"+files): - if ".py" in files: - content=Template(path1+"/"+files,appname=self.appname,modular=self.modular) - f=open(path2+"/"+files,"w+",encoding='utf-8') - f.write(content) - f.close() - else: - f=open(path1+"/"+files,"r",encoding='utf-8') - content=f.read() - f.close() - f=open(path2+"/"+files,"w+",encoding='utf-8') - f.write(content) - f.close() - elif files != '__pycache__': - if not os.path.exists(path2+"/"+files): - os.makedirs(path2+"/"+files) - self.zxmodular(sourcep+"/"+files) - diff --git a/setup.py b/setup.py new file mode 100644 index 0000000000000000000000000000000000000000..1d4a1c242983f7ead24deb4659e31d8e77a4d19b --- /dev/null +++ b/setup.py @@ -0,0 +1,59 @@ + +# python setup.py sdist upload +# twine upload --repository-url https://test.pypi.org/legacy/ dist/* #上传到测试 +# pip install --index-url https://test.pypi.org/simple/ kcweb #安装测试服务上的kcweb +############################################# +from setuptools import setup, find_packages,Extension +import os +def file_get_content(k): + "获取文件内容" + if os.path.isfile(k): + f=open(k,'r',encoding="utf-8") + con=f.read() + f.close() + else: + con='' + return con +confkcw={} +confkcw['name']='kcweb' #项目的名称 +confkcw['version']='4.12.2' #项目版本 +confkcw['description']='' #项目的简单描述 +confkcw['long_description']="增加websocket" #项目详细描述 +confkcw['license']='MIT License' #开源协议 mit开源 +confkcw['url']='' +confkcw['author']='禄可集团-坤坤' #名字 +confkcw['author_email']='fk1402936534@qq.com' #邮件地址 +confkcw['maintainer']='坤坤' #维护人员的名字 +confkcw['maintainer_email']='fk1402936534@qq.com' #维护人员的邮件地址 +def get_file(folder='./',lists=[]): + lis=os.listdir(folder) + for files in lis: + if not os.path.isfile(folder+"/"+files): + if files=='__pycache__' or files=='.git': + pass + else: + lists.append(folder+"/"+files) + get_file(folder+"/"+files,lists) + else: + pass + return lists +b=get_file("kcweb",['kcweb']) +setup( + name = confkcw["name"], + version = confkcw["version"], + keywords = "kcweb"+confkcw['version'], + description = confkcw["description"], + long_description = confkcw["long_description"], + license = confkcw["license"], + author = confkcw["author"], + author_email = confkcw["author_email"], + maintainer = confkcw["maintainer"], + maintainer_email = confkcw["maintainer_email"], + url=confkcw['url'], + packages = b, + # data_files=[('Scripts', ['kcweb/bin/kcw.exe'])], + install_requires = ['pymongo>=3.10.0','Mako>=1.1.2','requests>=2.23.0','six>=1.12.0','watchdog>=0.9.0','websockets==8.1'], #第三方包 + package_data = { + '': ['*.html', '*.js','*.css','*.jpg','*.png','*.gif'], + } +) \ No newline at end of file diff --git a/tpl/err.html b/tpl/err.html new file mode 100644 index 0000000000000000000000000000000000000000..2cfaad455dbc4e36dfdb29bfdab6bf9fcd5ce0cd --- /dev/null +++ b/tpl/err.html @@ -0,0 +1,55 @@ + + + + +${title} + + + + + + + + + + + + + +
+
+ +
+ + + + ${content}
+
+ +
+ + + diff --git a/utill/cache/cache.py b/utill/cache/cache.py index 17c87f1fa25631c120805c3e812c9833cfa489b7..8df20e7079fabaf114a35273f14c9b8e1c4b483e 100644 --- a/utill/cache/cache.py +++ b/utill/cache/cache.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import os,sys,time,hashlib,json from kcweb import config -from kcweb.utill import redis +from kcweb.utill import rediss as red from kcweb.utill.db.mysql import mysql import time,hashlib @@ -63,15 +63,15 @@ class cache: conf['db']=self.__config['db'] if conf['pattern']: if conf['password']: - redis_pool=redis.ConnectionPool(host=conf['host'],password=conf['password'],port=conf['port'],db=conf['db']) + redis_pool=red.ConnectionPool(host=conf['host'],password=conf['password'],port=conf['port'],db=conf['db']) else: - redis_pool=redis.ConnectionPool(host=conf['host'],port=conf['port'],db=conf['db']) - self.__redisobj=redis.Redis(connection_pool=redis_pool) + redis_pool=red.ConnectionPool(host=conf['host'],port=conf['port'],db=conf['db']) + self.__redisobj=red.Redis(connection_pool=redis_pool) else: if conf['password']: - self.__redisobj=redis.Redis(host=conf['host'],password=conf['password'],port=conf['port'],db=conf['db']) + self.__redisobj=red.Redis(host=conf['host'],password=conf['password'],port=conf['port'],db=conf['db']) else: - self.__redisobj=redis.Redis(host=conf['host'],port=conf['port'],db=conf['db']) + self.__redisobj=red.Redis(host=conf['host'],port=conf['port'],db=conf['db']) def set_cache(self,name,values,expire = 'no'): """设置缓存 @@ -140,6 +140,8 @@ class cache: return self.__getmysqlcache() elif types == 'del': return self.__delmysqlcache() + else: + raise Exception("缓存类型错误") def __setmysqlcache(self): ######################################################################################## """设置mysql缓存 @@ -233,7 +235,7 @@ class cache: try: f=open(self.__config['path']+"/"+self.__name,"r") except Exception: - return None + return "" json_str=f.read() f.close() ar=json_decode(json_str) @@ -242,7 +244,7 @@ class cache: if (times()-ar['time']) > ar['expire']: self.__delfilecache() - return None + return "" else: return ar['values'] else: diff --git a/utill/db/mongodb.py b/utill/db/mongodb.py index 960eaa466f4be218f1861c9867ee393064ecc7e1..d365abef757b58670f3efc293414f02e4eb56f45 100644 --- a/utill/db/mongodb.py +++ b/utill/db/mongodb.py @@ -46,19 +46,29 @@ class mongo: "获取mongodb链接实例" self.__setconn() return self.__tabobj + __order=None + def order(self,order): + """设置排序 + + order [('_id', -1)] + """ + self.__order=order + return self def select(self,id=None): """查询所有文档 返回 文档列表 """ + self.__setconn() if id: self.where('_id',id) lists=[] if self.__field: - arr=self.__tabobj.find(self.__where,self.__field) + arr=self.__tabobj.find(self.__where,self.__field,sort=self.__order) else: - arr=self.__tabobj.find(self.__where) + arr=self.__tabobj.find(self.__where,sort=self.__order) + print(self.__order) if self.__limit: if self.__limit[1]: arr.limit(self.__limit[1]) @@ -70,6 +80,7 @@ class mongo: except: pass else: k['_id']=str(k['_id']) lists.append(k) + self.__None() return lists def find(self,id=None): """查询一条文档 @@ -86,6 +97,7 @@ class mongo: try: arr['_id'] except: pass else: arr['_id']=str(arr['_id']) + self.__None() return arr def countlist(self): """查询文档数量和所有文档 @@ -109,6 +121,7 @@ class mongo: except: pass else: k['_id']=str(k['_id']) lists.append(k) + self.__None() return arr.count(),lists def count(self): """查询文档数量 @@ -117,6 +130,7 @@ class mongo: """ self.__setconn() count=self.__tabobj.find(self.__where,{}).count() + self.__None() return count def update(self,data,multi=True): """文档更新 @@ -130,11 +144,8 @@ class mongo: # print(self.__where) # print({"$set":data}) ar=self.__tabobj.update(self.__where,{"$set":data},multi=multi) + self.__None() return ar - if ar: - return ar['nModified'] - else: - return 0 def delete(self,id=None): """文档删除 删除条件是where函数 """ @@ -145,17 +156,20 @@ class mongo: # print(self.__where) # exit() bo=self.__tabobj.remove(self.__where) + self.__None() if bo: return bo['n'] else: return 0 else: + self.__None() return 0 def deleteAll(self,id=None): """删所有文档除 """ self.__setconn() bo=self.__tabobj.remove({}) + self.__None() if bo: return bo['n'] else: @@ -188,6 +202,23 @@ class mongo: """ self.__table=table return self + def __None(self): + "清除所有赋值条件" + # self.__lock=None + # self.__distinct=None + # self.__join=None + # self.__joinstr='' + # self.__alias=None + # self.__having=None + # self.__group=None + # self.__group1=None + # self.__order=None + # self.__order1=None + mongo.__limit=[] + mongo.__field={} + mongo.__where={} + mongo.__table=None + mongo.__order=None __where={} def where(self,where = None,*wheres): """设置过滤条件 @@ -294,6 +325,24 @@ class mongo: 参数 length:int 查询数量 """ + offset=int(offset) + length=int(length) + if length==None: + length=offset + offset=0 + # elif offset > 0: + # offset=offset*length-length + self.__limit=[offset,length] + return self + def page(self,offset, length = None): + """设置分页查询 + + 参数 offset:int 页码 + + 参数 length:int 页面数量 + """ + offset=int(offset) + length=int(length) if length==None: length=offset offset=0 @@ -301,8 +350,6 @@ class mongo: offset=offset*length-length self.__limit=[offset,length] return self - # def order(self,k): - # pass def __operator(self,strs): """运算符转换 参数 strs 待转的字符串 diff --git a/utill/db/mysql.py b/utill/db/mysql.py index 8bfaae3f9445ae17160da5395c90eb7b8056aae9..c2598e816e35e0464d29902ef0bec79194edce4e 100644 --- a/utill/db/mysql.py +++ b/utill/db/mysql.py @@ -2,12 +2,13 @@ from .pymysql import connect,escape_string # import config.conf as config import kcweb.config as config -import time,traceback,decimal,random +import time,traceback,decimal,random #,copy dbconfig=config.database class mysql: """数据库实例""" __config=dbconfig __conn={} #数据库链接对象 + __connlists=[] #短连接列表 用于关闭 __cursor=None #游标对象 __errorcount=dbconfig['break'] #允许最大链接错误次数 __errorcounts=0 #默认链接错误次数 @@ -17,11 +18,43 @@ class mysql: __masteridentifier='' # 主服务器标识 __slaveidentifier='' # 从服务器标识 def __del__(self): - if not self.__config['pattern'] and self.__conn: - try: - self.__conn.close() - except Exception as e: - print("关闭失败",e) + pass + def close(self): + "关闭连接,该方法无需理会,框架自动完成" + if not self.__config['pattern'] and mysql.__conn: + if self.__connlists: + for k in self.__connlists: + try: + mysql.__conn[k].close() + except Exception as e: + if self.__config['debug']: + print("mysql短连接关闭失败",str(e),k) + else: + if self.__config['debug']: + print("mysql短连接关闭成功",k) + mysql.__connlists=[] + mysql.__conn={} + elif isinstance(mysql.__conn,dict): + i=0 + for thost in self.__config['host']: + identifier=thost+str(self.__config['port'][i])+self.__config['user'][i]+self.__config['password'][i]+self.__config['db'][i] + print(mysql.__conn) + for k in mysql.__conn[identifier]: + try: + k['obj'].close() + if self.__config['debug']: + print("mysql短连接已关闭",k) + except Exception as e: + if self.__config['debug']: + print("mysql短连接关闭失败",str(e),k) + i+=1 + mysql.__conn={} + self.__connlists=[] + elif isinstance(mysql.__conn,object): + mysql.__conn[self.__masteridentifier].close() + mysql.__conn={} + if self.__config['debug']: + print("mysql短连接已关闭",mysql.__conn) __dbcount=1 def __setdbcount(self): "设置数据库配置总数量" @@ -48,12 +81,13 @@ class mysql: if self.__config['pattern']: if identifier in mysql.__conn: for k in mysql.__conn[identifier]: - # print(identifier) try: k['obj'].close() - print(k,"关闭成功") - except: - print(k,"关闭错误") + if self.__config['debug']: + print("mysql长连接关闭成功",k) + except Exception as e: + if self.__config['debug']: + print(k,"mysql长连接关闭失败",str(e)) mysql.__conn[identifier]=[] __dbobjident=None #集中式(单一服务器)并且长连接模式下随机服务器链接标识 和 分布式(主从服务器)模式下随机服务器链接标识 def __connects(self,typess="DQL"): @@ -61,11 +95,10 @@ class mysql: 参数 typess :数据查询语言DQL,数据操纵语言DML,数据定义语言DDL,数据控制语言DCL """ - try: if self.__config['deploy']==0: # 集中式(单一服务器) if self.__config['pattern']: # 长连接情况下 - self.__masteridentifier=self.__config['host'][0]+str(self.__config['port'][0])+self.__config['db'][0] # 服务器标识 + self.__masteridentifier=self.__config['host'][0]+str(self.__config['port'][0])+self.__config['user'][0]+self.__config['password'][0]+self.__config['db'][0] # 服务器标识 if self.__masteridentifier not in mysql.__conn or len(mysql.__conn[self.__masteridentifier])<1: i=0 masterlistsdb=[] @@ -75,25 +108,30 @@ class mysql: masterlistsdb.append(objar) i=i+1 mysql.__conn[self.__masteridentifier]=masterlistsdb - print("第%s次创建数据库链接对象,长连接模式" % (self.__errorcounts+1)) + if self.__config['debug']: + print("第%s次创建数据库链接对象,长连接模式" % (self.__errorcounts+1)) else: - mysql.__conn=connect(host=self.__config['host'][0], port=self.__config['port'][0], user=self.__config['user'][0], password=self.__config['password'][0], db=self.__config['db'][0], charset=self.__config['charset']) - # print("第%s次创建数据库链接对象" % (self.__errorcounts+1)+self.__config['host'][0]) + self.__masteridentifier=self.__config['host'][0]+str(self.__config['port'][0])+self.__config['user'][0]+self.__config['password'][0]+self.__config['db'][0] # 服务器标识 + try: + mysql.__conn[self.__masteridentifier] + except KeyError: # 铺获未知异常 + mysql.__conn[self.__masteridentifier]=connect(host=self.__config['host'][0], port=self.__config['port'][0], user=self.__config['user'][0], password=self.__config['password'][0], db=self.__config['db'][0], charset=self.__config['charset']) + self.__connlists.append(self.__masteridentifier) + if self.__config['debug']: + print("mysql短连接已创建",self.__masteridentifier,mysql.__conn[self.__masteridentifier]) elif self.__config['deploy']==1: # 分布式(主从服务器) if self.__config['pattern']: # 长连接情况下 j=0 self.__masteridentifier='' while j < self.__config['master_num']: - self.__masteridentifier=self.__masteridentifier+self.__config['host'][j]+str(self.__config['port'][j])+self.__config['db'][j] # 主服务器标识 + self.__masteridentifier=self.__masteridentifier+self.__config['host'][j]+str(self.__config['port'][j])+self.__config['user'][j]+self.__config['password'][j]+self.__config['db'][j] # 主服务器标识 j=j+1 j=self.__config['master_num'] self.__slaveidentifier='' while j < self.__dbcount: - self.__slaveidentifier=self.__slaveidentifier+self.__config['host'][j]+str(self.__config['port'][j])+self.__config['db'][j] # 从服务器标识 + self.__slaveidentifier=self.__slaveidentifier+self.__config['host'][j]+str(self.__config['port'][j])+self.__config['user'][j]+self.__config['password'][j]+self.__config['db'][j] # 从服务器标识 j=j+1 if self.__masteridentifier not in mysql.__conn or len(mysql.__conn[self.__masteridentifier])self.__config['dbObjcount'] * self.__dbcount: #长连接情况下如果错误次数超过数据实例数量 关闭使用连接进行重连接 self.__patternerrorcount=0 if self.__config['deploy'] == 1: #分布式(主从服务器) 情况下 - print("数据库连接失效,关闭主从连接池后重新连接") + if self.__config['debug']: + print("数据库连接失效,关闭主从连接池后重新连接") self.__closeconn(self.__masteridentifier) self.__closeconn(self.__slaveidentifier) time.sleep(10) - # mysql.__conn=[] #父类数据库实例 self.__connects(typess) self.__execute(typess) else: - print("数据库连接失效,关闭主连接池后重新连接") + if self.__config['debug']: + print("数据库连接失效,关闭主连接池后重新连接") self.__closeconn(self.__masteridentifier) time.sleep(10) - # mysql.__conn=[] #父类数据库实例 self.__connects(typess) self.__execute(typess) else: @@ -333,30 +394,25 @@ class mysql: self.__execute(typess) else: self.__conn[bs][self.__dbobjident]['error']=self.__conn[bs][self.__dbobjident]['error']+1 #当前数据库连接实例异常错误数量 - if self.__conn[bs][self.__dbobjident]['error'] > 2: + if self.__conn[bs][self.__dbobjident]['error'] > 0: try: - mysql.__conn[bs][self.__dbobjident]['obj'].close() #关闭当前实例 + # mysql.__conn[bs][self.__dbobjident]['obj'].close() #关闭当前实例 + self.__closeconn(bs) except Exception as e: - print("关闭异常",e) - # self.__conn[bs].pos(self.__dbobjident) #从列表中删除 - # if errorcodes == 2013: - #创建一个新的数据库实例 - if types=='master': - s=random.randint(0,self.__config['master_num']-1) + if self.__config['debug']: + print("关闭异常",e) else: - s=random.randint(self.__dbcount-self.__config['master_num']-1,self.__dbcount-1) - obj=connect(host=self.__config['host'][s], port=self.__config['port'][s], user=self.__config['user'][s], password=self.__config['password'][s], db=self.__config['db'][s], charset=self.__config['charset']) - mysql.__conn[bs][self.__dbobjident]['obj']=obj - mysql.__conn[bs][self.__dbobjident]['error']=0 - print("已重新创建一个新的数据库实例",mysql.__conn) - self.__execute(typess) + self.__connects(typess) + self.__execute(typess) else: # 短连接情况下 - print("服务器正在被关闭,关闭当前连接后重试") + if self.__config['debug']: + print("服务器正在被关闭,关闭当前连接后重试") try: - mysql.__conn.close() #关闭当前实例 + del mysql.__conn[self.__masteridentifier] + mysql.__conn[self.__masteridentifier].close() #关闭当前实例 except Exception as e: - print("关闭异常",e) - # mysql.__conn=[] #父类数据库实例 + if self.__config['debug']: + print("关闭异常",e) self.__connects(typess) self.__execute(typess) else: @@ -376,8 +432,8 @@ class mysql: res=self.__execute('DQL') description=self.__cursor.description #获取字段 result = self.__cursor.fetchall() #获取查询结果 - # print(result) self.__cursor.close() + self.__None() if description is None: return res else: @@ -404,8 +460,8 @@ class mysql: res=self.__execute('DML') description=self.__cursor.description #获取字段 result = self.__cursor.fetchall() #获取查询结果 - # print(result) self.__cursor.close() + self.__None() if description is None: return res else: @@ -439,8 +495,8 @@ class mysql: self.__execute() description=self.__cursor.description #获取字段 result = self.__cursor.fetchall() #获取查询结果 - # print(result) self.__cursor.close() + self.__None() lists=[] keys =[] for field in description:#获取字段 @@ -471,9 +527,8 @@ class mysql: self.__execute() description=self.__cursor.description #获取字段 result = self.__cursor.fetchall() #获取查询结果 - # print(result) self.__cursor.close() - + self.__None() item = dict() keys =[] for field in description:#获取字段 @@ -496,11 +551,19 @@ class mysql: self.__setsql('count') if self.__buildSql: self.__sqls="("+self.__sql+")" + self.__None() return self.__sql self.__execute() result = self.__cursor.fetchall() #获取查询结果 self.__cursor.close() - cou=int(result[0][0]) + if self.__group: + cou=len(result) + else: + try: + cou=int(result[0][0]) + except IndexError: + cou=0 + # self.__None() return cou def max(self,field): """查询某字段的最大值 @@ -511,11 +574,13 @@ class mysql: self.__setsql('max') if self.__buildSql: self.__sqls="("+self.__sql+")" + self.__None() return self.__sql self.__execute() result = self.__cursor.fetchall() #获取查询结果 self.__cursor.close() cou=int(result[0][0]) + self.__None() return cou def min(self,field): """查询某字段的最小值 @@ -526,10 +591,12 @@ class mysql: self.__setsql('min') if self.__buildSql: self.__sqls="("+self.__sql+")" + self.__None() return self.__sql self.__execute() result = self.__cursor.fetchall() #获取查询结果 self.__cursor.close() + self.__None() cou=int(result[0][0]) return cou def avg(self,field): @@ -541,10 +608,12 @@ class mysql: self.__setsql('avg') if self.__buildSql: self.__sqls="("+self.__sql+")" + self.__None() return self.__sql self.__execute() result = self.__cursor.fetchall() #获取查询结果 self.__cursor.close() + self.__None() cou=int(result[0][0]) return cou def sum(self,field): @@ -556,14 +625,35 @@ class mysql: self.__setsql('sum') if self.__buildSql: self.__sqls="("+self.__sql+")" + self.__None() return self.__sql self.__execute() result = self.__cursor.fetchall() #获取查询结果 self.__cursor.close() + self.__None() cou=int(result[0][0]) return cou - + def setinc(self,field,key=1,affair=False): + """更新字段增加 + + 参数 field 要更新的字段 + + 参数 key 字段需要加多少 + + 参数 affair 是否开启事务 True表示手动提交事务 False表示自动提交事务 + """ + data={"field":field,"key":key} + self.__setsql('setinc',data) + res=self.__execute('DML') + if affair==False and self.__startTrans==False: + if not self.__config['pattern']: + self.__conn[self.__masteridentifier].commit() + else: + self.__conn[self.__masteridentifier][self.__dbobjident]['obj'].commit() + self.__cursor.close() + self.__None() + return res def update(self,data,affair=False): """数据表更新 @@ -575,10 +665,11 @@ class mysql: res=self.__execute('DML') if affair==False and self.__startTrans==False: if not self.__config['pattern']: - self.__conn.commit() + self.__conn[self.__masteridentifier].commit() else: self.__conn[self.__masteridentifier][self.__dbobjident]['obj'].commit() self.__cursor.close() + self.__None() return res def delete(self,affair=False): """数据表删除 @@ -592,10 +683,11 @@ class mysql: return 0 if affair==False and self.__startTrans==False: if not self.__config['pattern']: - self.__conn.commit() + self.__conn[self.__masteridentifier].commit() else: self.__conn[self.__masteridentifier][self.__dbobjident]['obj'].commit() self.__cursor.close() + self.__None() return res def insert(self,dicts,affair=False): """插入数据库 单条插入或多条插入 @@ -610,10 +702,11 @@ class mysql: res=self.__execute('DML') if affair==False and self.__startTrans==False: if not self.__config['pattern']: - self.__conn.commit() + self.__conn[self.__masteridentifier].commit() else: self.__conn[self.__masteridentifier][self.__dbobjident]['obj'].commit() self.__cursor.close() + self.__None() return res __startTrans=False @@ -626,7 +719,7 @@ class mysql: 增删改后的任务进行提交 """ if not self.__config['pattern']: - self.__conn.commit() + self.__conn[self.__masteridentifier].commit() else: self.__conn[self.__masteridentifier][self.__dbobjident]['obj'].commit() @@ -636,7 +729,7 @@ class mysql: 增删改后的任务进行撤销 """ if not self.__config['pattern']: - self.__conn.rollback() + self.__conn[self.__masteridentifier].rollback() else: self.__conn[self.__masteridentifier][self.__dbobjident]['obj'].rollback() def getsql(self): @@ -704,13 +797,18 @@ class mysql: self.__field=field return self __limit=[] - def limit(self,offset, length = None): + def limit(self,offset=1, length = None): """设置查询数量 参数 offset:int 起始位置 参数 length:int 查询数量 """ + if not offset: + offset=1 + offset=int(offset) + if length: + length=int(length) self.__limit=[offset,length] return self def page(self,pagenow=1, length = 20): @@ -720,6 +818,12 @@ class mysql: 参数 length:int 查询数量 """ + if not pagenow: + pagenow=1 + if not length: + length=20 + pagenow=int(pagenow) + length=int(length) offset=(pagenow-1)*length self.__limit=[offset,length] return self @@ -842,6 +946,8 @@ class mysql: self.__sql="SELECT AVG(%s) FROM %s" % (self.__field,self.__table) elif types=='sum': self.__sql="SELECT SUM(%s) FROM %s" % (self.__field,self.__table) + elif types=='setinc': + self.__sql="update %s set %s=%s+%d" % (self.__table,data['field'],data['field'],data['key']) elif types=='update': strs='' for k in data: @@ -911,6 +1017,12 @@ class mysql: self.__sql=self.__sql + " WHERE %s" % self.__listTrans() else: print("参数where类型错误") + if self.__group: + s=self.__group + if self.__group1: + for key in self.__group1: + s=s+","+key + self.__sql=self.__sql+" GROUP BY "+s if self.__order: s='' if isinstance(self.__order,list): @@ -943,14 +1055,7 @@ class mysql: s=self.__order+" "+s else: s=self.__order - # print(s) self.__sql=self.__sql+" ORDER BY "+s - if self.__group: - s=self.__group - if self.__group1: - for key in self.__group1: - s=s+","+key - self.__sql=self.__sql+" GROUP BY "+s if self.__having: self.__sql=self.__sql+" HAVING "+self.__having if self.__limit: @@ -963,15 +1068,12 @@ class mysql: self.__sql=self.__sql+" "+self.__lock else: self.__sql=self.__sql+' FOR UPDATE' - # print(self.__sql) - # exit() def __listTrans(self): """列表转换sql表达式 返回 字符串 """ strs='' #[('id', 'eq', '1'), 'or', ('id', 'eq', '2')] - # print(self.__where) for k in self.__where: if isinstance(k,tuple): t=0 diff --git a/utill/db/sqlite copy.py b/utill/db/sqlite copy.py new file mode 100644 index 0000000000000000000000000000000000000000..ba6140e4864d52bc452e473eb630d9a117debd11 --- /dev/null +++ b/utill/db/sqlite copy.py @@ -0,0 +1,685 @@ +# -*- coding: utf-8 -*- +from kcweb.config import sqlite as sqliteconfig +import time,traceback,re +import random,sqlite3,os,hashlib +class sqlite: + __config=sqliteconfig + __configt={} + __conn={} + __cursor={} + __sql=None + __sqls=None + def md5(self,strs): + """md5加密 + + 参数 strs:要加密的字符串 + + return String类型 + """ + m = hashlib.md5() + b = strs.encode(encoding='utf-8') + m.update(b) + return m.hexdigest() + def close(self): + "关闭连接" + for k in sqlite.__conn.keys(): + sqlite.__cursor[k].close() + sqlite.__conn[k].close() + sqlite.__conn={} + sqlite.__cursor={} + sqlite.__configt={} + def __setconn(self): + if not self.__configt: + self.__configt=sqliteconfig + try: + self.__conn[self.md5(self.__configt['db'])] + except KeyError: + try: + if '/' in self.__configt['db']: + self.__conn[self.md5(self.__configt['db'])] = sqlite3.connect(self.__configt['db'],check_same_thread=False) + else: + self.__conn[self.md5(self.__configt['db'])] = sqlite3.connect(os.path.split(os.path.realpath(__file__))[0]+"/sqlitedata/"+self.__configt['db'],check_same_thread=False) + except Exception as e: + raise Exception(e) + self.__cursor[self.md5(self.__configt['db'])]=self.__conn[self.md5(self.__configt['db'])].cursor() + def __execute(self,typess='DQL'): + self.__setconn() + # print(self.__sql) + try: + res=self.__cursor[self.md5(self.__configt['db'])].execute(self.__sql) + except Exception as e: + + raise Exception(e) + else: + return res + def connect(self,config): + if isinstance(config,str): + self.__configt['db']=config + elif isinstance(config,dict): + if 'db' in config: + self.__configt['db']=config['db'] + return self + __table="" + def table(self,table): + """设置表名 + + 参数 table:str 表名 + """ + self.__table=table + return self + def query(self,sql): + self.__sql=sql + self.__execute(sql,'DQL') + # self.close() + def execute(self,sql): + self.__sql=sql + res=self.__execute('DML') + rowcount=res.rowcount + # self.close() + return rowcount + # def create_table(self): + # self.__sql=("CREATE TABLE "+self.__table+ + # "(ID INT PRIMARY KEY NOT NULL,"+ + # "NAME TEXT NOT NULL,"+ + # "AGE INT NOT NULL,"+ + # "ADDRESS CHAR(50),"+ + # "SALARY REAL);") + # # print(self.__sql) + # # exit() + # self.execute(self.__sql) + def select(self,id=None): + """select查询 + + 返回 list(列表) + """ + if id : + self.__where="id=%d" % id + self.__setsql() + if self.__buildSql: + self.__sqls="("+self.__sql+")" + self.__None() + return self.__sqls + self.__execute() + description=self.__cursor[self.md5(self.__configt['db'])].description #获取字段 + result = self.__cursor[self.md5(self.__configt['db'])].fetchall() #获取查询结果 + # self.close() + lists=[] + data_dict=[] + for field in description:#获取字段 + data_dict.append(field[0]) + for k in result: + i=0 + dicts={} + for j in k: + dicts[data_dict[i]]=j + i=i+1 + lists.append(dicts) + self.__None(table=False) + return lists + def find(self,id=None): + """查询一条记录 + + 返回 字典 + """ + if id : + self.__where="id=%s" % id + self.limit(1) + self.__setsql() + if self.__buildSql: + self.__sqls="("+self.__sql+")" + self.__None() + return self.__sqls + + self.__execute() + description=self.__cursor[self.md5(self.__configt['db'])].description #获取字段 + result = self.__cursor[self.md5(self.__configt['db'])].fetchall() #获取查询结果 + # self.close() + data_dict=[] + for field in description:#获取字段 + data_dict.append(field[0]) + dicts={} + for k in result: + i=0 + for j in k: + dicts[data_dict[i]]=j + i=i+1 + self.__None(table=False) + return dicts + def count(self,field="*"): + """查询数量 + + 返回 int 数字 + """ + self.__field=field + self.__setsql('count') + if self.__buildSql: + self.__sqls="("+self.__sql+")" + return self.__sql + self.__execute() + result = self.__cursor[self.md5(self.__configt['db'])].fetchall() #获取查询结果 + # self.close() + if self.__group: + cou=len(result) + else: + try: + cou=int(result[0][0]) + except IndexError: + cou=0 + self.__None(table=False) + return cou + def max(self,field): + """查询某字段的最大值 + + 返回 int 数字 + """ + self.__field=field + self.__setsql('max') + if self.__buildSql: + self.__sqls="("+self.__sql+")" + return self.__sql + self.__execute() + result = self.__cursor[self.md5(self.__configt['db'])].fetchall() #获取查询结果 + # self.close() + cou=int(result[0][0]) + self.__None(table=False) + return cou + def min(self,field): + """查询某字段的最小值 + + 返回 int 数字 + """ + self.__field=field + self.__setsql('min') + if self.__buildSql: + self.__sqls="("+self.__sql+")" + return self.__sql + self.__execute() + result = self.__cursor[self.md5(self.__configt['db'])].fetchall() #获取查询结果 + # self.close() + cou=int(result[0][0]) + self.__None(table=False) + return cou + def avg(self,field): + """查询某字段的平均值 + + 返回 int 数字 + """ + self.__field=field + self.__setsql('avg') + if self.__buildSql: + self.__sqls="("+self.__sql+")" + return self.__sql + self.__execute() + result = self.__cursor[self.md5(self.__configt['db'])].fetchall() #获取查询结果 + # self.close() + cou=int(result[0][0]) + self.__None(table=False) + return cou + def sum(self,field): + """查询某字段之和 + + 返回 int 数字 + """ + self.__field=field + self.__setsql('sum') + if self.__buildSql: + self.__sqls="("+self.__sql+")" + return self.__sql + self.__execute() + result = self.__cursor[self.md5(self.__configt['db'])].fetchall() #获取查询结果 + # self.close() + cou=int(result[0][0]) + self.__None(table=False) + return cou + def update(self,data,affair=False): + """数据表更新 + + 参数 data 要更新的内容 格式:{"name":"测试","age":20} + + 参数 affair 是否开启事务 True表示手动提交事务 False表示自动提交事务 + """ + self.__setsql('update',data) + res=self.__execute('DML') + if affair==False and self.__startTrans==False: + self.commit() + rowcount=res.rowcount + # self.close() + self.__None(table=False) + return rowcount + def delete(self,affair=False): + """数据表删除 + + 参数 affair 是否开启事务 True表示手动提交事务 False表示自动提交事务 + """ + self.__setsql('delete') + if self.__where: + res=self.__execute('DML') + else: + return 0 + if affair==False and self.__startTrans==False: + self.commit() + rowcount=res.rowcount + # self.close() + self.__None(table=False) + return rowcount + def insert(self,dicts,affair=False): + """插入数据库 单条插入或多条插入 + + 参数 dicts 要插入的内容 单条格式:{"name":"测试","age":20} 。 多条格式:[{"name":"测试","age":20},{"name":"测试","age":20}] + + 参数 affair 是否开启事务 True表示手动提交事务 False表示自动提交事务 + + 返回插入的数量 + """ + self.__setsql('insert',dicts) + res=self.__execute('DML') + if affair==False and self.__startTrans==False: + self.commit() + rowcount=res.rowcount + # self.close() + self.__None(table=False) + return rowcount + __startTrans=False + def startTrans(self): + "开启事务,仅对 update方法、delete方法、install方法有效" + self.__startTrans=True + def commit(self): + """事务提交 + + 增删改后的任务进行提交 + """ + self.__conn[self.md5(self.__configt['db'])].commit() + def rollback(self): + """事务回滚 + + 增删改后的任务进行撤销 + """ + self.__conn[self.md5(self.__configt['db'])].rollback() + def getsql(self): + """得到生成的sql语句""" + return self.__sql + __buildSql=None + def buildSql(self): + """构造子查询""" + self.__buildSql=True + return self + def __None(self,table=True): + "清除所有赋值条件" + self.__lock=None + self.__distinct=None + self.__join=None + self.__joinstr='' + self.__alias=None + self.__having=None + self.__group=None + self.__group1=None + self.__order=None + self.__order1=None + self.__limit=None + self.__field="*" + self.__where=None + self.__wheres=() + self.__buildSql=None + if table: + self.__table=None + __where=None + __wheres=() + def where(self,where = None,*wheres): + """设置过滤条件 + + 传入方式: + "id",2 表示id='2' + + "id","in",2,3,4,5,6,...表示 id in (2,3,4,5,6,...) + + "id","in",[2,3,4,5,6,...]表示 id in (2,3,4,5,6,...) + + [("id","gt",6000),"and",("name","like","%超")] 表示 ( id > "6000" and name LIKE "%超" ) + + "id","eq",1 表示 id = '1' + + eq 等于 + neq 不等于 + gt 大于 + egt 大于等于 + lt 小于 + elt 小于等于 + like LIKE + """ + self.__where=where + self.__wheres=wheres + # print(len(self.__wheres)) + return self + __field='*' + def field(self,field = "*"): + """设置过滤显示条件 + + 参数 field:str 字符串 + """ + self.__field=field + return self + __limit=[] + def limit(self,offset, length = None): + """设置查询数量 + + 参数 offset:int 起始位置 + + 参数 length:int 查询数量 + """ + self.__limit=[offset,length] + return self + def page(self,pagenow=1, length = 20): + """设置分页查询 + + 参数 pagenow:int 页码 + + 参数 length:int 查询数量 + """ + offset=(pagenow-1)*length + self.__limit=[offset,length] + return self + __order=None + __order1=None + def order(self,strs=None,*strs1): + """设置排序查询 + + 传入方式: + + "id desc" + + "id",'name','appkey','asc' + + "id",'name','appkey' 不包含asc或desc的情况下 默认是desc + + ['id','taskid',{"task_id":"desc"}] + """ + self.__order=strs + self.__order1=strs1 + return self + __group=None + __group1=None + def group(self,strs=None,*strs1): + """设置分组查询 + + 传入方式: + + "id,name" + + "id","name" + """ + self.__group=strs + self.__group1=strs1 + return self + __having=None + def having(self,strs=None): + """用于配合group方法完成从分组的结果中筛选(通常是聚合条件)数据 + + 参数 strs:string 如:"count(time)>3" + """ + self.__having=strs + return self + + __alias=None + def alias(self,strs=None): + """用于设置当前数据表的别名,便于使用其他的连贯操作例如join方法等。 + + 参数 strs:string 默认当前表作为别名 + """ + if strs: + self.__alias=strs + else: + self.__alias=self.__table + return self + __join=None + __joinstr='' + def join(self,strs,on=None,types='INNER'): + """用于根据两个或多个表中的列之间的关系,从这些表中查询数据 + + 参数 strs string 如:"test t1" test表设置别名t1 + + 参数 on string 如:"t1.id=t2.pid" 设置连接条件 + + 参数 types 支持INNER、LEFT、RIGHT、FULL 默认INNER + + """ + joinstr='' + if strs and on: + joinstr=joinstr+types+" JOIN "+strs+" ON "+on+" " + if joinstr: + self.__joinstr=self.__joinstr+joinstr + return self + __distinct=None + def distinct(self,bools=None): + "用于返回唯一不同的值,配合field方法使用生效,消除所有重复的记录,并只获取唯一一次记录。" + self.__distinct=bools + return self + __lock=None + def lock(self,strs=None): + """用于数据库的锁机制,在查询或者执行操作的时候使用 (暂未实现) + + 排他锁 (Exclusive lock) + + 共享锁 (Shared lock) + + 参数 strs 如:True表示自动在生成的SQL语句最后加上FOR UPDATE, + + + """ + # self.__lock=strs + return self + def __setsql(self,types=None,data = {}): + """生成sql语句""" + if types==None: + self.__sql="SELECT" + if self.__distinct and self.__field: + self.__sql=self.__sql+" DISTINCT" + if self.__alias: + self.__sql=self.__sql+" %s FROM %s %s" % (self.__field,self.__table,self.__alias) + else: + self.__sql=self.__sql+" %s FROM %s" % (self.__field,self.__table) + elif types=='count': + self.__sql="SELECT COUNT(%s) FROM %s" % (self.__field,self.__table) + elif types=='max': + self.__sql="SELECT MAX(%s) FROM %s" % (self.__field,self.__table) + elif types=='min': + self.__sql="SELECT MIN(%s) FROM %s" % (self.__field,self.__table) + elif types=='avg': + self.__sql="SELECT AVG(%s) FROM %s" % (self.__field,self.__table) + elif types=='sum': + self.__sql="SELECT SUM(%s) FROM %s" % (self.__field,self.__table) + elif types=='update': + strs='' + for k in data: + if isinstance(data[k],str): + strs=strs+" %s = '%s' ," % (k,self.escape_string(data[k])) + else: + strs=strs+" %s = %s ," % (k,data[k]) + strs=strs[:-1] + self.__sql="UPDATE %s SET %s" % (self.__table,strs) + # print(self.__sql) + elif types=='delete': + self.__sql="DELETE FROM %s" % (self.__table) + elif types=='insert': + if isinstance(data,dict): + strs='' + val='' + for k in data: + strs=strs+"%s," % k + if isinstance(data[k],str): + val=val+"'%s'," % self.escape_string(data[k]) + else: + val=val+"%s," % data[k] + strs=strs[:-1] + val=val[:-1] + self.__sql="INSERT INTO "+str(self.__table)+" ("+strs+") VALUES ("+val+")" + # print(self.__sql) + elif isinstance(data,list): + strs='' + val='(' + for k in data[0]: + strs=strs+" , "+k + for k in data: + for j in k: + if isinstance(k[j],str): + val=val+"'"+str(k[j])+"'," + else: + val=val+str(k[j])+"," + val=val[:-1] + val=val+"),(" + val=val[:-2] + self.__sql="INSERT INTO "+str(self.__table)+" ("+strs[3:]+") VALUES "+val + + if self.__joinstr: + # print(self.__sql) + self.__sql=self.__sql+" "+self.__joinstr + if self.__where: + if isinstance(self.__where,str): + if self.__wheres: + if len(self.__wheres) == 2: + if isinstance(self.__wheres[1],list): + self.__sql=self.__sql + " WHERE %s %s (" % (self.__where,self.__operator(self.__wheres[0])) + for k in self.__wheres[1]: + self.__sql=self.__sql+str(k)+"," + self.__sql=self.__sql[:-1]+")" + else: + self.__sql=self.__sql + " WHERE %s %s '%s'" % (self.__where,self.__operator(self.__wheres[0]),self.__wheres[1]) + elif len(self.__wheres) > 2: + if self.__wheres[0]=='in': + strs=str(self.__wheres[1]) + i=0 + for k in self.__wheres: + if i > 1: + strs=strs+","+str(k) + i=i+1 + self.__sql=self.__sql + " WHERE %s in (%s)" % (self.__where,strs) + else: + self.__sql=self.__sql + " WHERE %s = '%s'" % (self.__where,self.__wheres[0]) + else: + self.__sql=self.__sql + " WHERE %s" % self.__where + elif isinstance(self.__where,list): + self.__sql=self.__sql + " WHERE %s" % self.__listTrans() + else: + print("参数where类型错误",type(self.__where),self.__where) + if self.__order: + s='' + if isinstance(self.__order,list): + for strs in self.__order: + if isinstance(strs,str): + s=s+strs+"," + else: + pass + for key in strs: + s=s+key+" "+strs[key] + s=s+"," + s=s[:-1] + if isinstance(self.__order,str): + if self.__order1: + if len(self.__order1) > 1: + if self.__order1[len(self.__order1)-1] == 'desc' or self.__order1[len(self.__order1)-1] == 'asc': + i=0 + while i