91 lines
2.6 KiB
Python
91 lines
2.6 KiB
Python
|
""" A universal module with functions / classes without dependencies. """
|
||
|
import functools
|
||
|
import re
|
||
|
import os
|
||
|
|
||
|
|
||
|
_sep = os.path.sep
|
||
|
if os.path.altsep is not None:
|
||
|
_sep += os.path.altsep
|
||
|
_path_re = re.compile(r'(?:\.[^{0}]+|[{0}]__init__\.py)$'.format(re.escape(_sep)))
|
||
|
del _sep
|
||
|
|
||
|
|
||
|
def to_list(func):
|
||
|
def wrapper(*args, **kwargs):
|
||
|
return list(func(*args, **kwargs))
|
||
|
return wrapper
|
||
|
|
||
|
|
||
|
def to_tuple(func):
|
||
|
def wrapper(*args, **kwargs):
|
||
|
return tuple(func(*args, **kwargs))
|
||
|
return wrapper
|
||
|
|
||
|
|
||
|
def unite(iterable):
|
||
|
"""Turns a two dimensional array into a one dimensional."""
|
||
|
return set(typ for types in iterable for typ in types)
|
||
|
|
||
|
|
||
|
class UncaughtAttributeError(Exception):
|
||
|
"""
|
||
|
Important, because `__getattr__` and `hasattr` catch AttributeErrors
|
||
|
implicitly. This is really evil (mainly because of `__getattr__`).
|
||
|
Therefore this class originally had to be derived from `BaseException`
|
||
|
instead of `Exception`. But because I removed relevant `hasattr` from
|
||
|
the code base, we can now switch back to `Exception`.
|
||
|
|
||
|
:param base: return values of sys.exc_info().
|
||
|
"""
|
||
|
|
||
|
|
||
|
def safe_property(func):
|
||
|
return property(reraise_uncaught(func))
|
||
|
|
||
|
|
||
|
def reraise_uncaught(func):
|
||
|
"""
|
||
|
Re-throw uncaught `AttributeError`.
|
||
|
|
||
|
Usage: Put ``@rethrow_uncaught`` in front of the function
|
||
|
which does **not** suppose to raise `AttributeError`.
|
||
|
|
||
|
AttributeError is easily get caught by `hasattr` and another
|
||
|
``except AttributeError`` clause. This becomes problem when you use
|
||
|
a lot of "dynamic" attributes (e.g., using ``@property``) because you
|
||
|
can't distinguish if the property does not exist for real or some code
|
||
|
inside of the "dynamic" attribute through that error. In a well
|
||
|
written code, such error should not exist but getting there is very
|
||
|
difficult. This decorator is to help us getting there by changing
|
||
|
`AttributeError` to `UncaughtAttributeError` to avoid unexpected catch.
|
||
|
This helps us noticing bugs earlier and facilitates debugging.
|
||
|
"""
|
||
|
@functools.wraps(func)
|
||
|
def wrapper(*args, **kwds):
|
||
|
try:
|
||
|
return func(*args, **kwds)
|
||
|
except AttributeError as e:
|
||
|
raise UncaughtAttributeError(e) from e
|
||
|
return wrapper
|
||
|
|
||
|
|
||
|
class PushBackIterator:
|
||
|
def __init__(self, iterator):
|
||
|
self.pushes = []
|
||
|
self.iterator = iterator
|
||
|
self.current = None
|
||
|
|
||
|
def push_back(self, value):
|
||
|
self.pushes.append(value)
|
||
|
|
||
|
def __iter__(self):
|
||
|
return self
|
||
|
|
||
|
def __next__(self):
|
||
|
if self.pushes:
|
||
|
self.current = self.pushes.pop()
|
||
|
else:
|
||
|
self.current = next(self.iterator)
|
||
|
return self.current
|