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.
- cantools/__init__.py +24 -0
- cantools/_db.py +142 -0
- cantools/_memcache.py +76 -0
- cantools/_pay.py +46 -0
- cantools/admin.py +31 -0
- cantools/cfg.py +347 -0
- cantools/config.py +131 -0
- cantools/db/__init__.py +18 -0
- cantools/db/admin.py +27 -0
- cantools/db/gae/__init__.py +0 -0
- cantools/db/gae/model.py +127 -0
- cantools/db/gae/properties.py +35 -0
- cantools/db/wp.py +99 -0
- cantools/geo.py +188 -0
- cantools/hooks.py +13 -0
- cantools/scripts/__init__.py +0 -0
- cantools/scripts/bench.py +167 -0
- cantools/scripts/builder.py +272 -0
- cantools/scripts/deploy.py +154 -0
- cantools/scripts/doc.py +239 -0
- cantools/scripts/index.py +226 -0
- cantools/scripts/init.py +345 -0
- cantools/scripts/migrate.py +593 -0
- cantools/scripts/pubsub/__init__.py +28 -0
- cantools/scripts/pubsub/actor.py +13 -0
- cantools/scripts/pubsub/bots.py +143 -0
- cantools/scripts/pubsub/channel.py +85 -0
- cantools/scripts/pubsub/ps.py +145 -0
- cantools/scripts/pubsub/user.py +51 -0
- cantools/scripts/start.py +53 -0
- cantools/scripts/util.py +24 -0
- cantools/util/__init__.py +78 -0
- cantools/util/admin.py +620 -0
- cantools/util/data.py +109 -0
- cantools/util/media.py +303 -0
- cantools/util/package.py +125 -0
- cantools/util/system.py +73 -0
- cantools/web/__init__.py +9 -0
- cantools/web/dez_server/__init__.py +1 -0
- cantools/web/dez_server/controller.py +129 -0
- cantools/web/dez_server/cron.py +115 -0
- cantools/web/dez_server/daemons.py +64 -0
- cantools/web/dez_server/mail.py +24 -0
- cantools/web/dez_server/response.py +63 -0
- cantools/web/dez_server/routes.py +21 -0
- cantools/web/dez_server/server.py +229 -0
- cantools/web/dez_server/sms.py +12 -0
- cantools/web/gae_server.py +68 -0
- cantools/web/util.py +552 -0
- ct-0.10.8.114.dist-info/LICENSE +9 -0
- ct-0.10.8.114.dist-info/METADATA +25 -0
- ct-0.10.8.114.dist-info/RECORD +55 -0
- ct-0.10.8.114.dist-info/WHEEL +5 -0
- ct-0.10.8.114.dist-info/entry_points.txt +10 -0
- ct-0.10.8.114.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""
|
|
2
|
+
### Usage: ctbench [-bmsv] [--domain=DOMAIN] [--port=PORT] [--number=NUMBER] [--concurrency=CONCURRENCY] [--weight=WEIGHT] [--keys=KEYS]
|
|
3
|
+
|
|
4
|
+
### Options:
|
|
5
|
+
-h, --help show this help message and exit
|
|
6
|
+
-e, --encrypted use encrypted (https) connection
|
|
7
|
+
-d DOMAIN, --domain=DOMAIN
|
|
8
|
+
hostname (default: 0.0.0.0)
|
|
9
|
+
-p PORT, --port=PORT port (default=8080)
|
|
10
|
+
-n NUMBER, --number=NUMBER
|
|
11
|
+
number of requests (default=5000)
|
|
12
|
+
-c CONCURRENCY, --concurrency=CONCURRENCY
|
|
13
|
+
number of connections (default=100)
|
|
14
|
+
-w WEIGHT, --weight=WEIGHT
|
|
15
|
+
weight (size) of memcache blocks (default=10)
|
|
16
|
+
-k KEYS, --keys=KEYS key count - number of memcache blocks (default=10)
|
|
17
|
+
-l PIPELINERS, --pipeliners=PIPELINERS
|
|
18
|
+
number of pipelined requests (default=50)
|
|
19
|
+
-s, --static random static (html/css) requests (default=False)
|
|
20
|
+
-b, --blobs random blob requests (default=False)
|
|
21
|
+
-m, --memcache random memcache requests (default=False)
|
|
22
|
+
-v, --validate validate return values (default=False)
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import os, rel, json
|
|
26
|
+
from optparse import OptionParser
|
|
27
|
+
from dez.bench import MultiTester
|
|
28
|
+
from cantools import config
|
|
29
|
+
from cantools.util import cmd, log, read, error, token
|
|
30
|
+
from cantools.web import post
|
|
31
|
+
|
|
32
|
+
NUMBER = 5000
|
|
33
|
+
CONCURRENCY = 100
|
|
34
|
+
WEIGHT = 10
|
|
35
|
+
KEYS = 10
|
|
36
|
+
PIPES = 50
|
|
37
|
+
|
|
38
|
+
class Bencher(object):
|
|
39
|
+
def __init__(self, options):
|
|
40
|
+
self.options = options
|
|
41
|
+
if options.blobs or options.memcache or options.static:
|
|
42
|
+
self.multi()
|
|
43
|
+
else:
|
|
44
|
+
self.single()
|
|
45
|
+
|
|
46
|
+
def single(self):
|
|
47
|
+
log("single (direct dbench) mode", important=True)
|
|
48
|
+
oz = self.options
|
|
49
|
+
rel.signal(2, rel.abort)
|
|
50
|
+
cstr = "dbench %s %s %s %s %s"%(oz.domain, oz.port, oz.number, oz.concurrency, oz.pipeliners)
|
|
51
|
+
if oz.encrypted:
|
|
52
|
+
cstr += " -e"
|
|
53
|
+
cmd(cstr)
|
|
54
|
+
|
|
55
|
+
def initmc(self):
|
|
56
|
+
oz = self.options
|
|
57
|
+
self.memcache = {}
|
|
58
|
+
self.mccount = 0
|
|
59
|
+
proto = oz.encrypted and "https" or "http"
|
|
60
|
+
for key in map(lambda k : "bencher%s"%(k,), range(oz.keys)):
|
|
61
|
+
val = self.memcache[key] = token(oz.weight)
|
|
62
|
+
post(oz.domain, "/_memcache", oz.port, {
|
|
63
|
+
"action": "set",
|
|
64
|
+
"key": key,
|
|
65
|
+
"value": val
|
|
66
|
+
}, proto, cb=self.regmc)
|
|
67
|
+
rel.signal(2, rel.abort)
|
|
68
|
+
rel.dispatch()
|
|
69
|
+
|
|
70
|
+
def initpaths(self):
|
|
71
|
+
oz = self.options
|
|
72
|
+
paths = self.paths = []
|
|
73
|
+
self.values = {}
|
|
74
|
+
if oz.blobs:
|
|
75
|
+
paths += self.regfiles("blob", binary=True)
|
|
76
|
+
if oz.static:
|
|
77
|
+
paths += self.regfiles("css")
|
|
78
|
+
paths += self.regfiles(config.build.web[config.mode] or config.build.web.compiled[config.mode],
|
|
79
|
+
"/", suffix=".html")
|
|
80
|
+
if oz.memcache:
|
|
81
|
+
paths += self.regfiles("memcache", "/_memcache?action=get&key=", self.memcache)
|
|
82
|
+
paths or error("nothing to request!")
|
|
83
|
+
log("randomizing requests among %s total items"%(len(paths),))
|
|
84
|
+
|
|
85
|
+
def validator(self, path, value, headers):
|
|
86
|
+
vlen = len(value)
|
|
87
|
+
clen = int(headers['content-length'])
|
|
88
|
+
if vlen != clen:
|
|
89
|
+
error("content-length mismatch! %s != %s"%(vlen, clen))
|
|
90
|
+
if "memcache" in path:
|
|
91
|
+
value = json.loads(value[1:])
|
|
92
|
+
elif "blob" not in path:
|
|
93
|
+
value = value.decode()
|
|
94
|
+
if path in self.values and self.values[path] != value:
|
|
95
|
+
log("returned:\n%s"%(value,), important=True)
|
|
96
|
+
log("should have returned:\n%s"%(self.values[path],), important=True)
|
|
97
|
+
error("return value mismatch: %s"%(path,))
|
|
98
|
+
|
|
99
|
+
def regmc(self, resp):
|
|
100
|
+
self.mccount += 1
|
|
101
|
+
log("regmc %s"%(self.mccount,))
|
|
102
|
+
if self.mccount == self.options.keys:
|
|
103
|
+
log("registered %s memcache keys"%(self.mccount,))
|
|
104
|
+
self._multi()
|
|
105
|
+
|
|
106
|
+
def regfiles(self, fpath, upath=None, files=None, binary=False, suffix=None):
|
|
107
|
+
upath = upath or "/%s/"%(fpath,)
|
|
108
|
+
fnames = files and files.keys() or os.listdir(fpath)
|
|
109
|
+
log("including %s %s files"%(len(fnames), fpath))
|
|
110
|
+
if self.options.validate:
|
|
111
|
+
paths = []
|
|
112
|
+
for fname in fnames:
|
|
113
|
+
if suffix and not fname.endswith(suffix):
|
|
114
|
+
continue
|
|
115
|
+
path = "%s%s"%(upath, fname)
|
|
116
|
+
paths.append(path)
|
|
117
|
+
self.values[path] = files and files[fname] or read(os.path.join(fpath, fname), binary=binary)
|
|
118
|
+
else:
|
|
119
|
+
paths = list(map(lambda n : "%s%s"%(upath, n), fnames))
|
|
120
|
+
return paths
|
|
121
|
+
|
|
122
|
+
def _multi(self):
|
|
123
|
+
oz = self.options
|
|
124
|
+
self.initpaths()
|
|
125
|
+
MultiTester(oz.domain, oz.port, self.paths, oz.number, oz.concurrency,
|
|
126
|
+
oz.pipeliners, oz.encrypted, oz.validate and self.validator).start()
|
|
127
|
+
|
|
128
|
+
def multi(self):
|
|
129
|
+
log("multi (randomized requests) mode", important=True)
|
|
130
|
+
if self.options.memcache:
|
|
131
|
+
self.initmc()
|
|
132
|
+
else:
|
|
133
|
+
self._multi()
|
|
134
|
+
|
|
135
|
+
def run():
|
|
136
|
+
parser = OptionParser("ctbench [-bmsve] [--domain=DOMAIN] [--port=PORT] [--number=NUMBER] [--concurrency=CONCURRENCY] [--weight=WEIGHT] [--keys=KEYS] [--pipeliners=PIPELINERS]")
|
|
137
|
+
parser.add_option("-e", "--encrypted", action="store_true", dest="encrypted",
|
|
138
|
+
default=False, help="use encrypted (https) connection")
|
|
139
|
+
parser.add_option("-d", "--domain", dest="domain", default=config.web.host,
|
|
140
|
+
help="hostname (default: %s)"%(config.web.host,))
|
|
141
|
+
parser.add_option("-p", "--port", dest="port", default=config.web.port,
|
|
142
|
+
help="port (default=%s)"%(config.web.port,))
|
|
143
|
+
parser.add_option("-n", "--number", dest="number", default=NUMBER,
|
|
144
|
+
help="number of requests (default=%s)"%(NUMBER,))
|
|
145
|
+
parser.add_option("-c", "--concurrency", dest="concurrency", default=CONCURRENCY,
|
|
146
|
+
help="number of connections (default=%s)"%(CONCURRENCY,))
|
|
147
|
+
parser.add_option("-w", "--weight", dest="weight", default=WEIGHT,
|
|
148
|
+
help="weight (size) of memcache blocks (default=%s)"%(WEIGHT,))
|
|
149
|
+
parser.add_option("-k", "--keys", dest="keys", default=KEYS,
|
|
150
|
+
help="key count - number of memcache blocks (default=%s)"%(KEYS,))
|
|
151
|
+
parser.add_option("-l", "--pipeliners", dest="pipeliners", default=PIPES,
|
|
152
|
+
help="number of pipelined requests (default=%s)"%(PIPES,))
|
|
153
|
+
parser.add_option("-s", "--static", action="store_true", dest="static",
|
|
154
|
+
default=False, help="random static (html/css) requests (default=False)")
|
|
155
|
+
parser.add_option("-b", "--blobs", action="store_true", dest="blobs",
|
|
156
|
+
default=False, help="random blob requests (default=False)")
|
|
157
|
+
parser.add_option("-m", "--memcache", action="store_true", dest="memcache",
|
|
158
|
+
default=False, help="random memcache requests (default=False)")
|
|
159
|
+
parser.add_option("-v", "--validate", action="store_true", dest="validate",
|
|
160
|
+
default=False, help="validate return values (default=False)")
|
|
161
|
+
options = parser.parse_args()[0]
|
|
162
|
+
for key in ["port", "number", "concurrency", "weight", "keys", "pipeliners"]:
|
|
163
|
+
setattr(options, key, int(getattr(options, key)))
|
|
164
|
+
Bencher(options)
|
|
165
|
+
|
|
166
|
+
if __name__ == "__main__":
|
|
167
|
+
run()
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import subprocess, os, base64
|
|
2
|
+
from cantools import config
|
|
3
|
+
from cantools.util import log, error, read, write, mkdir
|
|
4
|
+
|
|
5
|
+
# jsmin was removed from the cantools requirements to reduce
|
|
6
|
+
# the maintenance burden for deployments on older systems
|
|
7
|
+
# (often with some degree of broken package management) that
|
|
8
|
+
# don't need to compile anything. so we check here instead.
|
|
9
|
+
try:
|
|
10
|
+
from jsmin import jsmin
|
|
11
|
+
BUILDER_READY = True
|
|
12
|
+
except:
|
|
13
|
+
BUILDER_READY = False
|
|
14
|
+
|
|
15
|
+
pcfg = config.build.prod
|
|
16
|
+
pwrap = {
|
|
17
|
+
"standard": "<script>%s</script>",
|
|
18
|
+
"closure": "<script>(function(){%s})();</script>"
|
|
19
|
+
}[pcfg.closure and "closure" or "standard"]
|
|
20
|
+
iwraps = {
|
|
21
|
+
"standard": "%s = %s || {}",
|
|
22
|
+
"closure": 'if (typeof %s == "undefined") %s = {}',
|
|
23
|
+
"closvar": 'if (typeof %s == "undefined") var %s = {}'
|
|
24
|
+
}
|
|
25
|
+
iwrap = iwraps[pcfg.closure and "closure" or "standard"]
|
|
26
|
+
bwrap = "Function(atob(\"%s\"))();"
|
|
27
|
+
|
|
28
|
+
def pwrapper(stxt):
|
|
29
|
+
if pcfg.b64:
|
|
30
|
+
stxt = bwrap%(base64.b64encode(stxt.encode()).decode(),)
|
|
31
|
+
return pwrap%(stxt,)
|
|
32
|
+
|
|
33
|
+
def iwrapper(mod):
|
|
34
|
+
if config.build.prod.closure:
|
|
35
|
+
parts = mod.split(".")
|
|
36
|
+
if len(parts) == 2: # good enough? ....
|
|
37
|
+
return iwraps["closvar"]%(parts[1], parts[1])
|
|
38
|
+
return iwrap%(mod, mod)
|
|
39
|
+
|
|
40
|
+
def nextQuote(text, lastIndex=0):
|
|
41
|
+
z = i = text.find('"', lastIndex)
|
|
42
|
+
while z > 0 and text[z-1] == "\\":
|
|
43
|
+
z -= 1
|
|
44
|
+
return ((i-z)%2 == 0) and i or nextQuote(text, i+1)
|
|
45
|
+
|
|
46
|
+
def encodestrings(text):
|
|
47
|
+
start = nextQuote(text) + 1
|
|
48
|
+
while start != 0:
|
|
49
|
+
end = nextQuote(text, start)
|
|
50
|
+
if end == -1:
|
|
51
|
+
error("parse",
|
|
52
|
+
"unclosed quote: character %s"%(start-1,),
|
|
53
|
+
"this quote: %s"%(text[start:start+config.parse_error_segment_length],),
|
|
54
|
+
text)
|
|
55
|
+
word = ''.join(["\\%s"%(oct(ord(ch))[1:],) for ch in list(text[start:end])])
|
|
56
|
+
if "\\134" not in word: # don't re-escape!
|
|
57
|
+
text = text[:start] + word + text[end:]
|
|
58
|
+
start = nextQuote(text, start + len(word) + 1) + 1
|
|
59
|
+
return text
|
|
60
|
+
|
|
61
|
+
def processhtml(html):
|
|
62
|
+
html = html.replace("{", "{").replace("}", "}").replace("</body>", "%s</body>"%(config.noscript,))
|
|
63
|
+
start = 0
|
|
64
|
+
while not start or html[start + config.js.offset + 1] == "/":
|
|
65
|
+
firststart = start = end = html.find(config.js.flag, start + 1)
|
|
66
|
+
js = []
|
|
67
|
+
while start != -1:
|
|
68
|
+
start += config.js.offset
|
|
69
|
+
end = html.find('"', start)
|
|
70
|
+
if end == -1:
|
|
71
|
+
error("no closing quote in this file: %s"%(html,))
|
|
72
|
+
js.append(html[start:end].strip("/"))
|
|
73
|
+
start = html.find(config.js.flag, end)
|
|
74
|
+
log("js: %s"%(js,), 1)
|
|
75
|
+
if start == end:
|
|
76
|
+
return html, ""
|
|
77
|
+
return html[:firststart] + "{jsspot}" + html[end+config.js.endoffset:], js
|
|
78
|
+
|
|
79
|
+
def compress(html):
|
|
80
|
+
log("compressing html", 1)
|
|
81
|
+
newhtml = html.replace("\n", " ").replace("\t", " ")
|
|
82
|
+
while " " in newhtml:
|
|
83
|
+
newhtml = newhtml.replace(" ", " ")
|
|
84
|
+
newhtml = newhtml.replace("> <", "><")
|
|
85
|
+
log("orig: %s. new: %s"%(len(html), len(newhtml)), 2)
|
|
86
|
+
return newhtml
|
|
87
|
+
|
|
88
|
+
def bfiles(dirname, fnames):
|
|
89
|
+
return [fname for fname in fnames if os.path.isfile(os.path.join(dirname, fname)) and fname != ".svn" and not fname.endswith("~") and not "_old." in fname]
|
|
90
|
+
|
|
91
|
+
def tryinit(iline, inits, prefixes):
|
|
92
|
+
if iline not in inits:
|
|
93
|
+
inits.add(iline)
|
|
94
|
+
prefixes.append(iline)
|
|
95
|
+
|
|
96
|
+
fragz = set()
|
|
97
|
+
def frag(path):
|
|
98
|
+
if '"' in path:
|
|
99
|
+
path = path.split('"')[0]
|
|
100
|
+
for root, dirs, files in os.walk(path, followlinks=True):
|
|
101
|
+
for sub in files:
|
|
102
|
+
if sub.endswith(".js"):
|
|
103
|
+
fp = os.path.join(root, sub)
|
|
104
|
+
log("fragment identified: %s"%(fp,), 1)
|
|
105
|
+
fragz.add(fp)
|
|
106
|
+
else:
|
|
107
|
+
fragz.add(path)
|
|
108
|
+
|
|
109
|
+
def require(line, jspaths, block, inits, admin_ct_path=None):
|
|
110
|
+
dynamic = False
|
|
111
|
+
if line.startswith("CT.scriptImport("):
|
|
112
|
+
rline = line[17:].split(line[16])[0]
|
|
113
|
+
if line[16] not in "'\"" or rline.startswith("http"):
|
|
114
|
+
log("skipping scriptImport: %s"%(line,), important=True)
|
|
115
|
+
return block
|
|
116
|
+
dynamic = True
|
|
117
|
+
else:
|
|
118
|
+
rline = line.split('require(')[1][1:].split(");")[0].strip(")")
|
|
119
|
+
if rline.endswith('"skip"'):
|
|
120
|
+
log("skipping require: %s"%(line,), important=True)
|
|
121
|
+
return block
|
|
122
|
+
elif rline.endswith(", true"):
|
|
123
|
+
dynamic = True
|
|
124
|
+
rline = rline.split(", ")[0]
|
|
125
|
+
rline = rline[:-1]
|
|
126
|
+
rsplit = rline.split(".")
|
|
127
|
+
log("module %s"%(rline,), important=True)
|
|
128
|
+
jspath = os.path.join(config.js.path, *rsplit) + ".js"
|
|
129
|
+
log("path %s"%(jspath,))
|
|
130
|
+
log("dynamic %s"%(dynamic,))
|
|
131
|
+
if jspath not in jspaths:
|
|
132
|
+
if dynamic:
|
|
133
|
+
frag(jspath)
|
|
134
|
+
else:
|
|
135
|
+
prefixes = []
|
|
136
|
+
fullp = "window"
|
|
137
|
+
for rword in rsplit:
|
|
138
|
+
if rword[0].isalpha():
|
|
139
|
+
fullp = ".".join([fullp, rword])
|
|
140
|
+
else:
|
|
141
|
+
fullp = "%s[%s]"%(fullp, rword)
|
|
142
|
+
tryinit(iwrapper(fullp), inits, prefixes)
|
|
143
|
+
pblock = ";".join(prefixes)
|
|
144
|
+
if pblock:
|
|
145
|
+
if config.build.prod.closure:
|
|
146
|
+
pblock = pblock.replace("window.", "")
|
|
147
|
+
jspaths.append(pblock)
|
|
148
|
+
block = block.replace(line, "%s;%s;%s"%(pblock, line.replace("require", "regmod"),
|
|
149
|
+
processjs(jspath, jspaths, inits, admin_ct_path)), 1)
|
|
150
|
+
return block
|
|
151
|
+
|
|
152
|
+
def processjs(path, jspaths=[], inits=set(), admin_ct_path=None):
|
|
153
|
+
log("processing js: %s"%(path,), 1)
|
|
154
|
+
p = path.split("#")[0] # potentially modded to locate file for prod (path remains the same for static)
|
|
155
|
+
if admin_ct_path: # admin pages
|
|
156
|
+
if path.startswith(config.js.path): # such as /js/CT/ct.js
|
|
157
|
+
p = admin_ct_path + path[len(config.js.path):]
|
|
158
|
+
else: # regular admin pages (/memcache/mc.js)
|
|
159
|
+
p = os.path.join(os.path.abspath(os.curdir), "dynamic", path)
|
|
160
|
+
block = read(p)
|
|
161
|
+
for line in [bit for bit in block.split("\n") if not bit.startswith("//")]:
|
|
162
|
+
for flag in ["CT.require(", "CT.scriptImport("]:
|
|
163
|
+
if flag in line and line[line.index(flag) - 1] != "'":
|
|
164
|
+
rline = line.strip().split(flag)[1]
|
|
165
|
+
if rline[0] != "\\" and rline[1] not in ".,": # require() is embedded in text. dot is weird.
|
|
166
|
+
block = require("%s%s"%(flag, rline),
|
|
167
|
+
jspaths, block, inits, admin_ct_path)
|
|
168
|
+
jspaths.append(path)
|
|
169
|
+
return "%s;\n"%(block,)
|
|
170
|
+
|
|
171
|
+
def compilejs(js, admin_ct_path=None):
|
|
172
|
+
jsblock = ""
|
|
173
|
+
jspaths = []
|
|
174
|
+
inits = set([iwrapper("window.CT")]) # already initialized
|
|
175
|
+
for p in js:
|
|
176
|
+
jsblock += processjs(p, jspaths, inits, admin_ct_path)
|
|
177
|
+
return jspaths, jsblock
|
|
178
|
+
|
|
179
|
+
def checkdir(p, recursive=False):
|
|
180
|
+
if not os.path.isdir(p):
|
|
181
|
+
mkdir(p, recursive)
|
|
182
|
+
|
|
183
|
+
def remerge(txt, js):
|
|
184
|
+
return txt.format(jsspot=js).replace("{", "{").replace("}", "}")
|
|
185
|
+
|
|
186
|
+
def build_frags(mode="web", admin_ct_path=None):
|
|
187
|
+
log("Compiling Dynamically-Referenced Fragments", important=True)
|
|
188
|
+
base = config.build[mode].compiled.production
|
|
189
|
+
if config.build.include:
|
|
190
|
+
log("Including Config-Specified Modules")
|
|
191
|
+
for p in config.build.include:
|
|
192
|
+
log("include: %s"%(p,), 1)
|
|
193
|
+
fragz.add(p)
|
|
194
|
+
fragged = set()
|
|
195
|
+
def build_frag(frag):
|
|
196
|
+
notjs = frag in config.build.notjs
|
|
197
|
+
if notjs:
|
|
198
|
+
log("not js: %s"%(frag,))
|
|
199
|
+
block = read(frag, binary=True)
|
|
200
|
+
else:
|
|
201
|
+
block = processjs(frag, admin_ct_path=admin_ct_path)
|
|
202
|
+
path = os.path.join(base, frag[len(config.js.path)+1:])
|
|
203
|
+
checkdir(path.rsplit("/", 1)[0], True)
|
|
204
|
+
if notjs or frag in config.build.exclude:
|
|
205
|
+
log("path excluded -- skipping compression/obfuscation", 2)
|
|
206
|
+
else:
|
|
207
|
+
log("mangling", 2)
|
|
208
|
+
block = jsmin(block)
|
|
209
|
+
write(block, path, binary=notjs)
|
|
210
|
+
while len(fragged) is not len(fragz):
|
|
211
|
+
fcopy = list(filter(lambda f : f not in fragged, fragz))
|
|
212
|
+
fragged.update(fcopy)
|
|
213
|
+
list(map(build_frag, fcopy))
|
|
214
|
+
|
|
215
|
+
def build(admin_ct_path, dirname, fnames):
|
|
216
|
+
"""
|
|
217
|
+
This parses an html file, squishes together the javascript, scans
|
|
218
|
+
through for dynamic imports (CT.require statements), injects modules
|
|
219
|
+
wherever necessary, and sticks the result in a big <script> tag.
|
|
220
|
+
"""
|
|
221
|
+
conf = admin_ct_path and config.build.admin or config.build.web
|
|
222
|
+
todir_stat = dirname.replace(conf.dynamic, conf.compiled.static)
|
|
223
|
+
todir_prod = dirname.replace(conf.dynamic, conf.compiled.production)
|
|
224
|
+
log("Target Static Directory: %s"%(todir_stat,), important=True)
|
|
225
|
+
log("Target Production Directory: %s"%(todir_prod,))
|
|
226
|
+
checkdir(todir_stat)
|
|
227
|
+
checkdir(todir_prod)
|
|
228
|
+
for fname in bfiles(dirname, fnames):
|
|
229
|
+
frompath = os.path.join(dirname, fname)
|
|
230
|
+
topath_stat = os.path.join(todir_stat, fname)
|
|
231
|
+
topath_prod = os.path.join(todir_prod, fname)
|
|
232
|
+
data = read(frompath)
|
|
233
|
+
if "fonts" in dirname or not fname.endswith(".html"):
|
|
234
|
+
log('copying non-html file (%s)'%(fname,), 1)
|
|
235
|
+
else:
|
|
236
|
+
log("processing html: %s"%(frompath,))
|
|
237
|
+
txt, js = processhtml(data)
|
|
238
|
+
if js:
|
|
239
|
+
jspaths, jsblock = compilejs(js, admin_ct_path)
|
|
240
|
+
log('writing static: %s -> %s'%(frompath, topath_stat), important=True)
|
|
241
|
+
write(remerge(txt, '\n'.join([p.split("#")[0].endswith("js") and '<script src="/%s"></script>'%(p,) or '<script>%s</script>'%(p,) for p in jspaths])), topath_stat)
|
|
242
|
+
log('writing production: %s -> %s'%(frompath, topath_prod))
|
|
243
|
+
jsb = jsblock.replace('"_encode": false,', '"_encode": true,').replace("CT.log._silent = false;", "CT.log._silent = true;")
|
|
244
|
+
if config.customscrambler:
|
|
245
|
+
jsb += '; CT.net.setScrambler("%s");'%(config.scrambler,)
|
|
246
|
+
write(remerge(compress(txt), pwrapper(jsmin(jsb))), topath_prod)
|
|
247
|
+
continue
|
|
248
|
+
else:
|
|
249
|
+
log('copying to prod/stat unmodified (%s)'%(fname,), important=True)
|
|
250
|
+
write(data, topath_stat)
|
|
251
|
+
write(data, topath_prod)
|
|
252
|
+
for fname in [f for f in fnames if os.path.isdir(os.path.join(dirname, f))]:
|
|
253
|
+
for dname, dirnames, filenames in os.walk(os.path.join(dirname, fname)):
|
|
254
|
+
build(admin_ct_path, dname, filenames + dirnames)
|
|
255
|
+
|
|
256
|
+
def silence_warnings():
|
|
257
|
+
from ply import yacc, lex
|
|
258
|
+
def quiet(*args, **kwargs):
|
|
259
|
+
pass
|
|
260
|
+
yacc.PlyLogger.warning = quiet
|
|
261
|
+
lex.PlyLogger.warning = quiet
|
|
262
|
+
|
|
263
|
+
def build_all(mode="web", admin_ct_path=None):
|
|
264
|
+
if not BUILDER_READY:
|
|
265
|
+
error("can't build - please install jsmin >= 2.2.2")
|
|
266
|
+
#silence_warnings() # is this unnecessary now.... maybe...
|
|
267
|
+
for dirname, dirnames, filenames in os.walk(config.build[mode].dynamic):
|
|
268
|
+
build(admin_ct_path, dirname, filenames + dirnames)
|
|
269
|
+
build_frags(mode, admin_ct_path)
|
|
270
|
+
|
|
271
|
+
if __name__ == "__main__":
|
|
272
|
+
build_all()
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"""
|
|
2
|
+
### Usage: ctdeploy [-d|s|p] [-un] [--js_path=PATH]
|
|
3
|
+
|
|
4
|
+
### Options:
|
|
5
|
+
|
|
6
|
+
-h, --help show this help message and exit
|
|
7
|
+
-d, --dynamic switch to dynamic (development) mode
|
|
8
|
+
-s, --static switch to static (debug) mode
|
|
9
|
+
-p, --production switch to production (garbled) mode
|
|
10
|
+
-u, --upload uploads project in specified mode and then (if
|
|
11
|
+
gae) switches back to dynamic (development) mode
|
|
12
|
+
-n, --no_build skip compilation step
|
|
13
|
+
-j JS_PATH, --js_path=JS_PATH
|
|
14
|
+
set javascript path (default=js)
|
|
15
|
+
|
|
16
|
+
### Supports 3 modes:
|
|
17
|
+
- dynamic (files live in html)
|
|
18
|
+
- normal development files
|
|
19
|
+
- dynamic imports throughout
|
|
20
|
+
- original files are loaded ad-hoc
|
|
21
|
+
- chrome debugger plays nice
|
|
22
|
+
- no wire encryption
|
|
23
|
+
- all imports lazy
|
|
24
|
+
- static (files live in html-static)
|
|
25
|
+
- compiler builds same html files
|
|
26
|
+
- script imports in head
|
|
27
|
+
- otherwise unmodified source files
|
|
28
|
+
- original files are directly referenced
|
|
29
|
+
- chrome debugger prefers
|
|
30
|
+
- no wire encryption
|
|
31
|
+
- all hard requirements loaded in head
|
|
32
|
+
- lazy-designated imports still lazy
|
|
33
|
+
- production (files live in html-production)
|
|
34
|
+
- all code is compiled in head
|
|
35
|
+
- html is compressed
|
|
36
|
+
- javascript is minified and mangled
|
|
37
|
+
- original code is unrecognizable
|
|
38
|
+
- chrome debugger almost useless
|
|
39
|
+
- wire encryption
|
|
40
|
+
- designated lazy imports (indicated by second bool arg to CT.require)
|
|
41
|
+
|
|
42
|
+
Generates fresh 'static' and 'production' files (from 'development' source files in 'html' on every run, unless -n [or --no_build] flag is used). Mode is established in the app.yaml file, which routes requests to the appropriate directory, and the ct.cfg file, which determines backend behavior, especially regarding encryption.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
import subprocess, os
|
|
46
|
+
from cantools import config, __version__
|
|
47
|
+
from cantools.util import log, error, read, write, cmd
|
|
48
|
+
from cantools.util.package import vpush
|
|
49
|
+
from .builder import build_all
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
input = raw_input # py2/3 compatibility
|
|
53
|
+
except NameError:
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
def doyaml(mode):
|
|
57
|
+
log("switching to %s mode"%(mode,))
|
|
58
|
+
lines = read(config.yaml.path, lines=True)
|
|
59
|
+
f = open(config.yaml.path, 'w')
|
|
60
|
+
m = None
|
|
61
|
+
for line in lines:
|
|
62
|
+
if line.startswith(config.yaml.start):
|
|
63
|
+
m = line[len(config.yaml.start):].strip()
|
|
64
|
+
elif line.startswith(config.yaml.end):
|
|
65
|
+
m = None
|
|
66
|
+
elif m == mode:
|
|
67
|
+
line = line.strip("#")
|
|
68
|
+
elif m:
|
|
69
|
+
line = "#%s"%(line.strip("#"),)
|
|
70
|
+
f.write(line)
|
|
71
|
+
f.close()
|
|
72
|
+
|
|
73
|
+
def setmode(mode):
|
|
74
|
+
doyaml(mode) # support other backends beyond app engine
|
|
75
|
+
ctpy = read(config.py.path)
|
|
76
|
+
isprod = mode == "production"
|
|
77
|
+
# set encode
|
|
78
|
+
ctpy = ctpy.replace(config.py.enc%(str(not isprod),),
|
|
79
|
+
config.py.enc%(str(isprod),))
|
|
80
|
+
# set mode
|
|
81
|
+
for m in ["dynamic", "static", "production"]:
|
|
82
|
+
if m != mode:
|
|
83
|
+
ctpy = ctpy.replace(config.py.mode%(m,), config.py.mode%(mode,))
|
|
84
|
+
write(ctpy, config.py.path)
|
|
85
|
+
|
|
86
|
+
def buildox():
|
|
87
|
+
cmd("ctinit -a")
|
|
88
|
+
cmd("ctdoc -w")
|
|
89
|
+
os.chdir("docs")
|
|
90
|
+
cmd("ctdeploy -p")
|
|
91
|
+
os.chdir("..")
|
|
92
|
+
|
|
93
|
+
def upit():
|
|
94
|
+
if config.web.server == "gae": # switches back to -d mode [ultra deprecated]
|
|
95
|
+
log("uploading files")
|
|
96
|
+
subprocess.call('appcfg.py update . --no_precompilation --noauth_local_webserver', shell=True)
|
|
97
|
+
setmode("dynamic")
|
|
98
|
+
return
|
|
99
|
+
upop = input("upload with git or svn or cancel ([G]it/[s]vn/[c]ancel)? ").lower()[:1]
|
|
100
|
+
if upop == "c":
|
|
101
|
+
return
|
|
102
|
+
if upop == "s":
|
|
103
|
+
cmd('svn commit html-production/ -m"prod"')
|
|
104
|
+
else: # default git
|
|
105
|
+
cmd("git add html-production/")
|
|
106
|
+
cmd('git commit -m "prod"')
|
|
107
|
+
cmd("git push")
|
|
108
|
+
|
|
109
|
+
def run():
|
|
110
|
+
from optparse import OptionParser
|
|
111
|
+
parser = OptionParser("ctdeploy [-d|s|p] [-un] [--js_path=PATH]")
|
|
112
|
+
parser.add_option("-d", "--dynamic", action="store_true", dest="dynamic",
|
|
113
|
+
default=False, help="switch to dynamic (development) mode")
|
|
114
|
+
parser.add_option("-s", "--static", action="store_true", dest="static",
|
|
115
|
+
default=False, help="switch to static (debug) mode")
|
|
116
|
+
parser.add_option("-p", "--production", action="store_true", dest="production",
|
|
117
|
+
default=False, help="switch to production (garbled) mode")
|
|
118
|
+
parser.add_option("-u", "--upload", action="store_true", dest="upload", default=False,
|
|
119
|
+
help="uploads project in specified mode and then (if gae) switches back to dynamic (development) mode")
|
|
120
|
+
parser.add_option("-n", "--no_build", action="store_true",
|
|
121
|
+
dest="no_build", default=False, help="skip compilation step")
|
|
122
|
+
parser.add_option("-j", "--js_path", dest="js_path", default=config.js.path,
|
|
123
|
+
help="set javascript path (default=%s)"%(config.js.path,))
|
|
124
|
+
parser.add_option("-v", "--vpush", action="store_true", dest="vpush",
|
|
125
|
+
default=False, help="push a new version of cantools [ct dev only]")
|
|
126
|
+
options, args = parser.parse_args()
|
|
127
|
+
|
|
128
|
+
if options.vpush:
|
|
129
|
+
vpush("cantools", "ct", __version__, buildox)
|
|
130
|
+
else:
|
|
131
|
+
mode = options.dynamic and "dynamic" or options.static and "static" or options.production and "production"
|
|
132
|
+
if not mode:
|
|
133
|
+
error("no mode specified")
|
|
134
|
+
|
|
135
|
+
# 0) set js path
|
|
136
|
+
if options.js_path != config.js.path:
|
|
137
|
+
log("setting js path to: %s"%(options.js_path,))
|
|
138
|
+
config.js.update("path", options.js_path)
|
|
139
|
+
|
|
140
|
+
# 1) build static/production files
|
|
141
|
+
if not options.no_build:
|
|
142
|
+
build_all()
|
|
143
|
+
|
|
144
|
+
# 2) switch to specified mode
|
|
145
|
+
setmode(mode)
|
|
146
|
+
|
|
147
|
+
# 3) if -u, upload project
|
|
148
|
+
if options.upload:
|
|
149
|
+
upit()
|
|
150
|
+
|
|
151
|
+
log("goodbye")
|
|
152
|
+
|
|
153
|
+
if __name__ == "__main__":
|
|
154
|
+
run()
|