-#!/usr/bin/python3
+#!/usr/bin/python
from distutils.core import setup, Extension
__all__ = ["wsgiwrap", "restart", "cookie", "formdata"]
-from . import proto
-from .util import wsgiwrap, stringwrap, formparams, funplex, persession, sessiondata, autodirty, manudirty, specdirty
-from .dispatch import restart
-from . import cookie
-from .form import formdata
+import proto
+from util import wsgiwrap, formparams, funplex, persession, sessiondata, autodirty, manudirty, specdirty
+from dispatch import restart
+import cookie
+from form import formdata
import binascii, hashlib, threading, time
-from . import resp
+import resp
class unauthorized(resp.httperror):
def __init__(self, challenge, message=None, detail=None):
- super().__init__(401, message, detail)
+ super(unauthorized, self).__init__(401, message, detail)
if isinstance(challenge, str):
challenge = [challenge]
self.challenge = challenge
def handle(self, req):
for challenge in self.challenge:
req.ohead.add("WWW-Authenticate", challenge)
- return super().handle(req)
+ return super(unauthorized, self).handle(req)
class forbidden(resp.httperror):
def __init__(self, message=None, detail=None):
- super().__init__(403, message, detail)
+ super(forbidden, self).__init__(403, message, detail)
def parsemech(req):
h = req.ihead.get("Authorization", None)
if mech != "basic":
return None, None
try:
- data = data.encode("us-ascii")
- except UnicodeError:
- return None, None
- try:
raw = binascii.a2b_base64(data)
except binascii.Error:
return None, None
- try:
- raw = raw.decode("utf-8")
- except UnicodeError:
- raw = raw.decode("latin1")
p = raw.find(":")
if p < 0:
return None, None
def _obscure(self, nm, pw):
dig = hashlib.sha256()
- dig.update(self.realm.encode("utf-8"))
- dig.update(nm.encode("utf-8"))
- dig.update(pw.encode("utf-8"))
+ dig.update(self.realm)
+ dig.update(nm)
+ dig.update(pw)
return dig.digest()
def check(self, req):
with lock:
try:
ret = self.auth(req, nm, pw)
- except forbidden as exc:
+ except forbidden, exc:
with self._lock:
self._cache[nm, pwh] = (lock, now, "f", exc)
raise
-import http.cookies, time
-from . import proto
+import Cookie, time
+import proto
__all__ = ["cookies", "get", "add"]
class cookiedict(object):
def __init__(self, req):
try:
- self.bk = http.cookies.SimpleCookie(req.ihead.get("Cookie"))
- except http.cookies.CookieError:
- self.bk = http.cookies.SimpleCookie()
- self.codec = http.cookies.SimpleCookie()
+ self.bk = Cookie.SimpleCookie(req.ihead.get("Cookie"))
+ except Cookie.CookieError:
+ self.bk = Cookie.SimpleCookie()
+ self.codec = Cookie.SimpleCookie()
req.oncommit(addcookies)
def __getitem__(self, name):
def add(self, name, value, **kw):
self.codec[name] = value
- for key, value in kw.items():
+ for key, value in kw.iteritems():
self.codec[name][key] = value
def __setitem__(self, name, value):
import sys, traceback
-from . import env, req, proto
+import env, req, proto
__all__ = ["restart"]
return [str(result)]
def defaulterror(req, excinfo):
- from . import resp
+ import resp
traceback.print_exception(*excinfo)
sys.stderr.flush()
raise resp.httperror(500)
try:
resp = handler(req)
break
- except restart as i:
+ except restart, i:
handler = i.handle
- except Exception as i:
+ except Exception, i:
if eh is None:
raise
handler = wraphandler(eh, sys.exc_info())
def exterror(env, startreq):
def handler(req):
- from . import resp
+ import resp
code = 404
if "Response-Code" in req.ihead:
try:
import os
-from . import resp
+import resp
pj = os.path.join
__all__ = ["filehandler"]
elif ext == "html":
ctype = "text/html"
req.ohead["Content-Type"] = ctype
- return open(path, "rb")
+ return open(path, "r")
def resolvefile(self, req, curpath, el):
if os.path.isfile(pj(curpath, el)):
-import urllib.parse
-from . import proto
+import urlparse
+import proto
__all__ = ["formdata"]
def formparse(req):
buf = {}
- buf.update(urllib.parse.parse_qsl(req.query))
+ buf.update(urlparse.parse_qsl(req.query))
if req.ihead.get("Content-Type") == "application/x-www-form-urlencoded":
rbody = req.input.read(2 ** 20)
if len(rbody) >= 2 ** 20:
raise ValueError("x-www-form-urlencoded data is absurdly long")
- buf.update(urllib.parse.parse_qsl(rbody.decode("latin1")))
+ buf.update(urlparse.parse_qsl(rbody))
return buf
class badmultipart(Exception):
class formpart(object):
def __init__(self, form):
self.form = form
- self.buf = b""
+ self.buf = ""
self.eof = False
self.head = {}
def fillbuf(self, sz):
req = self.form.req
- mboundary = b"\r\n--" + self.form.boundary + b"\r\n"
- lboundary = b"\r\n--" + self.form.boundary + b"--\r\n"
+ mboundary = "\r\n--" + self.form.boundary + "\r\n"
+ lboundary = "\r\n--" + self.form.boundary + "--\r\n"
while not self.eof:
p = self.form.buf.find(mboundary)
if p >= 0:
def readline(self, limit=-1):
last = 0
while True:
- p = self.buf.find(b'\n', last)
+ p = self.buf.find('\n', last)
if p < 0:
if self.eof:
ret = self.buf
self.close()
return False
- def parsehead(self, charset):
+ def parsehead(self):
def headline():
ln = self.readline(256)
- if ln[-1] != ord(b'\n'):
+ if ln[-1] != '\n':
raise badmultipart("Too long header line in part")
- try:
- return ln.decode(charset).rstrip()
- except UnicodeError:
- raise badmultipart("Form part header is not in assumed charset")
+ return ln.rstrip()
ln = headline()
while True:
raise badmultipart("Form part uses unexpected transfer encoding: %r" % encoding)
class multipart(object):
- def __init__(self, req, charset):
+ def __init__(self, req):
val, par = proto.pmimehead(req.ihead.get("Content-Type", ""))
if req.method != "POST" or val != "multipart/form-data":
raise badmultipart("Request is not a multipart form")
if "boundary" not in par:
raise badmultipart("Multipart form lacks boundary")
- try:
- self.boundary = par["boundary"].encode("us-ascii")
- except UnicodeError:
- raise badmultipart("Multipart boundary must be ASCII string")
+ self.boundary = par["boundary"]
self.req = req
- self.buf = b"\r\n"
+ self.buf = "\r\n"
self.eof = False
- self.headcs = charset
self.lastpart = formpart(self)
self.lastpart.close()
def __iter__(self):
return self
- def __next__(self):
+ def next(self):
if not self.lastpart.eof:
raise RuntimeError("All form parts must be read entirely")
if self.eof:
raise StopIteration()
self.lastpart = formpart(self)
- self.lastpart.parsehead(self.headcs)
+ self.lastpart.parsehead()
return self.lastpart
def formdata(req):
</body>
</html>
""" % (title, title, htmlq(msg))
- buf = buf.encode("us-ascii")
startreq("%i %s" % (code, title), [("Content-Type", "text/html"), ("Content-Length", str(len(buf)))])
return [buf]
def urlq(url):
- if isinstance(url, str):
+ if isinstance(url, unicode):
url = url.encode("utf-8")
ret = ""
- invalid = b"&=#?/\"'"
+ invalid = "&=#?/\"'"
for c in url:
- if c in invalid or (c <= 32) or (c >= 128):
- ret += "%%%02X" % c
+ if c in invalid or (ord(c) <= 32) or (ord(c) >= 128):
+ ret += "%%%02X" % ord(c)
else:
- ret += chr(c)
+ ret += c
return ret
class urlerror(ValueError):
del self.dict[key.lower()]
def __iter__(self):
- return iter((list[0] for list in self.dict.values()))
+ return iter((list[0] for list in self.dict.itervalues()))
def get(self, key, default=""):
if key.lower() in self.dict:
ra = min(ra, size)
while len(self.buf) < ra:
ret = self.bk.read(ra - len(self.buf))
- if ret == b"":
+ if ret == "":
raise IOError("Unexpected EOF")
self.buf.extend(ret)
self.rb += len(ret)
- ret = bytes(self.buf[:ra])
+ ret = str(self.buf[:ra])
self.buf = self.buf[ra:]
return ret
def readline(self, size=-1):
off = 0
while True:
- p = self.buf.find(b'\n', off)
+ p = self.buf.find('\n', off)
if p >= 0:
- ret = bytes(self.buf[:p + 1])
+ ret = str(self.buf[:p + 1])
self.buf = self.buf[p + 1:]
return ret
off = len(self.buf)
if size >= 0 and len(self.buf) >= size:
- ret = bytes(self.buf[:size])
+ ret = str(self.buf[:size])
self.buf = self.buf[size:]
return ret
if self.rb == self.limit:
- ret = bytes(self.buf)
+ ret = str(self.buf)
self.buf = bytearray()
return ret
ra = self.limit - self.rb
ra = min(ra, size)
ra = min(ra, 1024)
ret = self.bk.read(ra)
- if ret == b"":
+ if ret == "":
raise IOError("Unpexpected EOF")
self.buf.extend(ret)
self.rb += len(ret)
class lineiter(object):
def __iter__(self):
return self
- def __next__(self):
+ def next(self):
ret = rd.readline()
- if ret == b"":
+ if ret == "":
raise StopIteration()
return ret
return lineiter()
self.input = limitreader(env["wsgi.input"], int(clen))
else:
# XXX: What to do?
- self.input = io.BytesIO(b"")
+ self.input = io.BytesIO("")
else:
# Assume input is chunked and read until ordinary EOF.
self.input = env["wsgi.input"]
-from . import dispatch, proto, env
-from .sp import xhtml
+import dispatch, proto, env
+from sp import xhtml
h = xhtml.cons()
__all__ = ["skeleton", "skelfor", "setskel", "usererror"]
class usererror(dispatch.restart):
def __init__(self, message, *detail):
- super().__init__()
+ super(usererror, self).__init__()
self.message = message
self.detail = detail
return skelfor(req).error(req, self.message, *self.detail)
class message(dispatch.restart):
- def __init__(self, message, *detail):
- super().__init__()
- self.message = message
+ def __init__(self, msg, *detail):
+ super(message, self).__init__()
+ self.message = msg
self.detail = detail
def handle(self, req):
message = proto.statusinfo[status][0]
if detail is None:
detail = (proto.statusinfo[status][1],)
- super().__init__(message, *detail)
+ super(httperror, self).__init__(message, *detail)
self.status = status
def handle(self, req):
req.status(self.status, self.message)
- return super().handle(req)
+ return super(httperror, self).handle(req)
class notfound(httperror):
def __init__(self):
- return super().__init__(404)
+ return super(notfound, self).__init__(404)
class redirect(dispatch.restart):
bases = {"url": proto.requrl,
"site": proto.siteurl}
def __init__(self, url, status=303, base="url"):
- super().__init__()
+ super(redirect, self).__init__()
self.url = url
self.status = status
self.bases[base]
-import threading, time, pickle, random, os, binascii
-from . import cookie, env
+import threading, time, pickle, random, os
+import cookie, env
__all__ = ["db", "get"]
class session(object):
def __init__(self, lock, expire=86400 * 7):
- self.id = binascii.b2a_hex(gennonce(16)).decode("us-ascii")
+ self.id = gennonce(16).encode("hex")
self.dict = {}
self.lock = lock
self.ctime = self.atime = self.mtime = int(time.time())
def clean(self):
now = int(time.time())
with self.lock:
- clist = list(self.live.keys())
+ clist = self.live.keys()
for sessid in clist:
with self.lock:
try:
data = self.backdb[sessid]
try:
return pickle.loads(data)
- except:
+ except Exception, e:
raise KeyError()
def freeze(self, sess):
class node(object):
pass
-class text(node, str):
+class text(node, unicode):
def __todom__(self, doc):
return doc.createTextNode(self)
-class raw(node, str):
+class raw(node, unicode):
def __todom__(self, doc):
raise Exception("Cannot convert raw code to DOM objects")
class element(node):
def __init__(self, ns, name, ctx):
self.ns = ns
- self.name = str(name)
+ self.name = unicode(name)
self.ctx = ctx
self.attrs = {}
self.children = []
def __call__(self, *children, **attrs):
for child in children:
self.ctx.addchild(self, child)
- for k, v in attrs.items():
+ for k, v in attrs.iteritems():
self.ctx.addattr(self, k, v)
return self
def __todom__(self, doc):
el = doc.createElementNS(self.ns, self.name)
- for k, v in self.attrs.items():
+ for k, v in self.attrs.iteritems():
el.setAttribute(k, v)
for child in self.children:
el.appendChild(child.__todom__(doc))
def __init__(self):
self.nodeconv = {}
- self.nodeconv[bytes] = lambda ob: text(ob, self.charset)
- self.nodeconv[str] = text
+ self.nodeconv[str] = lambda ob: text(ob, self.charset)
+ self.nodeconv[unicode] = text
self.nodeconv[int] = text
+ self.nodeconv[long] = text
self.nodeconv[float] = text
def nodefrom(self, ob):
def addattr(self, node, k, v):
if v is not None:
- node.attrs[str(k)] = str(v)
+ node.attrs[unicode(k)] = unicode(v)
class constructor(object):
def __init__(self, ns, elcls=element, ctx=None):
-import itertools, io
-from .. import dispatch
-from . import cons
+import itertools, StringIO
+from wrw import dispatch
+import cons
def findnsnames(el):
names = {}
def proc(el):
if isinstance(el, cons.element):
if el.ns not in names:
- names[el.ns] = "n" + str(nid[0])
+ names[el.ns] = u"n" + unicode(nid[0])
nid[:] = [nid[0] + 1]
for ch in el.children:
proc(ch)
self.buf.extend(text.encode(self.charset))
def quotewrite(self, buf):
- buf = buf.replace('&', "&")
- buf = buf.replace('<', "<")
- buf = buf.replace('>', ">")
+ buf = buf.replace(u'&', u"&")
+ buf = buf.replace(u'<', u"<")
+ buf = buf.replace(u'>', u">")
self.write(buf)
def __iter__(self):
if ns is None:
return el.name
else:
- return ns + ":" + el.name
+ return ns + u":" + el.name
def attrval(self, v):
- qc, qt = ("'", "'") if '"' in v else ('"', """)
+ qc, qt = (u"'", u"'") if u'"' in v else (u'"', u""")
self.write(qc)
- v = v.replace('&', "&")
- v = v.replace('<', "<")
- v = v.replace('>', ">")
+ v = v.replace(u'&', u"&")
+ v = v.replace(u'<', u"<")
+ v = v.replace(u'>', u">")
v = v.replace(qc, qt)
self.write(v)
self.write(qc)
def attr(self, k, v):
self.write(k)
- self.write("=")
+ self.write(u"=")
self.attrval(v)
def attrs(self, attrs):
for k, v in attrs:
- self.write(" ")
+ self.write(u" ")
self.attr(k, v)
def inittag(self, el):
- self.write("<" + self.elname(el))
- attrs = el.attrs.items()
+ self.write(u"<" + self.elname(el))
+ attrs = el.attrs.iteritems()
if self.first:
nsnames = []
- for ns, name in self.nsnames.items():
+ for ns, name in self.nsnames.iteritems():
if ns is None:
if name is not None:
raise Exception("null namespace must have null name, not" + name)
continue
- nsnames.append(("xmlns" if name is None else ("xmlns:" + name), ns))
+ nsnames.append((u"xmlns" if name is None else (u"xmlns:" + name), ns))
attrs = itertools.chain(attrs, iter(nsnames))
self.first = False
self.attrs(attrs)
def starttag(self, el):
self.inittag(el)
- self.write(">")
+ self.write(u">")
def shorttag(self, el):
self.inittag(el)
- self.write(" />")
+ self.write(u" />")
def endtag(self, el):
- self.write("</" + self.elname(el) + ">")
+ self.write(u"</" + self.elname(el) + u">")
def text(self, el):
self.quotewrite(el)
self.write(el)
def start(self, el):
- self.write('<?xml version="1.0" encoding="' + self.charset + '" ?>\n')
+ self.write(u'<?xml version="1.0" encoding="' + self.charset + u'" ?>\n')
if isinstance(el, cons.doctype):
- self.write('<!DOCTYPE %s PUBLIC "%s" "%s">\n' % (el.rootname,
+ self.write(u'<!DOCTYPE %s PUBLIC "%s" "%s">\n' % (el.rootname,
el.pubid,
el.dtdid))
self.first = True
elif ev == "$":
self.end(el)
- def __next__(self):
+ def next(self):
if self.src is None:
raise StopIteration()
try:
self.src = None
ev, el = "$", None
self.handle(ev, el)
- ret = bytes(self.buf)
- self.buf[:] = b""
+ ret = str(self.buf)
+ self.buf[:] = ""
return ret
def nsname(self, el):
return ret
if el.ns is None:
return None
- ret = "n" + str(self.nextns)
+ ret = u"n" + unicode(self.nextns)
self.nextns += 1
return ret
@classmethod
def format(cls, root, **kw):
- buf = io.BytesIO()
+ buf = StringIO.StringIO()
cls.output(buf, root, **kw)
return buf.getvalue()
class indenter(formatter):
- def __init__(self, indent=" ", *args, **kw):
- super().__init__(*args, **kw)
+ def __init__(self, indent=u" ", *args, **kw):
+ super(indenter, self).__init__(*args, **kw)
self.indent = indent
self.col = 0
- self.curind = ""
+ self.curind = u""
self.atbreak = True
self.inline = False
self.stack = []
self.lastendbr = True
def write(self, text):
- lines = text.split("\n")
+ lines = text.split(u"\n")
if len(lines) > 1:
for ln in lines[:-1]:
self.buf.extend(ln.encode(self.charset))
- self.buf.extend(b"\n")
+ self.buf.extend("\n")
self.col = 0
self.buf.extend(lines[-1].encode(self.charset))
self.col += len(lines[-1])
def br(self):
if not self.atbreak:
- self.buf.extend(("\n" + self.curind).encode(self.charset))
+ self.buf.extend((u"\n" + self.curind).encode(self.charset))
self.col = 0
self.atbreak = True
self.push(el)
self.inline = self.inline or self.inlinep(el)
self.curind += self.indent
- super().starttag(el)
+ super(indenter, self).starttag(el)
def shorttag(self, el):
if not self.inline:
self.br()
- super().shorttag(el)
+ super(indenter, self).shorttag(el)
def endtag(self, el):
il = self.inline
else:
self.br()
self.lastendbr = True
- super().endtag(el)
+ super(indenter, self).endtag(el)
def start(self, el):
- super().start(el)
+ super(indenter, self).start(el)
self.atbreak = True
def end(self, el):
self.br()
def handle(self, ev, el):
- super().handle(ev, el)
+ super(indenter, self).handle(ev, el)
self.last = ev, el
class textindenter(indenter):
maxcol = 70
def text(self, el):
- left = str(el)
+ left = unicode(el)
while True:
if len(left) + self.col > self.maxcol:
bp = max(self.maxcol - self.col, 0)
- for i in range(bp, -1, -1):
+ for i in xrange(bp, -1, -1):
if left[i].isspace():
while i > 0 and left[i - 1].isspace(): i -= 1
break
else:
- for i in range(bp + 1, len(left)):
+ for i in xrange(bp + 1, len(left)):
if left[i].isspace():
break
else:
formatter = indenter
def __init__(self, root):
- super().__init__()
+ super(response, self).__init__()
self.root = root
@property
-import xml.dom.minidom, io
-from . import cons as _cons
-from . import util
+import xml.dom.minidom, StringIO
+import cons as _cons
+import util
dom = xml.dom.minidom.getDOMImplementation()
-ns = "http://www.w3.org/1999/xhtml"
-doctype = "-//W3C//DTD XHTML 1.1//EN"
-dtd = "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"
+ns = u"http://www.w3.org/1999/xhtml"
+doctype = u"-//W3C//DTD XHTML 1.1//EN"
+dtd = u"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"
class htmlelement(_cons.element):
def __todoc__(self):
return doc
class xhtmlcontext(_cons.context):
- attrmap = {"klass": "class"}
+ attrmap = {u"klass": u"class"}
def addattr(self, node, k, v):
- k = str(k)
- super().addattr(node, self.attrmap.get(k, k), v)
+ k = unicode(k)
+ super(xhtmlcontext, self).addattr(node, self.attrmap.get(k, k), v)
def cons(ctx=None):
if ctx is None: ctx = xhtmlcontext()
head = h.head
if title:
head(h.title(title))
- if isinstance(css, str) or isinstance(css, bytes):
+ if isinstance(css, str) or isinstance(css, unicode):
head(h.link(rel="stylesheet", type="text/css", href=css))
elif css:
for ss in css:
return head
class htmlformatter(util.formatter):
- allowshort = {"br", "hr", "img", "input", "meta", "link"}
+ allowshort = set([u"br", u"hr", u"img", u"input", u"meta", u"link"])
def shorttag(self, el):
if el.name in self.allowshort:
- super().shorttag(el)
+ super(htmlformatter, self).shorttag(el)
else:
self.handle(">", el)
self.handle("<", el)
def forreq(req, tree):
# XXX: Use proper Content-Type for clients accepting it.
req.ohead["Content-Type"] = "text/html; charset=utf-8"
- buf = io.BytesIO()
+ buf = StringIO.StringIO()
htmlindenter.output(buf, tree, doctype=(doctype, dtd), charset="utf-8")
ret = buf.getvalue()
req.ohead["Content-Length"] = len(ret)
import inspect, math
-from . import req, dispatch, session, form, resp, proto
+import req, dispatch, session, form, resp, proto
def wsgiwrap(callable):
def wrapper(env, startreq):
for arg in list(args):
if arg not in spec.args:
del args[arg]
- for i in range(len(spec.args) - (len(spec.defaults) if spec.defaults else 0)):
+ for i in xrange(len(spec.args) - (len(spec.defaults) if spec.defaults else 0)):
if spec.args[i] not in args:
raise resp.httperror(400, "Missing parameter", ("The query parameter `", resp.h.code(spec.args[i]), "' is required but not supplied."))
return callable(**args)
self.bk = real
self.bki = iter(real)
self._next = None
- self.__next__()
+ self.next()
def __iter__(self):
return self
- def __next__(self):
+ def next(self):
if self._next is self.end:
raise StopIteration()
ret = self._next
wrapper.__wrapped__ = callable
return wrapper
-def stringwrap(charset):
- def dec(callable):
- @pregen
- def wrapper(*args, **kwargs):
- for string in callable(*args, **kwargs):
- yield string.encode(charset)
- wrapper.__wrapped__ = callable
- return wrapper
- return dec
-
class sessiondata(object):
@classmethod
def get(cls, req, create=True):
class autodirty(sessiondata):
@classmethod
def get(cls, req):
- ret = super().get(req)
+ ret = super(autodirty, cls).get(req)
if "_is_dirty" not in ret.__dict__:
ret.__dict__["_is_dirty"] = False
return ret
return self._is_dirty
def __setattr__(self, name, value):
- super().__setattr__(name, value)
+ super(autodirty, self).__setattr__(name, value)
if "_is_dirty" in self.__dict__:
self.__dict__["_is_dirty"] = True
def __delattr__(self, name):
- super().__delattr__(name, value)
+ super(autodirty, self).__delattr__(name, value)
if "_is_dirty" in self.__dict__:
self.__dict__["_is_dirty"] = True
class manudirty(object):
def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
+ super(manudirty, self).__init__(*args, **kwargs)
self.__dirty = False
def sessfrozen(self):
class specclass(type):
def __init__(self, name, bases, tdict):
- super().__init__(name, bases, tdict)
+ super(specclass, self).__init__(name, bases, tdict)
sslots = set()
dslots = set()
for cls in self.__mro__:
for i, slot in enumerate(self.__sslots_a__):
setattr(self, slot, specslot(slot, i, slot in dslots))
-class specdirty(sessiondata, metaclass=specclass):
+class specdirty(sessiondata):
+ __metaclass__ = specclass
__slots__ = ["session", "__sslots__", "_is_dirty"]
def __specinit__(self):
@staticmethod
def __new__(cls, req, sess):
- self = super().__new__(cls)
+ self = super(specdirty, cls).__new__(cls)
self.session = sess
self.__sslots__ = [specslot.unbound] * len(cls.__sslots_a__)
self.__specinit__()
import os, threading
from mako import template, lookup, filters
-from . import util, form, session, env, resp
+import util, form, session, env, resp
# It seems Mako isn't thread-safe.
makolock = threading.Lock()