160 lines
4.8 KiB
Python
160 lines
4.8 KiB
Python
|
from __future__ import absolute_import, division, unicode_literals
|
||
|
|
||
|
from types import ModuleType
|
||
|
|
||
|
try:
|
||
|
from collections.abc import Mapping
|
||
|
except ImportError:
|
||
|
from collections import Mapping
|
||
|
|
||
|
from six import text_type, PY3
|
||
|
|
||
|
if PY3:
|
||
|
import xml.etree.ElementTree as default_etree
|
||
|
else:
|
||
|
try:
|
||
|
import xml.etree.cElementTree as default_etree
|
||
|
except ImportError:
|
||
|
import xml.etree.ElementTree as default_etree
|
||
|
|
||
|
|
||
|
__all__ = ["default_etree", "MethodDispatcher", "isSurrogatePair",
|
||
|
"surrogatePairToCodepoint", "moduleFactoryFactory",
|
||
|
"supports_lone_surrogates"]
|
||
|
|
||
|
|
||
|
# Platforms not supporting lone surrogates (\uD800-\uDFFF) should be
|
||
|
# caught by the below test. In general this would be any platform
|
||
|
# using UTF-16 as its encoding of unicode strings, such as
|
||
|
# Jython. This is because UTF-16 itself is based on the use of such
|
||
|
# surrogates, and there is no mechanism to further escape such
|
||
|
# escapes.
|
||
|
try:
|
||
|
_x = eval('"\\uD800"') # pylint:disable=eval-used
|
||
|
if not isinstance(_x, text_type):
|
||
|
# We need this with u"" because of http://bugs.jython.org/issue2039
|
||
|
_x = eval('u"\\uD800"') # pylint:disable=eval-used
|
||
|
assert isinstance(_x, text_type)
|
||
|
except Exception:
|
||
|
supports_lone_surrogates = False
|
||
|
else:
|
||
|
supports_lone_surrogates = True
|
||
|
|
||
|
|
||
|
class MethodDispatcher(dict):
|
||
|
"""Dict with 2 special properties:
|
||
|
|
||
|
On initiation, keys that are lists, sets or tuples are converted to
|
||
|
multiple keys so accessing any one of the items in the original
|
||
|
list-like object returns the matching value
|
||
|
|
||
|
md = MethodDispatcher({("foo", "bar"):"baz"})
|
||
|
md["foo"] == "baz"
|
||
|
|
||
|
A default value which can be set through the default attribute.
|
||
|
"""
|
||
|
|
||
|
def __init__(self, items=()):
|
||
|
_dictEntries = []
|
||
|
for name, value in items:
|
||
|
if isinstance(name, (list, tuple, frozenset, set)):
|
||
|
for item in name:
|
||
|
_dictEntries.append((item, value))
|
||
|
else:
|
||
|
_dictEntries.append((name, value))
|
||
|
dict.__init__(self, _dictEntries)
|
||
|
assert len(self) == len(_dictEntries)
|
||
|
self.default = None
|
||
|
|
||
|
def __getitem__(self, key):
|
||
|
return dict.get(self, key, self.default)
|
||
|
|
||
|
def __get__(self, instance, owner=None):
|
||
|
return BoundMethodDispatcher(instance, self)
|
||
|
|
||
|
|
||
|
class BoundMethodDispatcher(Mapping):
|
||
|
"""Wraps a MethodDispatcher, binding its return values to `instance`"""
|
||
|
def __init__(self, instance, dispatcher):
|
||
|
self.instance = instance
|
||
|
self.dispatcher = dispatcher
|
||
|
|
||
|
def __getitem__(self, key):
|
||
|
# see https://docs.python.org/3/reference/datamodel.html#object.__get__
|
||
|
# on a function, __get__ is used to bind a function to an instance as a bound method
|
||
|
return self.dispatcher[key].__get__(self.instance)
|
||
|
|
||
|
def get(self, key, default):
|
||
|
if key in self.dispatcher:
|
||
|
return self[key]
|
||
|
else:
|
||
|
return default
|
||
|
|
||
|
def __iter__(self):
|
||
|
return iter(self.dispatcher)
|
||
|
|
||
|
def __len__(self):
|
||
|
return len(self.dispatcher)
|
||
|
|
||
|
def __contains__(self, key):
|
||
|
return key in self.dispatcher
|
||
|
|
||
|
|
||
|
# Some utility functions to deal with weirdness around UCS2 vs UCS4
|
||
|
# python builds
|
||
|
|
||
|
def isSurrogatePair(data):
|
||
|
return (len(data) == 2 and
|
||
|
ord(data[0]) >= 0xD800 and ord(data[0]) <= 0xDBFF and
|
||
|
ord(data[1]) >= 0xDC00 and ord(data[1]) <= 0xDFFF)
|
||
|
|
||
|
|
||
|
def surrogatePairToCodepoint(data):
|
||
|
char_val = (0x10000 + (ord(data[0]) - 0xD800) * 0x400 +
|
||
|
(ord(data[1]) - 0xDC00))
|
||
|
return char_val
|
||
|
|
||
|
# Module Factory Factory (no, this isn't Java, I know)
|
||
|
# Here to stop this being duplicated all over the place.
|
||
|
|
||
|
|
||
|
def moduleFactoryFactory(factory):
|
||
|
moduleCache = {}
|
||
|
|
||
|
def moduleFactory(baseModule, *args, **kwargs):
|
||
|
if isinstance(ModuleType.__name__, type("")):
|
||
|
name = "_%s_factory" % baseModule.__name__
|
||
|
else:
|
||
|
name = b"_%s_factory" % baseModule.__name__
|
||
|
|
||
|
kwargs_tuple = tuple(kwargs.items())
|
||
|
|
||
|
try:
|
||
|
return moduleCache[name][args][kwargs_tuple]
|
||
|
except KeyError:
|
||
|
mod = ModuleType(name)
|
||
|
objs = factory(baseModule, *args, **kwargs)
|
||
|
mod.__dict__.update(objs)
|
||
|
if "name" not in moduleCache:
|
||
|
moduleCache[name] = {}
|
||
|
if "args" not in moduleCache[name]:
|
||
|
moduleCache[name][args] = {}
|
||
|
if "kwargs" not in moduleCache[name][args]:
|
||
|
moduleCache[name][args][kwargs_tuple] = {}
|
||
|
moduleCache[name][args][kwargs_tuple] = mod
|
||
|
return mod
|
||
|
|
||
|
return moduleFactory
|
||
|
|
||
|
|
||
|
def memoize(func):
|
||
|
cache = {}
|
||
|
|
||
|
def wrapped(*args, **kwargs):
|
||
|
key = (tuple(args), tuple(kwargs.items()))
|
||
|
if key not in cache:
|
||
|
cache[key] = func(*args, **kwargs)
|
||
|
return cache[key]
|
||
|
|
||
|
return wrapped
|