Viewing file: util.py (10.76 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
# mako/util.py # Copyright 2006-2019 the Mako authors and contributors <see AUTHORS file> # # This module is part of Mako and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php
import codecs import collections import operator import os import re import timeit
from mako import compat
def update_wrapper(decorated, fn): decorated.__wrapped__ = fn decorated.__name__ = fn.__name__ return decorated
class PluginLoader(object): def __init__(self, group): self.group = group self.impls = {}
def load(self, name): if name in self.impls: return self.impls[name]() else: import pkg_resources
for impl in pkg_resources.iter_entry_points(self.group, name): self.impls[name] = impl.load return impl.load() else: from mako import exceptions
raise exceptions.RuntimeException( "Can't load plugin %s %s" % (self.group, name) )
def register(self, name, modulepath, objname): def load(): mod = __import__(modulepath) for token in modulepath.split(".")[1:]: mod = getattr(mod, token) return getattr(mod, objname)
self.impls[name] = load
def verify_directory(dir_): """create and/or verify a filesystem directory."""
tries = 0
while not os.path.exists(dir_): try: tries += 1 os.makedirs(dir_, compat.octal("0775")) except: if tries > 5: raise
def to_list(x, default=None): if x is None: return default if not isinstance(x, (list, tuple)): return [x] else: return x
class memoized_property(object):
"""A read-only @property that is only evaluated once."""
def __init__(self, fget, doc=None): self.fget = fget self.__doc__ = doc or fget.__doc__ self.__name__ = fget.__name__
def __get__(self, obj, cls): if obj is None: return self obj.__dict__[self.__name__] = result = self.fget(obj) return result
class memoized_instancemethod(object):
"""Decorate a method memoize its return value.
Best applied to no-arg methods: memoization is not sensitive to argument values, and will always return the same value even when called with different arguments.
"""
def __init__(self, fget, doc=None): self.fget = fget self.__doc__ = doc or fget.__doc__ self.__name__ = fget.__name__
def __get__(self, obj, cls): if obj is None: return self
def oneshot(*args, **kw): result = self.fget(obj, *args, **kw)
def memo(*a, **kw): return result
memo.__name__ = self.__name__ memo.__doc__ = self.__doc__ obj.__dict__[self.__name__] = memo return result
oneshot.__name__ = self.__name__ oneshot.__doc__ = self.__doc__ return oneshot
class SetLikeDict(dict):
"""a dictionary that has some setlike methods on it"""
def union(self, other): """produce a 'union' of this dict and another (at the key level).
values in the second dict take precedence over that of the first""" x = SetLikeDict(**self) x.update(other) return x
class FastEncodingBuffer(object):
"""a very rudimentary buffer that is faster than StringIO, but doesn't crash on unicode data like cStringIO."""
def __init__(self, encoding=None, errors="strict", as_unicode=False): self.data = collections.deque() self.encoding = encoding if as_unicode: self.delim = compat.u("") else: self.delim = "" self.as_unicode = as_unicode self.errors = errors self.write = self.data.append
def truncate(self): self.data = collections.deque() self.write = self.data.append
def getvalue(self): if self.encoding: return self.delim.join(self.data).encode( self.encoding, self.errors ) else: return self.delim.join(self.data)
class LRUCache(dict):
"""A dictionary-like object that stores a limited number of items, discarding lesser used items periodically.
this is a rewrite of LRUCache from Myghty to use a periodic timestamp-based paradigm so that synchronization is not really needed. the size management is inexact. """
class _Item(object): def __init__(self, key, value): self.key = key self.value = value self.timestamp = timeit.default_timer()
def __repr__(self): return repr(self.value)
def __init__(self, capacity, threshold=0.5): self.capacity = capacity self.threshold = threshold
def __getitem__(self, key): item = dict.__getitem__(self, key) item.timestamp = timeit.default_timer() return item.value
def values(self): return [i.value for i in dict.values(self)]
def setdefault(self, key, value): if key in self: return self[key] else: self[key] = value return value
def __setitem__(self, key, value): item = dict.get(self, key) if item is None: item = self._Item(key, value) dict.__setitem__(self, key, item) else: item.value = value self._manage_size()
def _manage_size(self): while len(self) > self.capacity + self.capacity * self.threshold: bytime = sorted( dict.values(self), key=operator.attrgetter("timestamp"), reverse=True, ) for item in bytime[self.capacity :]: try: del self[item.key] except KeyError: # if we couldn't find a key, most likely some other thread # broke in on us. loop around and try again break
# Regexp to match python magic encoding line _PYTHON_MAGIC_COMMENT_re = re.compile( r"[ \t\f]* \# .* coding[=:][ \t]*([-\w.]+)", re.VERBOSE )
def parse_encoding(fp): """Deduce the encoding of a Python source file (binary mode) from magic comment.
It does this in the same way as the `Python interpreter`__
.. __: http://docs.python.org/ref/encodings.html
The ``fp`` argument should be a seekable file object in binary mode. """ pos = fp.tell() fp.seek(0) try: line1 = fp.readline() has_bom = line1.startswith(codecs.BOM_UTF8) if has_bom: line1 = line1[len(codecs.BOM_UTF8) :]
m = _PYTHON_MAGIC_COMMENT_re.match(line1.decode("ascii", "ignore")) if not m: try: import parser
parser.suite(line1.decode("ascii", "ignore")) except (ImportError, SyntaxError): # Either it's a real syntax error, in which case the source # is not valid python source, or line2 is a continuation of # line1, in which case we don't want to scan line2 for a magic # comment. pass else: line2 = fp.readline() m = _PYTHON_MAGIC_COMMENT_re.match( line2.decode("ascii", "ignore") )
if has_bom: if m: raise SyntaxError( "python refuses to compile code with both a UTF8" " byte-order-mark and a magic encoding comment" ) return "utf_8" elif m: return m.group(1) else: return None finally: fp.seek(pos)
def sorted_dict_repr(d): """repr() a dictionary with the keys in order.
Used by the lexer unit test to compare parse trees based on strings.
""" keys = list(d.keys()) keys.sort() return "{" + ", ".join(["%r: %r" % (k, d[k]) for k in keys]) + "}"
def restore__ast(_ast): """Attempt to restore the required classes to the _ast module if it appears to be missing them """ if hasattr(_ast, "AST"): return _ast.PyCF_ONLY_AST = 2 << 9 m = compile( """\ def foo(): pass class Bar(object): pass if False: pass baz = 'mako' 1 + 2 - 3 * 4 / 5 6 // 7 % 8 << 9 >> 10 11 & 12 ^ 13 | 14 15 and 16 or 17 -baz + (not +18) - ~17 baz and 'foo' or 'bar' (mako is baz == baz) is not baz != mako mako > baz < mako >= baz <= mako mako in baz not in mako""", "<unknown>", "exec", _ast.PyCF_ONLY_AST, ) _ast.Module = type(m)
for cls in _ast.Module.__mro__: if cls.__name__ == "mod": _ast.mod = cls elif cls.__name__ == "AST": _ast.AST = cls
_ast.FunctionDef = type(m.body[0]) _ast.ClassDef = type(m.body[1]) _ast.If = type(m.body[2])
_ast.Name = type(m.body[3].targets[0]) _ast.Store = type(m.body[3].targets[0].ctx) _ast.Str = type(m.body[3].value)
_ast.Sub = type(m.body[4].value.op) _ast.Add = type(m.body[4].value.left.op) _ast.Div = type(m.body[4].value.right.op) _ast.Mult = type(m.body[4].value.right.left.op)
_ast.RShift = type(m.body[5].value.op) _ast.LShift = type(m.body[5].value.left.op) _ast.Mod = type(m.body[5].value.left.left.op) _ast.FloorDiv = type(m.body[5].value.left.left.left.op)
_ast.BitOr = type(m.body[6].value.op) _ast.BitXor = type(m.body[6].value.left.op) _ast.BitAnd = type(m.body[6].value.left.left.op)
_ast.Or = type(m.body[7].value.op) _ast.And = type(m.body[7].value.values[0].op)
_ast.Invert = type(m.body[8].value.right.op) _ast.Not = type(m.body[8].value.left.right.op) _ast.UAdd = type(m.body[8].value.left.right.operand.op) _ast.USub = type(m.body[8].value.left.left.op)
_ast.Or = type(m.body[9].value.op) _ast.And = type(m.body[9].value.values[0].op)
_ast.IsNot = type(m.body[10].value.ops[0]) _ast.NotEq = type(m.body[10].value.ops[1]) _ast.Is = type(m.body[10].value.left.ops[0]) _ast.Eq = type(m.body[10].value.left.ops[1])
_ast.Gt = type(m.body[11].value.ops[0]) _ast.Lt = type(m.body[11].value.ops[1]) _ast.GtE = type(m.body[11].value.ops[2]) _ast.LtE = type(m.body[11].value.ops[3])
_ast.In = type(m.body[12].value.ops[0]) _ast.NotIn = type(m.body[12].value.ops[1])
def read_file(path, mode="rb"): fp = open(path, mode) try: data = fp.read() return data finally: fp.close()
def read_python_file(path): fp = open(path, "rb") try: encoding = parse_encoding(fp) data = fp.read() if encoding: data = data.decode(encoding) return data finally: fp.close()
|