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):
-
- % for i in data:
- - ${i}
- % endfor
-
- % 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):
-
- % for i in data:
- - ${i}
- % endfor
-
- % 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