ct 0.10.8.114__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. cantools/__init__.py +24 -0
  2. cantools/_db.py +142 -0
  3. cantools/_memcache.py +76 -0
  4. cantools/_pay.py +46 -0
  5. cantools/admin.py +31 -0
  6. cantools/cfg.py +347 -0
  7. cantools/config.py +131 -0
  8. cantools/db/__init__.py +18 -0
  9. cantools/db/admin.py +27 -0
  10. cantools/db/gae/__init__.py +0 -0
  11. cantools/db/gae/model.py +127 -0
  12. cantools/db/gae/properties.py +35 -0
  13. cantools/db/wp.py +99 -0
  14. cantools/geo.py +188 -0
  15. cantools/hooks.py +13 -0
  16. cantools/scripts/__init__.py +0 -0
  17. cantools/scripts/bench.py +167 -0
  18. cantools/scripts/builder.py +272 -0
  19. cantools/scripts/deploy.py +154 -0
  20. cantools/scripts/doc.py +239 -0
  21. cantools/scripts/index.py +226 -0
  22. cantools/scripts/init.py +345 -0
  23. cantools/scripts/migrate.py +593 -0
  24. cantools/scripts/pubsub/__init__.py +28 -0
  25. cantools/scripts/pubsub/actor.py +13 -0
  26. cantools/scripts/pubsub/bots.py +143 -0
  27. cantools/scripts/pubsub/channel.py +85 -0
  28. cantools/scripts/pubsub/ps.py +145 -0
  29. cantools/scripts/pubsub/user.py +51 -0
  30. cantools/scripts/start.py +53 -0
  31. cantools/scripts/util.py +24 -0
  32. cantools/util/__init__.py +78 -0
  33. cantools/util/admin.py +620 -0
  34. cantools/util/data.py +109 -0
  35. cantools/util/media.py +303 -0
  36. cantools/util/package.py +125 -0
  37. cantools/util/system.py +73 -0
  38. cantools/web/__init__.py +9 -0
  39. cantools/web/dez_server/__init__.py +1 -0
  40. cantools/web/dez_server/controller.py +129 -0
  41. cantools/web/dez_server/cron.py +115 -0
  42. cantools/web/dez_server/daemons.py +64 -0
  43. cantools/web/dez_server/mail.py +24 -0
  44. cantools/web/dez_server/response.py +63 -0
  45. cantools/web/dez_server/routes.py +21 -0
  46. cantools/web/dez_server/server.py +229 -0
  47. cantools/web/dez_server/sms.py +12 -0
  48. cantools/web/gae_server.py +68 -0
  49. cantools/web/util.py +552 -0
  50. ct-0.10.8.114.dist-info/LICENSE +9 -0
  51. ct-0.10.8.114.dist-info/METADATA +25 -0
  52. ct-0.10.8.114.dist-info/RECORD +55 -0
  53. ct-0.10.8.114.dist-info/WHEEL +5 -0
  54. ct-0.10.8.114.dist-info/entry_points.txt +10 -0
  55. ct-0.10.8.114.dist-info/top_level.txt +1 -0
@@ -0,0 +1,129 @@
1
+ import os, sys, platform
2
+ from dez.network import SocketController, daemon_wrapper
3
+ from dez.http.server.shield import Shield
4
+ from dez.logging import get_logger_getter
5
+ from cantools import config
6
+ from .daemons import Web, Admin
7
+ from .response import Response
8
+ from .cron import Cron
9
+ from cantools import config, __version__
10
+ from ..util import *
11
+ from ...util import read, write, log as syslog
12
+
13
+ logger_getter = get_logger_getter("httpd", syslog, config.log.allow)
14
+ CTR = None
15
+
16
+ class DController(SocketController):
17
+ def __init__(self, *args, **kwargs):
18
+ self.logger = logger_getter("Controller")
19
+ SocketController.__init__(self, *args, **kwargs)
20
+ self.handlers = {}
21
+ self.modules = {}
22
+ self.blcount = 0
23
+ self.blchunk = getattr(config.web.shield, "chunk", 5)
24
+ self.logger.info("cantools: %s"%(__version__,))
25
+ self.logger.info("Python: %s"%(sys.version.split(' ')[0],))
26
+ self.logger.info("System: " + " > ".join([part for part in platform.uname() if part]))
27
+
28
+ def _respond(self, resp, *args, **kwargs):
29
+ if resp == "nothread": # "on start nothread" cron
30
+ kwargs["noLoad"] = True
31
+ elif resp: # regular request
32
+ kwargs["response"] = resp
33
+ localvars.response = resp
34
+ else: # regular cron
35
+ kwargs["noLoad"] = True
36
+ kwargs["threaded"] = True
37
+ do_respond(*args, **kwargs)
38
+
39
+ def register_handler(self, args, kwargs):
40
+ self.logger.info("register handler: %s"%(self.curpath,))
41
+ self.handlers[self.curpath] = lambda resp : self._respond(resp, *args, **kwargs)
42
+
43
+ def trigger_handler(self, rule, target, req=None, option=None):
44
+ self.curpath = rule
45
+ if rule not in self.handlers:
46
+ if target in self.modules:
47
+ self.logger.info("linking module: %s"%(target,))
48
+ self.handlers[rule] = self.modules[target]
49
+ else:
50
+ self.logger.info("importing module: %s"%(target,))
51
+ __import__(target)
52
+ self.modules[target] = self.handlers[rule]
53
+ self.handlers[rule](req and Response(req) or option)
54
+
55
+ def blup(self):
56
+ wcfg = config.web
57
+ bl = wcfg.blacklist.obj()
58
+ blen = len(bl.keys())
59
+ self.logger.warn("saving %s IPs in black.list"%(blen,))
60
+ write(bl, "black.list", isjson=True)
61
+ if wcfg.report and self.blcount != blen:
62
+ self.blcount = blen
63
+ if not blen % self.blchunk:
64
+ from cantools.web import email_admins
65
+ email_admins("sketch IPs blacklisted", "sketch count: %s"%(blen,))
66
+ wcfg.blacklister and wcfg.blacklister.update(bl)
67
+
68
+ class PaperShield(object):
69
+ def __init__(self):
70
+ self.logger = logger_getter("PaperShield")
71
+ self.default = { "reason": "I'm just a paper shield!" }
72
+ self.ips = {}
73
+
74
+ def __call__(self, path, ip, fspath=False, count=True):
75
+ self.logger.access('NOOP > paperShield("%s", "%s", fspath=%s, count=%s)'%(path, ip, fspath, count))
76
+
77
+ def suss(self, ip, reason):
78
+ self.logger.access("suss(%s) -> %s"%(ip, reason))
79
+ self.ips[ip] = { "reason": reason }
80
+
81
+ def ip(self, ip):
82
+ return self.ips.get(ip, self.default)
83
+
84
+ def setBlacklist():
85
+ bl = {}
86
+ for preban in config.web.blacklist:
87
+ bl[preban] = "config ban"
88
+ if os.path.isfile("black.list"):
89
+ try:
90
+ bsaved = read("black.list", isjson=True)
91
+ except: # old style
92
+ bsaved = {}
93
+ for line in read("black.list", lines=True):
94
+ bsaved[line.strip()] = "legacy ban"
95
+ bsaved and bl.update(bsaved)
96
+ config.web.update("blacklist", bl)
97
+
98
+ def getController():
99
+ global CTR
100
+ if not CTR:
101
+ # controller
102
+ CTR = DController()
103
+
104
+ shield = None
105
+ shfg = config.web.shield
106
+ if shfg: # web/admin share shield and blacklist
107
+ setBlacklist()
108
+ shield = Shield(config.web.blacklist, logger_getter, CTR.blup,
109
+ getattr(shfg, "limit", 400),
110
+ getattr(shfg, "interval", 2))
111
+ config.web.update("shield", shield or PaperShield())
112
+ mempad = config.mempad
113
+
114
+ # web
115
+ CTR.web = CTR.register_address(config.web.host,
116
+ config.web.port, dclass=daemon_wrapper(Web, logger_getter, shield, mempad))
117
+ CTR.web.controller = CTR
118
+
119
+ # admin
120
+ config.admin.update("pw",
121
+ config.cache("admin password? ", overwrite=config.newpass))
122
+ CTR.admin = CTR.register_address(config.admin.host,
123
+ config.admin.port, dclass=daemon_wrapper(Admin, logger_getter, shield, mempad))
124
+ CTR.admin.controller = CTR
125
+
126
+ # cron
127
+ Cron(CTR, logger_getter)
128
+
129
+ return CTR
@@ -0,0 +1,115 @@
1
+ import time, os
2
+ from datetime import datetime, timedelta
3
+ from rel import timeout
4
+ from cantools import config
5
+ from cantools.util import read, write
6
+ from ..util import do_respond
7
+
8
+ secsPerUnit = {
9
+ "days": 60 * 60 * 24,
10
+ "hours": 60 * 60,
11
+ "minutes": 60,
12
+ "mins": 60,
13
+ "seconds": 1
14
+ }
15
+
16
+ class Rule(object):
17
+ def __init__(self, controller, scheduler, url, rule, logger_getter):
18
+ self.logger = logger_getter("Rule(%s -> %s)"%(rule, url))
19
+ self.controller = controller
20
+ self.scheduler = scheduler
21
+ self.url = url
22
+ self.rule = rule
23
+ self.exact = len(rule) == 5 # 17:00
24
+ self.words = rule.split(" ")
25
+ self.timer = timeout(None, self.trigger)
26
+ self.parse()
27
+
28
+ def trigger(self):
29
+ self.logger.info("trigger: %s (%s)"%(self.url, getattr(self, "seconds", self.rule)))
30
+ self.controller.trigger_handler(self.url, self.url[1:],
31
+ option=self.rule.endswith("nothread") and "nothread")
32
+ if not self.exact and not self.rule.startswith("on start"):
33
+ self.scheduler.update(self)
34
+ return True
35
+
36
+ def start(self):
37
+ if self.rule.startswith("on start"):
38
+ return
39
+ self.logger.info("start (%s seconds)"%(self.seconds,))
40
+ self.timer.add(self.seconds)
41
+ if self.exact:
42
+ self.timer.delay = 60 * 60 * 24
43
+ else:
44
+ ff = self.scheduler.ff(self)
45
+ if ff:
46
+ self.logger.info("rescheduled for %s seconds"%(self.timer.delay - ff,))
47
+ self.timer.expiration -= ff
48
+
49
+ def parse(self):
50
+ self.logger.info("parse")
51
+ if self.exact:
52
+ hours, mins = self.rule.split(":")
53
+ n = datetime.now()
54
+ t = datetime(n.year, n.month, n.day, int(hours), int(mins))
55
+ self.seconds = (t - n).seconds
56
+ elif self.rule.startswith("on start"):
57
+ self.logger.info("triggering start script")
58
+ self.trigger()
59
+ elif len(self.words) == 3 and self.words[0] == "every": # every [NUMBER] [UNIT]
60
+ num = int(self.words[1])
61
+ unit = self.words[2]
62
+ self.seconds = num * secsPerUnit[unit]
63
+ else: # we can implement more later...
64
+ self.logger.error("can't parse: %s"%(self.rule,))
65
+
66
+ class Scheduler(object):
67
+ def __init__(self, logger_getter):
68
+ self.logger_getter = logger_getter
69
+ self.logger = logger_getter("Scheduler")
70
+ self.tsfile = os.path.join(os.path.abspath("."), "cron.ts")
71
+ self.lasts = read(self.tsfile, isjson=True, default={})
72
+ self.logger.info("initialized with %s timestamps"%(len(self.lasts.keys()),))
73
+ self.logger.info("saving state to timestamp file at %s"%(self.tsfile,))
74
+
75
+ def update(self, rule):
76
+ self.lasts[rule.url] = time.time()
77
+ self.logger.info("updating %s timestamp to %s"%(rule.url, self.lasts[rule.url]))
78
+ write(self.lasts, self.tsfile, isjson=True)
79
+
80
+ def ff(self, rule):
81
+ if rule.url in self.lasts:
82
+ ff = min(rule.seconds, time.time() - self.lasts[rule.url])
83
+ self.logger.info("fast-forwarding %s %s seconds"%(rule.url, ff))
84
+ return ff
85
+ elif config.cron.catchup:
86
+ self.logger.info("catching up %s"%(rule.url,))
87
+ return rule.seconds
88
+
89
+ class Cron(object):
90
+ def __init__(self, controller, logger_getter):
91
+ self.logger_getter = logger_getter
92
+ self.logger = logger_getter("Cron")
93
+ self.scheduler = Scheduler(logger_getter)
94
+ self.controller = controller
95
+ self.timers = {}
96
+ self.parse()
97
+ self.start()
98
+
99
+ def parse(self):
100
+ self.logger.info("parse")
101
+ url = None
102
+ for line in read("cron.yaml", True):
103
+ if line.startswith("- description: "):
104
+ self.logger.info("initializing %s"%(line[15:],))
105
+ elif line.startswith(" url: "):
106
+ url = line[7:].strip()
107
+ elif url:
108
+ self.timers[url] = Rule(self.controller, self.scheduler,
109
+ url, line[12:].strip(), self.logger_getter)
110
+ url = None
111
+
112
+ def start(self):
113
+ self.logger.info("start")
114
+ for rule in list(self.timers.values()):
115
+ rule.start()
@@ -0,0 +1,64 @@
1
+ import sys, json, gc, os, inspect, threading
2
+ try:
3
+ import psutil
4
+ except ImportError as e:
5
+ pass # google crap engine (get it if you need it!)
6
+ from dez.memcache import get_memcache
7
+ from dez.http.application import HTTPApplication
8
+ from .routes import static, cb
9
+ from cantools import config
10
+ sys.path.insert(0, ".") # for dynamically loading modules
11
+
12
+ A_STATIC = {
13
+ "dynamic": { "/": "_/dynamic", "/css/": "_/css", "/js/CT/": "js/CT", "/logs/": "logs", "/logs": "logs" },
14
+ "static": { "/": "_/static", "/css/": "_/css", "/js/CT/": "js/CT", "/logs/": "logs", "/logs": "logs" },
15
+ "production": { "/": "_/production", "/css/": "_/css", "/logs/": "logs", "/logs": "logs" },
16
+ }
17
+ A_CB = { "/admin": "admin", "/_db": "_db" }
18
+
19
+ class CTWebBase(HTTPApplication):
20
+ def __init__(self, bind_address, port, logger_getter, static=static, cb=cb, whitelist=[], blacklist=[], shield=False, mempad=0):
21
+ isprod = config.mode == "production"
22
+ HTTPApplication.__init__(self, bind_address, port, logger_getter, "dez/cantools",
23
+ config.ssl.certfile, config.ssl.keyfile, config.ssl.cacerts,
24
+ isprod, config.web.rollz, isprod, whitelist, blacklist, shield, mempad, config.web.xorigin)
25
+ self.memcache = get_memcache()
26
+ self.handlers = {}
27
+ for key, val in list(static.items()):
28
+ self.add_static_rule(key, val)
29
+ for key, val in list(cb.items()):
30
+ self.add_cb_rule(key, self._handler(key, val))
31
+
32
+ def _handler(self, rule, target):
33
+ self.logger.info("setting handler: %s %s"%(rule, target))
34
+ def h(req):
35
+ self.logger.info("triggering handler: %s %s"%(rule, target))
36
+ self.controller.trigger_handler(rule, target, req)
37
+ return h
38
+
39
+ class Web(CTWebBase):
40
+ def __init__(self, bind_address, port, logger_getter, shield, mempad):
41
+ self.logger = logger_getter("Web")
42
+ wcfg = config.web
43
+ CTWebBase.__init__(self, bind_address, port, logger_getter,
44
+ whitelist=wcfg.whitelist, blacklist=wcfg.blacklist, shield=shield, mempad=mempad)
45
+
46
+ class Admin(CTWebBase):
47
+ def __init__(self, bind_address, port, logger_getter, shield, mempad):
48
+ self.logger = logger_getter("Admin")
49
+ acfg = config.admin
50
+ CTWebBase.__init__(self, bind_address, port, logger_getter, # share shield/blacklist
51
+ A_STATIC[config.mode], A_CB, acfg.whitelist, config.web.blacklist, shield, mempad)
52
+ self.add_cb_rule("/_report", self.report)
53
+
54
+ def report(self, req):
55
+ report = json.dumps({
56
+ "threads": threading.active_count(),
57
+ "stack_frames": len(inspect.stack()),
58
+ "web": self.controller.web.daemon.counter.report(),
59
+ "admin": self.daemon.counter.report(),
60
+ "gc": len(gc.get_objects()),
61
+ "mem": psutil.Process(os.getpid()).memory_percent()
62
+ })
63
+ self.logger.info(report)
64
+ self.daemon.respond(req, report)
@@ -0,0 +1,24 @@
1
+ from catmail import Mailer, Reader, Scanner
2
+ from catmail import config as catfyg
3
+ from ..util import config
4
+
5
+ catfyg.set({
6
+ "html": config.mailhtml,
7
+ "gmailer": config.gmailer,
8
+ "scantick": config.mailscantick,
9
+ "verbose": config.mailoud,
10
+ "cache": config.cache
11
+ })
12
+ catfyg.admin.update("contacts", config.admin.contacts)
13
+ catfyg.admin.update("reportees", config.admin.reportees)
14
+
15
+ mailer = Mailer(config.mailer, config.mailername)
16
+ send_mail = mailer.mail
17
+ email_admins = mailer.admins
18
+ email_reportees = mailer.reportees
19
+
20
+ reader = Reader(config.mailer)
21
+ check_inbox = reader.inbox
22
+
23
+ scanner = Scanner(reader)
24
+ on_mail = scanner.on
@@ -0,0 +1,63 @@
1
+ import json
2
+ from rel import abort_branch, timeout
3
+ from dez.http.server import HTTPResponse
4
+ from cantools import config
5
+ from ..util import *
6
+
7
+ files = {}
8
+
9
+ class Response(object):
10
+ def __init__(self, request):
11
+ self.id = request.id
12
+ self.ip = request.real_ip
13
+ self.request = request
14
+ self.response = HTTPResponse(request)
15
+
16
+ def _read(self):
17
+ b = self.request.body or json.dumps(rec_conv(self.request.qs_params))
18
+ # print(b)
19
+ ctype = self.request.headers.get("content-type")
20
+ if ctype and ctype.startswith("multipart/form-data"):
21
+ if type(b) != bytes:
22
+ b = b.encode()
23
+ obj = {}
24
+ splitter, body = b.rsplit(b"\r\n", 2)[0].split(b"\r\n", 1)
25
+ for chunk in body.split(b"\r\n%s\r\n"%(splitter,)):
26
+ nameline, data = chunk.split(b"\r\n\r\n", 1)
27
+ nameline = nameline.decode()
28
+ name = nameline.split("; filename=")[0][:-1].split('name="')[1]
29
+ if "filename=" in nameline:
30
+ signature = "%s%s"%(self.id, name)
31
+ files[signature] = data
32
+ obj[name] = signature
33
+ else:
34
+ obj[name] = data
35
+ b = json.dumps(rec_conv(obj))
36
+ return b
37
+
38
+ def _send(self, *args, **kwargs):
39
+ wcfg = config.web
40
+ if wcfg.xorigin:
41
+ self.response.headers["Access-Control-Allow-Origin"] = "*"
42
+ if wcfg.csp:
43
+ if wcfg.csp.startswith("auto"):
44
+ d = wcfg.domain
45
+ if wcfg.csp != "autosub":
46
+ d = ".".join(d.split(".")[-2:])
47
+ wcfg.update("csp", "default-src 'self' %s *.%s"%(d, d))
48
+ self.response.headers["Content-Security-Policy"] = wcfg.csp
49
+ self.response.write(*args, **kwargs)
50
+
51
+ def _close(self):
52
+ timeout(.001, self.response.dispatch)
53
+ abort_branch()
54
+
55
+ def _header(self, *args, **kwargs):
56
+ self.response.__setitem__(*args, **kwargs)
57
+
58
+ def set_cbs(self):
59
+ set_read(self._read)
60
+ set_header(self._header)
61
+ set_send(self._send)
62
+ set_close(self._close)
63
+ set_redir(self.response.redirect)
@@ -0,0 +1,21 @@
1
+ from ...util import read
2
+
3
+ url = None
4
+ static = {}
5
+ cb = {}
6
+
7
+ for line in read("app.yaml", True):
8
+ if line.startswith("- url: "):
9
+ url = line[7:].strip()
10
+ elif url:
11
+ if line.startswith(" static_dir: "):
12
+ target = line[14:].strip()
13
+ if "*" in url: # bad regex detection...
14
+ static[url] = target
15
+ else:
16
+ url = url.rstrip("/")
17
+ static[url] = static[url + "/"] = target
18
+ url = None
19
+ elif line.startswith(" script: "):
20
+ cb[url] = line[10:].split(".")[0]
21
+ url = None
@@ -0,0 +1,229 @@
1
+ import os, rel, ssl, sys, json
2
+ from dez.http import fetch as dfetch, post as dpost
3
+ from ..util import *
4
+ from ...util import set_log, set_error, init_rel
5
+ from .mail import send_mail, email_admins, email_reportees, mailer, reader, check_inbox, scanner, on_mail
6
+ from .sms import send_sms
7
+ from .controller import getController
8
+ from cantools import config
9
+
10
+ def fdup():
11
+ import resource
12
+ from cantools.util import log # gives us logger set in run_dez_webserver()
13
+ log("checking the number of file descriptors allowed on your system", important=True)
14
+ soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
15
+ half = int(hard / 2)
16
+ log("soft: %s. hard: %s. half: %s"%(soft, hard, half))
17
+ if soft < half:
18
+ log("increasing soft limit to half of hard limit")
19
+ resource.setrlimit(resource.RLIMIT_NOFILE, (half, hard))
20
+ log("new limits! soft: %s. hard: %s"%resource.getrlimit(resource.RLIMIT_NOFILE))
21
+
22
+ def log_kernel():
23
+ from cantools.web.util import log
24
+ log(json.dumps(rel.report()), "kernel")
25
+ return True
26
+
27
+ TMSNAP = None
28
+
29
+ def log_tracemalloc():
30
+ global TMSNAP
31
+ import tracemalloc
32
+ from cantools.util import log
33
+ snapshot = tracemalloc.take_snapshot()
34
+ log("[LINEMALLOC START]", important=True)
35
+ if TMSNAP:
36
+ lines = snapshot.compare_to(TMSNAP, 'lineno')
37
+ else:
38
+ lines = snapshot.statistics("lineno")
39
+ TMSNAP = snapshot
40
+ for line in lines[:10]:
41
+ log(line)
42
+ log("[LINEMALLOC END]", important=True)
43
+ return True
44
+
45
+ PROC = None
46
+
47
+ def log_openfiles():
48
+ global PROC
49
+ from cantools.util import log
50
+ if not PROC:
51
+ import psutil
52
+ PROC = psutil.Process(os.getpid())
53
+ ofz = PROC.open_files()
54
+ if config.log.oflist:
55
+ log("OPEN FILES: %s"%(ofz,), important=True)
56
+ else:
57
+ log("OPEN FILE COUNT: %s"%(len(ofz),), important=True)
58
+ return True
59
+
60
+ def quit():
61
+ from cantools.util import log
62
+ if config.web.errlog:
63
+ log("closing error log", important=True)
64
+ sys.stderr.close()
65
+ log("quitting - goodbye!", important=True)
66
+
67
+ def run_dez_webserver():
68
+ if not config.ssl.verify and hasattr(ssl, "_https_verify_certificates"):
69
+ ssl._https_verify_certificates(False)
70
+ c = getController()
71
+ setlog(c.web.logger.simple)
72
+ if config.web.log:
73
+ set_log(os.path.join("logs", config.web.log))
74
+ init_rel()
75
+ clog = config.log
76
+ if clog.openfiles:
77
+ rel.timeout(clog.openfiles, log_openfiles)
78
+ if clog.tracemalloc:
79
+ import tracemalloc
80
+ tracemalloc.start()
81
+ rel.timeout(clog.tracemalloc, log_tracemalloc)
82
+ if "kernel" in clog.allow:
83
+ rel.timeout(1, log_kernel)
84
+ set_error(fail)
85
+ if config.fdup:
86
+ fdup()
87
+ if config.web.errlog:
88
+ sys.stderr = open(os.path.join("logs", config.web.errlog), "a")
89
+ c.start(quit)
90
+
91
+ def dweb():
92
+ return getController().web
93
+
94
+ def respond(*args, **kwargs):
95
+ getController().register_handler(args, kwargs)
96
+
97
+ def _ctjson(result):
98
+ from cantools.util import log # gives us logger set in run_dez_webserver()
99
+ result = result.decode()
100
+ code = result[0]
101
+ if code not in "0123":
102
+ log("response encoded:")
103
+ log(result)
104
+ log("attempting decode")
105
+ result = dec(result)
106
+ if code in "02":
107
+ from cantools.util import log
108
+ log("request failed!! : %s"%(result,), important=True)
109
+ elif code == "3":
110
+ return rec_conv(json.loads(result[1:]), True)
111
+ else:
112
+ return json.loads(result[1:])
113
+
114
+ def parse_url_parts(host, path, port, protocol):
115
+ if "://" in host:
116
+ protocol, host = host.split("://", 1)
117
+ if "/" in host:
118
+ host, path = host.split("/", 1)
119
+ path = "/" + path
120
+ else:
121
+ path = "/"
122
+ if ":" in host:
123
+ host, port = host.split(":")
124
+ port = int(port)
125
+ elif not port:
126
+ port = protocol == "https" and 443 or 80
127
+ return host, path, port, protocol
128
+
129
+ def fetch(host, path="/", port=None, asjson=False, cb=None, timeout=1, asyn=False, protocol="http", ctjson=False, qsp=None, fakeua=False, retries=5):
130
+ from cantools.util import log # gives us logger set in run_dez_webserver()
131
+ host, path, port, protocol = parse_url_parts(host, path, port, protocol)
132
+ if qsp:
133
+ path += "?"
134
+ for k, v in list(qsp.items()):
135
+ path += "%s=%s&"%(k, v)
136
+ path = path[:-1]
137
+ gkwargs = {}
138
+ headers = {}
139
+ if fakeua:
140
+ if type(fakeua) is str:
141
+ headers['User-Agent'] = fakeua
142
+ else:
143
+ headers['User-Agent'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36'
144
+ gkwargs["headers"] = headers
145
+ if asyn or cb: # asyn w/o cb works, will just log
146
+ secure = protocol == "https"
147
+ if ctjson:
148
+ orig_cb = cb or log
149
+ cb = lambda v : orig_cb(_ctjson(v))
150
+ return dfetch(host, path, port, secure, headers, cb, timeout, asjson)
151
+ if timeout:
152
+ gkwargs["timeout"] = timeout
153
+ furl = "%s://%s:%s%s"%(protocol, host, port, path)
154
+ log("fetch %s"%(furl,))
155
+ return syncreq(furl, "get", asjson, ctjson, retries, gkwargs)
156
+
157
+ def post(host, path="/", port=80, data=None, protocol="http", asjson=False, ctjson=False, text=None, cb=None):
158
+ if ctjson:
159
+ data = rec_conv(data)
160
+ if cb:
161
+ if ctjson:
162
+ orig_cb = cb
163
+ cb = lambda v : orig_cb(_ctjson(v))
164
+ host, path, port, protocol = parse_url_parts(host, path, port, protocol)
165
+ return dpost(host, path, port, protocol == "https", data=data, text=text, cb=cb)
166
+ url = "://" in host and host or "%s://%s:%s%s"%(protocol, host, port, path)
167
+ log("post %s"%(url,))
168
+ kwargs = {}
169
+ if data:
170
+ kwargs["json"] = data
171
+ elif text:
172
+ kwargs["data"] = text
173
+ return syncreq(url, "post", asjson, ctjson, rekwargs=kwargs)
174
+
175
+ def _dosyncreq(requester, url, asjson, ctjson, rekwargs):
176
+ result = requester(url, **rekwargs).content
177
+ if ctjson:
178
+ return _ctjson(result)
179
+ return asjson and json.loads(result) or result
180
+
181
+ def syncreq(url, method="get", asjson=False, ctjson=False, retries=5, rekwargs={}):
182
+ import time, requests
183
+ attempt = 1
184
+ requester = getattr(requests, method)
185
+ while attempt < retries:
186
+ try:
187
+ return _dosyncreq(requester, url, asjson, ctjson, rekwargs)
188
+ except requests.exceptions.ConnectionError:
189
+ log("syncreq(%s %s) attempt #%s failed"%(method, url, attempt))
190
+ time.sleep(1)
191
+ attempt += 1
192
+ return _dosyncreq(requester, url, asjson, ctjson, rekwargs) # final try-less try
193
+
194
+ # file uploads
195
+ def read_file(data_field):
196
+ from .response import files
197
+ return files.pop(data_field)
198
+
199
+ # memcache stuff
200
+ def getmem(key, tojson=True):
201
+ return dweb().memcache.get(key, tojson)
202
+
203
+ def setmem(key, val, fromjson=True):
204
+ dweb().memcache.set(key, val, fromjson)
205
+
206
+ def delmem(key):
207
+ dweb().memcache.rm(key)
208
+
209
+ def clearmem():
210
+ dweb().memcache.clear()
211
+
212
+ def getcache():
213
+ c = {}
214
+ orig = dweb().memcache.cache
215
+ for k, v in list(orig.items()):
216
+ if v.__class__ == set:
217
+ v = list(v)
218
+ elif v.__class__ == dict and "ttl" in v: # countdown
219
+ v = str(v)
220
+ c[k] = v
221
+ return c
222
+
223
+ set_getmem(getmem)
224
+ set_setmem(setmem)
225
+ set_delmem(delmem)
226
+ set_clearmem(clearmem)
227
+
228
+ if __name__ == "__main__":
229
+ run_dez_webserver()
@@ -0,0 +1,12 @@
1
+ from .mail import send_mail
2
+ from cantools import config
3
+
4
+ carriers = {
5
+ 'at&t': 'mms.att.net',
6
+ 'verizon': 'vtext.com',
7
+ 'tmobile': 'tmomail.net',
8
+ 'sprint': 'page.nextel.com'
9
+ }
10
+
11
+ def send_sms(number, subject="hello", body="goodbye", carrier="at&t"):
12
+ send_mail(to="%s@%s"%(number, carriers[carrier]), subject=subject, body=body)