Source code for sondra.utils

import importlib
import inspect
import re
from copy import deepcopy
import hashlib
import random
import time
import sys
from importlib import import_module


[docs]def split_camelcase(name): return re.sub('([A-Z]+)', r' \1', name).title().strip()
[docs]def convert_camelcase(name): s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
[docs]def camelcase_slugify(name): s1 = re.sub('(.)([A-Z][a-z]+)', r'\1-\2', name) return re.sub('([a-z0-9])([A-Z])', r'\1-\2', s1).lower()
[docs]def mapjson(fun, doc): if isinstance(doc, dict): return {k: mapjson(fun, v) for k, v in doc.items()} elif isinstance(doc, list): return [mapjson(fun, v) for v in doc] else: return fun(doc)
[docs]def qiter(o): if o is not None: for x in o: yield x else: raise StopIteration
[docs]def is_exposed(fun): return hasattr(fun, 'exposed')
[docs]def schema_with_properties(original, **updates): new_schema = deepcopy(original) new_schema['properties'].update(updates) return new_schema
[docs]def schema_sans_properties(original, *properties): new_schema = deepcopy(original) for property in (p for p in properties if p in new_schema['properties']): del new_schema['properties'][property] return new_schema
[docs]def schema_with_definitions(original, **updates): new_schema = deepcopy(original) new_schema['definitions'].update(updates) return new_schema
[docs]def schema_sans_definitions(original, *properties): new_schema = deepcopy(original) for property in (p for p in properties if p in new_schema['definitions']): del new_schema['definitions'][property] return new_schema
[docs]def resolve_class(obj, required_superclass=object, required_metaclass=type): if isinstance(obj, str): modulename, classname = obj.rsplit('.', 1) module = importlib.import_module(modulename) klass = getattr(module, classname) else: klass = obj if not issubclass(klass, required_superclass): raise TypeError("{0} is not of type {1}".format( klass.__name__, required_superclass.__name__ )) if not isinstance(klass, required_metaclass): raise TypeError("{0} must use {1} metaclass".format( klass.__name__, required_metaclass.__name__ )) return obj # Use the system PRNG if possible
try: random = random.SystemRandom() using_sysrandom = True except NotImplementedError: import warnings warnings.warn('A secure pseudo-random number generator is not available ' 'on your system. Falling back to Mersenne Twister.') using_sysrandom = False
[docs]def get_random_string(length=12, allowed_chars='abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'): """ Adapted from Django. REMOVED SECRET_KEY FOR NOW Returns a securely generated random string. The default length of 12 with the a-z, A-Z, 0-9 character set returns a 71-bit value. log_2((26+26+10)^12) =~ 71 bits """ if not using_sysrandom: # This is ugly, and a hack, but it makes things better than # the alternative of predictability. This re-seeds the PRNG # using a value that is hard for an attacker to predict, every # time a random string is required. This may change the # properties of the chosen random sequence slightly, but this # is better than absolute predictability. random.seed( hashlib.sha256( ("%s%s" % ( random.getstate(), time.time()).encode('utf-8')) ).digest()) return ''.join(random.choice(allowed_chars) for i in range(length))
[docs]def import_string(dotted_path): """ Adapted from Django. Import a dotted module path and return the attribute/class designated by the last name in the path. Raise ImportError if the import failed. """ try: module_path, class_name = dotted_path.rsplit('.', 1) except ValueError: msg = "%s doesn't look like a module path" % dotted_path raise(ImportError, ImportError(msg), sys.exc_info()[2]) module = import_module(module_path) try: return getattr(module, class_name) except AttributeError: msg = 'Module "%s" does not define a "%s" attribute/class' % ( module_path, class_name) six.reraise(ImportError, ImportError(msg), sys.exc_info()[2])