core.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  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. @staticmethod
  376. def init(name):
  377. if Demeter.logger:
  378. return Demeter.logger
  379. import logging
  380. from logging.handlers import RotatingFileHandler
  381. formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
  382. Demeter.logger = logging.getLogger(name)
  383. Demeter.logger.setLevel(logging.INFO)
  384. path = File.path() + 'runtime/log/'
  385. File.mkdir(path)
  386. file_handler = RotatingFileHandler(os.path.join(path, 'vecan.log'), maxBytes=1024*1024,backupCount=5)
  387. file_handler.setLevel(level=logging.DEBUG)
  388. file_handler.setFormatter(formatter)
  389. Demeter.logger.addHandler(file_handler)
  390. return Demeter.logger
  391. class WatchDog(object):
  392. @staticmethod
  393. def init(path = [], reloads = [], recursive = False):
  394. if Demeter.dog:
  395. return Demeter.dog
  396. event_handler = WatchDogHandle(reloads)
  397. Demeter.dog = Observer()
  398. base = File.path()
  399. if not path:
  400. path = ['conf/',]
  401. for item in path:
  402. Demeter.dog.schedule(event_handler, base + item, recursive=recursive)
  403. Demeter.dog.start()
  404. return Demeter.dog
  405. class WatchDogHandle(FileSystemEventHandler):
  406. @classmethod
  407. def __init__(self, reloads = False):
  408. FileSystemEventHandler.__init__(self)
  409. self.reloads = reloads
  410. @classmethod
  411. def on_modified(self, event):
  412. if not event.is_directory and '.' in event.src_path:
  413. if self.reloads:
  414. for item in self.reloads:
  415. item.reload()
  416. elif Demeter.web:
  417. Demeter.webInit(Demeter.web)
  418. else:
  419. Demeter.echo('modify ' + event.src_path)
  420. class File(object):
  421. @staticmethod
  422. def write(file, content):
  423. handle = open(file, 'w')
  424. handle.write(content)
  425. handle.close()
  426. Shell.popen('chmod +x ' + file)
  427. @staticmethod
  428. def read(path, name = ''):
  429. handle = open(path + name, 'r')
  430. content = handle.read()
  431. handle.close()
  432. return content
  433. @staticmethod
  434. def readContent(path, name = ''):
  435. content = ''
  436. handle = open(path + name, 'r')
  437. while True:
  438. line = handle.readline()
  439. if line:
  440. line = line.rstrip("\n")
  441. content = content + line
  442. else:
  443. break
  444. handle.close()
  445. return content
  446. @staticmethod
  447. def cur_path():
  448. return os.path.split(os.path.realpath(__file__))[0] + '/'
  449. @staticmethod
  450. def getFiles(path):
  451. return os.listdir(path)
  452. @staticmethod
  453. def path():
  454. return os.sys.path[0] + '/'
  455. @staticmethod
  456. def exists(name):
  457. return os.path.exists(name)
  458. @staticmethod
  459. def rename(old, new):
  460. return os.rename(old, new)
  461. @classmethod
  462. def remove(self, file):
  463. if isinstance(file, str) and self.exists(file):
  464. if os.path.isfile(file):
  465. return os.remove(file)
  466. else:
  467. # 删除非空目录
  468. return shutil.rmtree(file)
  469. return False
  470. @staticmethod
  471. def mkdir(path):
  472. if File.exists(path) == False:
  473. os.mkdir(path)
  474. return path
  475. @staticmethod
  476. def mkdirs(path):
  477. if File.exists(path) == False:
  478. os.makedirs(path)
  479. return path
  480. @staticmethod
  481. def ext(path):
  482. return os.path.splitext(path)[1]
  483. @classmethod
  484. def runtime(self, path = 'data'):
  485. return self.mkdir(self.path() + 'runtime/' + path + '/')
  486. class Shell(object):
  487. @staticmethod
  488. def popen(command, sub=False, bg=False, timeout=0):
  489. string = command
  490. if bg == True:
  491. command = command + ' 1>/dev/null 2>&1 &'
  492. if timeout > 0:
  493. proc = subprocess.Popen(command,bufsize=0,stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True, close_fds=True, preexec_fn = os.setsid)
  494. poll_seconds = .250
  495. deadline = time.time() + timeout
  496. while time.time() < deadline and proc.poll() == None:
  497. time.sleep(poll_seconds)
  498. if proc.poll() == None:
  499. os.killpg(proc.pid, signal.SIGTERM)
  500. return 'timeout'
  501. stdout,stderr = proc.communicate()
  502. return stdout
  503. elif sub == False:
  504. process = os.popen(command)
  505. output = process.read()
  506. process.close()
  507. return output
  508. else:
  509. popen = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
  510. output = ''
  511. Demeter.echo(string)
  512. while True:
  513. output = popen.stdout.readline()
  514. Demeter.echo(output)
  515. if popen.poll() is not None:
  516. break
  517. return output
  518. class Check(object):
  519. @staticmethod
  520. def match(rule, value):
  521. if not rule.match(value):
  522. return False
  523. return True
  524. @staticmethod
  525. def mobile(value):
  526. rule = re.compile(r'1\d{10}')
  527. return Check.match(rule, value)
  528. @staticmethod
  529. def number(value):
  530. try:
  531. int(value)
  532. return True
  533. except ValueError:
  534. return False
  535. @staticmethod
  536. def numberFloat(value):
  537. try:
  538. float(value)
  539. return True
  540. except ValueError:
  541. return False