core.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. # -*- coding: utf-8 -*-
  2. """
  3. demeter
  4. name:core.py
  5. author:rabin
  6. """
  7. import time
  8. import os
  9. import signal
  10. import shutil
  11. import re
  12. import sys
  13. import json
  14. import subprocess
  15. import importlib
  16. #import fire
  17. from watchdog.observers import Observer
  18. from watchdog.events import FileSystemEventHandler
  19. class Demeter(object):
  20. path = ''
  21. root = ''
  22. config = {}
  23. option = {}
  24. web = ''
  25. request = False
  26. route = []
  27. logger = False
  28. dog = False
  29. def __new__(self, *args, **kwargs):
  30. sys.exit()
  31. def __init__(self):
  32. pass
  33. @staticmethod
  34. def checkPy3():
  35. if sys.version > '3':
  36. state = True
  37. else:
  38. state = False
  39. return state
  40. @classmethod
  41. def getConfig(self):
  42. state = self.checkPy3()
  43. if state:
  44. import configparser
  45. return configparser.ConfigParser()
  46. else:
  47. import ConfigParser
  48. return ConfigParser.ConfigParser()
  49. @staticmethod
  50. def isset(v):
  51. try :
  52. type (eval(v))
  53. except :
  54. return 0
  55. else :
  56. return 1
  57. @classmethod
  58. def initConfig(self):
  59. self.path = File.path()
  60. self.root = File.cur_path()
  61. if self.config == {}:
  62. filename = self.path + 'conf/'+self.getConfigName()+'.conf'
  63. if File.exists(filename):
  64. config = self.getConfig()
  65. config.read(filename)
  66. for item in config.sections():
  67. self.config[item] = self.readConfig(config, item)
  68. return True
  69. else:
  70. Demeter.echo(filename + ' is not exists')
  71. sys.exit()
  72. @classmethod
  73. def getConfigName(self):
  74. name = 'dev'
  75. if 'DEMETER_CONF' in os.environ:
  76. name = os.environ['DEMETER_CONF']
  77. param = {}
  78. param['config'] = 'c'
  79. self.getopt(param)
  80. if 'config' in self.option and self.option['config']:
  81. name = self.option['config']
  82. return name
  83. @staticmethod
  84. def readConfig(config, type):
  85. value = config.options(type)
  86. result = {}
  87. for item in value:
  88. result[item] = config.get(type, item)
  89. return result
  90. @classmethod
  91. def getopt(self, param = {}):
  92. import getopt
  93. param['help'] = 'h'
  94. shortopts = ''
  95. longopts = []
  96. check = []
  97. for k,v in param.items():
  98. if k == 'help':
  99. shortopts = shortopts + v
  100. else:
  101. shortopts = shortopts + v + ':'
  102. longopts.append(k)
  103. try:
  104. options, args = getopt.getopt(sys.argv[1:], shortopts, longopts)
  105. for name, value in options:
  106. for k,v in param.items():
  107. if name in ('-' + v, '--' + k):
  108. if k == 'help':
  109. self.usage()
  110. else:
  111. self.option[k] = value
  112. except getopt.GetoptError:
  113. #self.usage()
  114. return
  115. @classmethod
  116. def usage(self, name = 'usage'):
  117. file = self.path + name
  118. if not File.exists(file):
  119. file = self.root + name
  120. self.echo(File.read(file))
  121. sys.exit()
  122. @classmethod
  123. def temp(self, key='', name='', value=''):
  124. temp = Demeter.path + 'conf/temp.conf'
  125. if File.exists(temp):
  126. config = self.getConfig()
  127. config.read(temp)
  128. if key and name:
  129. config.set(key, name, value)
  130. config.write(open(temp, 'w'))
  131. else:
  132. result = {}
  133. for item in config.sections():
  134. result[item] = self.readConfig(config, item)
  135. return result
  136. @classmethod
  137. def echo(self, args):
  138. import pprint
  139. pprint.pprint(args)
  140. @classmethod
  141. def record(self, key, value):
  142. # 记录日志
  143. # self.log(key, value)
  144. service = self.service('record')
  145. service.push(key, value)
  146. @classmethod
  147. def service(self, name, parent = ''):
  148. self.initConfig()
  149. path = 'service.'
  150. if name == 'common':
  151. path = 'demeter.'
  152. name = 'service'
  153. if parent:
  154. path = path + parent + '.'
  155. service = self.getClass(name, path)
  156. return service()
  157. @classmethod
  158. def adminModel(self, table):
  159. self.initConfig()
  160. config = ('manage_admin', 'manage_log', 'manage_role')
  161. if table in config:
  162. return self.getClass(table, 'demeter.admin.model.')
  163. return False
  164. @classmethod
  165. def model(self, table, name='rdb'):
  166. self.initConfig()
  167. name = self.config['db'][name]
  168. config = self.config[name]
  169. obj = self.getObject('db', 'demeter.')
  170. db = getattr(obj, name.capitalize())
  171. connect = db(config).get()
  172. model = self.adminModel(table)
  173. if not model:
  174. model = self.getClass(table, 'model.')
  175. return model(name, connect, config)
  176. @classmethod
  177. def getMethod(self, module):
  178. import inspect
  179. return inspect.getmembers(module, callable)
  180. @classmethod
  181. def getPackage(self, package):
  182. import pkgutil
  183. for importer, modname, ispkg in pkgutil.walk_packages(path=package.__path__, prefix=package.__name__ + '.', onerror=lambda x: None):
  184. yield modname
  185. @staticmethod
  186. def getObject(name, path = ''):
  187. return importlib.import_module(path + name)
  188. """
  189. @classmethod
  190. def getObject(self, name, path = ''):
  191. module = __import__(path + name)
  192. return getattr(module, name)
  193. """
  194. @classmethod
  195. def getClass(self, name, path=''):
  196. obj = self.getObject(name, path)
  197. return getattr(obj, name.capitalize())
  198. @staticmethod
  199. def bool(value, type = 'db'):
  200. if type == 'mysql':
  201. value = value == str(True)
  202. if value == True:
  203. return '1'
  204. else:
  205. return '2'
  206. else:
  207. return value == str(True)
  208. @classmethod
  209. def runtime(self, path, file, content=''):
  210. path = self.path + 'runtime/' + path + '/'
  211. File.mkdir(path)
  212. file = path + file
  213. if File.exists(file):
  214. return False
  215. else:
  216. File.write(file, content)
  217. return True
  218. @classmethod
  219. def webInit(self, name):
  220. self.initConfig()
  221. self.web = name
  222. self.webPath = self.path + self.web + '/'
  223. if self.web == 'admin':
  224. self.webPath = self.root + self.web + '/'
  225. self.getObject('main', name + '.')
  226. @classmethod
  227. def md5(self, value, salt=False):
  228. import hashlib
  229. if salt:
  230. if salt == True:
  231. salt = self.rand()
  232. value = value + salt
  233. return hashlib.md5(value.encode("utf-8")).hexdigest() + '_' + salt
  234. else:
  235. return hashlib.md5(value.encode("utf-8")).hexdigest()
  236. @classmethod
  237. def sha1(self, value, salt=False):
  238. import hashlib
  239. if salt:
  240. if salt == True:
  241. salt = self.rand()
  242. value = value + salt
  243. return hashlib.sha1(value.encode("utf-8")).hexdigest() + '_' + salt
  244. else:
  245. return hashlib.sha1(value.encode("utf-8")).hexdigest()
  246. @classmethod
  247. def rand(self, length = 4):
  248. module = self.getObject('random')
  249. rand = getattr(module, 'randint')
  250. salt = ''
  251. chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
  252. len_chars = len(chars) - 1
  253. for i in range(length):
  254. salt += chars[rand(0, len_chars)]
  255. return salt
  256. @classmethod
  257. def hash(self):
  258. return self.md5(str(time.clock()))
  259. @classmethod
  260. def uuid(self, value):
  261. import uuid
  262. return str(uuid.uuid5(uuid.uuid1(), value))
  263. @staticmethod
  264. def hour(value):
  265. if value < 10:
  266. return '0' + str(value)
  267. return value
  268. @staticmethod
  269. def time():
  270. return int(time.time())
  271. @staticmethod
  272. def mktime(value, string='%Y-%m-%d %H:%M:%S'):
  273. if ' ' in string and ' ' not in value:
  274. value = value + ' 00:00:00'
  275. return int(time.mktime(time.strptime(value,string)))
  276. @classmethod
  277. def date(self, value, string='%Y-%m-%d %H:%M:%S'):
  278. module = self.getObject('datetime')
  279. datetime = getattr(module, 'datetime')
  280. fromtimestamp = getattr(datetime, 'fromtimestamp')
  281. return str(fromtimestamp(value).strftime(string))
  282. @staticmethod
  283. def isJson(value):
  284. result = False
  285. try:
  286. result = json.loads(value)
  287. except ValueError:
  288. return result
  289. return result
  290. @staticmethod
  291. def host(value):
  292. import urllib
  293. protocol, s1 = urllib.splittype(value)
  294. value, s2 = urllib.splithost(s1)
  295. value, port = urllib.splitport(value)
  296. return value
  297. @staticmethod
  298. def compressUuid(value):
  299. row = value.replace('-', '')
  300. code = ''
  301. hash = [x for x in "0123456789-abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ"]
  302. for i in xrange(10):
  303. enbin = "%012d" % int(bin(int(row[i * 3] + row[i * 3 + 1] + row[i * 3 + 2], 16))[2:], 10)
  304. code += (hash[int(enbin[0:6], 2)] + hash[int(enbin[6:12], 2)])
  305. return code
  306. @staticmethod
  307. def checkMobile(request):
  308. if 'Demeter-Mobile' in request.headers:
  309. return True
  310. userAgent = request.headers['User-Agent']
  311. # userAgent = env.get('HTTP_USER_AGENT')
  312. _long_matches = r'googlebot-mobile|android|avantgo|blackberry|blazer|elaine|hiptop|ip(hone|od)|kindle|midp|mmp|mobile|o2|opera mini|palm( os)?|pda|plucker|pocket|psp|smartphone|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce; (iemobile|ppc)|xiino|maemo|fennec'
  313. _long_matches = re.compile(_long_matches, re.IGNORECASE)
  314. _short_matches = r'1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|e\-|e\/|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(\-|2|g)|yas\-|your|zeto|zte\-'
  315. _short_matches = re.compile(_short_matches, re.IGNORECASE)
  316. if _long_matches.search(userAgent) != None:
  317. return True
  318. user_agent = userAgent[0:4]
  319. if _short_matches.search(user_agent) != None:
  320. return True
  321. return False
  322. @staticmethod
  323. def exp(exp, value):
  324. if exp:
  325. exp = exp.replace('{n}', value)
  326. value = str(eval(exp))
  327. return value
  328. @classmethod
  329. def curl(self, url = '', param={}, method = 'get'):
  330. import requests
  331. if method == 'get':
  332. req = requests.get(url, params=param)
  333. else:
  334. req = requests.post(url, params=param)
  335. result = req.text
  336. return result
  337. @classmethod
  338. def out(self, msg='', data={}, code=0, callback='', function=''):
  339. if data:
  340. if 'page' in data and data['page']['total'] <= 0:
  341. del data['page']
  342. if 'update' in data and not data['update']:
  343. del data['update']
  344. if 'search' in data and not data['search']:
  345. del data['search']
  346. result = ''
  347. send = {}
  348. send['status'] = 1
  349. send['msg'] = msg
  350. send['data'] = data
  351. send['code'] = code
  352. if not send['data']:
  353. send['status'] = 2
  354. result = json.dumps(send)
  355. if callback:
  356. result = callback + '(' + result + ')'
  357. elif function:
  358. result = '<script>parent.' + function + '(' + result + ')' + '</script>';
  359. return result
  360. @classmethod
  361. def error(self, string):
  362. if self.request:
  363. self.request.out(string)
  364. else:
  365. self.echo(string)
  366. #os._exit(0)
  367. @classmethod
  368. def redis(self):
  369. self.initConfig()
  370. import redis
  371. config = self.config['redis']
  372. pool = redis.ConnectionPool(host=config['host'], password=config['password'], port=int(config['port']))
  373. return redis.Redis(connection_pool=pool)
  374. class Log(object):
  375. @classmethod
  376. def init(self, name):
  377. import logging
  378. from logging.handlers import RotatingFileHandler
  379. formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
  380. logger = logging.getLogger(name)
  381. logger.setLevel(logging.INFO)
  382. file_handler = RotatingFileHandler(self.file(name), maxBytes=1024*1024,backupCount=5)
  383. file_handler.setLevel(level=logging.DEBUG)
  384. file_handler.setFormatter(formatter)
  385. logger.addHandler(file_handler)
  386. return logger
  387. @classmethod
  388. def read(self, name, lines=200):
  389. file = self.file(name)
  390. if File.exists(file):
  391. return File.tail(file, lines)
  392. return ''
  393. @classmethod
  394. def file(self, name):
  395. path = File.path() + 'runtime/log/'
  396. File.mkdir(path)
  397. return os.path.join(path, 'vecan.log')
  398. class WatchDog(object):
  399. @staticmethod
  400. def init(path = [], reloads = [], recursive = False):
  401. event_handler = WatchDogHandle(reloads)
  402. dog = Observer()
  403. base = File.path()
  404. if not path:
  405. path = ['conf/',]
  406. for item in path:
  407. dog.schedule(event_handler, base + item, recursive=recursive)
  408. dog.start()
  409. return dog
  410. class WatchDogHandle(FileSystemEventHandler):
  411. @classmethod
  412. def __init__(self, reloads = False):
  413. FileSystemEventHandler.__init__(self)
  414. self.reloads = reloads
  415. @classmethod
  416. def on_modified(self, event):
  417. if not event.is_directory and '.' in event.src_path:
  418. if self.reloads:
  419. for item in self.reloads:
  420. item.reload()
  421. elif Demeter.web:
  422. Demeter.webInit(Demeter.web)
  423. else:
  424. Demeter.echo('modify ' + event.src_path)
  425. class File(object):
  426. @staticmethod
  427. def write(file, content):
  428. handle = open(file, 'w')
  429. handle.write(content)
  430. handle.close()
  431. Shell.popen('chmod +x ' + file)
  432. @staticmethod
  433. def read(path, name = ''):
  434. handle = open(path + name, 'r')
  435. content = handle.read()
  436. handle.close()
  437. return content
  438. @staticmethod
  439. def readContent(path, name = ''):
  440. content = ''
  441. handle = open(path + name, 'r')
  442. while True:
  443. line = handle.readline()
  444. if line:
  445. line = line.rstrip("\n")
  446. content = content + line
  447. else:
  448. break
  449. handle.close()
  450. return content
  451. @staticmethod
  452. def cur_path():
  453. return os.path.split(os.path.realpath(__file__))[0] + '/'
  454. @staticmethod
  455. def getFiles(path):
  456. return os.listdir(path)
  457. @staticmethod
  458. def path():
  459. return os.sys.path[0] + '/'
  460. @staticmethod
  461. def exists(name):
  462. return os.path.exists(name)
  463. @staticmethod
  464. def rename(old, new):
  465. return os.rename(old, new)
  466. @classmethod
  467. def remove(self, file):
  468. if isinstance(file, str) and self.exists(file):
  469. if os.path.isfile(file):
  470. return os.remove(file)
  471. else:
  472. # 删除非空目录
  473. return shutil.rmtree(file)
  474. return False
  475. @staticmethod
  476. def mkdir(path):
  477. if File.exists(path) == False:
  478. os.mkdir(path)
  479. return path
  480. @staticmethod
  481. def mkdirs(path):
  482. if File.exists(path) == False:
  483. os.makedirs(path)
  484. return path
  485. @staticmethod
  486. def ext(path):
  487. return os.path.splitext(path)[1]
  488. @classmethod
  489. def runtime(self, path = 'data'):
  490. return self.mkdir(self.path() + 'runtime/' + path + '/')
  491. """
  492. 实现 tail -n
  493. """
  494. @classmethod
  495. def tail(self, filepath, n=10):
  496. PAGE = 4096
  497. res = ""
  498. with open(filepath, 'rb') as f:
  499. f_len = f.seek(0, 2)
  500. rem = f_len % PAGE
  501. page_n = f_len // PAGE
  502. r_len = rem if rem else PAGE
  503. while True:
  504. # 如果读取的页大小>=文件大小,直接读取数据输出
  505. if r_len >= f_len:
  506. f.seek(0)
  507. lines = f.readlines()[::-1]
  508. break
  509. f.seek(-r_len, 2)
  510. # print('f_len: {}, rem: {}, page_n: {}, r_len: {}'.format(f_len, rem, page_n, r_len))
  511. lines = f.readlines()[::-1]
  512. count = len(lines) -1 # 末行可能不完整,减一行,加大读取量
  513. if count >= n: # 如果读取到的行数>=指定行数,则退出循环读取数据
  514. break
  515. else: # 如果读取行数不够,载入更多的页大小读取数据
  516. r_len += PAGE
  517. page_n -= 1
  518. for line in lines[:n][::-1]:
  519. res += line.decode('utf-8')
  520. return res
  521. class Shell(object):
  522. @staticmethod
  523. def popen(command, sub=False, bg=False, timeout=0):
  524. string = command
  525. if bg == True:
  526. command = command + ' 1>/dev/null 2>&1 &'
  527. if timeout > 0:
  528. proc = subprocess.Popen(command,bufsize=0,stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True, close_fds=True, preexec_fn = os.setsid)
  529. poll_seconds = .250
  530. deadline = time.time() + timeout
  531. while time.time() < deadline and proc.poll() == None:
  532. time.sleep(poll_seconds)
  533. if proc.poll() == None:
  534. os.killpg(proc.pid, signal.SIGTERM)
  535. return 'timeout'
  536. stdout,stderr = proc.communicate()
  537. return stdout
  538. elif sub == False:
  539. process = os.popen(command)
  540. output = process.read()
  541. process.close()
  542. return output
  543. else:
  544. popen = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
  545. output = ''
  546. Demeter.echo(string)
  547. while True:
  548. output = popen.stdout.readline()
  549. Demeter.echo(output)
  550. if popen.poll() is not None:
  551. break
  552. return output
  553. class Check(object):
  554. @staticmethod
  555. def match(rule, value):
  556. if not rule.match(value):
  557. return False
  558. return True
  559. @staticmethod
  560. def mobile(value):
  561. rule = re.compile(r'1\d{10}')
  562. return Check.match(rule, value)
  563. @staticmethod
  564. def number(value):
  565. try:
  566. int(value)
  567. return True
  568. except ValueError:
  569. return False
  570. @staticmethod
  571. def numberFloat(value):
  572. try:
  573. float(value)
  574. return True
  575. except ValueError:
  576. return False