Source code for AutoArchive._infrastructure.py_additions

# _py_additions.py
#
# Project: AutoArchive
# License: GNU GPLv3
#
# Copyright (C) 2003 - 2022 Róbert Čerňanský



"""Various enhancements to Python."""



__all__ = ["Enum", "event", "classproperty", "staticproperty", "Flag", "maxRecursion"]



# {{{ INCLUDES

from collections.abc import Iterable
from contextlib import ContextDecorator

# }}} INCLUDES



# {{{ CLASSES

[docs]class Enum(Iterable): """Simple enum class. Example Usage:: codes = Enum("FOO", "BAR", "BAZ") # codes.BAZ will be 2 and so on and str.BAZ will be "BAZ" :param names: Iterable of Enum members. :type names: ``Iterator<str>``""" def __init__(self, *names): self.__names = names for number, name in enumerate(names): setattr(self, name, self._DescriptiveDecimalInt(number, name)) def __repr__(self): return str.join(", ", self.__names) def __iter__(self): return iter(range(len(self.__names))) class _DescriptiveDecimalInt(int): def __new__(cls, value = 0, *args): return super().__new__(cls, value) def __init__(self, value = 0, description = None): super().__init__() self.__description = description def __str__(self): if self.description is not None: return self.description else: return super().__str__() @property def description(self): return self.__description @description.setter def description(self, value): self.__description = value
[docs]class event: """Decorator that declares a function as an *event*. Implements a C#-like events (a call dispatchers). Decorating a function or method with this decorator declares it as an *event*. In order to subscribe a handler function to the event, one should use the “+=” operator and to unsubscribe the “-=” operator. Such event can be fired by a calling the decorated function. This will dispatch the event to all subscribers, i.e. subscribed handler methods are called. .. note:: Decorated function should implement only ``pass`` as its body. .. note:: Decorated function can take any number of parameters or keyword parameters. Subscriber functions has to take same parameters as the event. Example usage:: class Button: # ... @event def clicked(self, some_parameter): "Fired when the button was clicked." pass def _fireClicked(self, some_parameter): clicked(some_parameter) # ... class Ui: def __init__(self, button): button.clicked += self._onButtonClicked def _onButtonClicked(self, some_parameter): "Handle the button click." # play a sound... :param eventFunction: Decorated function or method that becomes an *event*. :type eventFunction: ``function``""" def __init__(self, eventFunction): self.__eventFunction = eventFunction self.__subscribers = set() def __call__(self, *args, **kwargs): "Fires the event." # call the event function just to check that the event is called with correct number of parameters; besides # that this call is not necessary self.__eventFunction(*args, **kwargs) # call subscriber methods for subscriber in self.__subscribers: subscriber(*args, **kwargs) def __iadd__(self, subscriber): """Subscribes the ``subscriber`` to the *event*. :param subscriber: A listener that wants to be subscribed to the event. :type subscriber: ``function`` :note: Same object is subscribed only once, even if multiple attempts to subscribe it are performed.""" self.__subscribers.add(subscriber) return self def __isub__(self, subscriber): """Unsubscribes the ``subscriber`` from the *event*. :param subscriber: A listener that wants to be unsubscribed from the event. :type subscriber: ``function``""" self.__subscribers.discard(subscriber) return self
[docs]class classproperty(property): """Decorator that makes the decorated method a class property.""" def __get__(self, obj, type = None): return classmethod(self.fget).__get__(None, type)()
[docs]class staticproperty(property): """Decorator that makes the decorated method a static property.""" def __get__(self, obj, type = None): return staticmethod(self.fget).__get__(None, type)()
[docs]class Flag: """Context management-aware boolean flag. Example usage:: class Foo: def __init__(self): self._someFlag = Flag() def bar(self): with self._someFlag: # do_stuff def baz(self): if self._someFlag.isSet(): # do_not_do_stuff return """ def __init__(self): self.__flag = False def __enter__(self): self.__flag = True def __exit__(self, exceptionType, exception, exceptionTraceback): self.__flag = False
[docs] def isSet(self): """Returns ``True`` if the flag is set; ``False`` otherwise.""" return self.__flag
[docs]class maxRecursion(ContextDecorator): """Decorator that limits number of recursive calls to a function. :param maxDepth: Recursion limit. :raise RuntimeError: If recursion limit was exceeded.""" def __init__(self, maxDepth): self.__maxDepth = maxDepth self.__depthRemainder = maxDepth def __enter__(self): if self.__depthRemainder == 0: raise RuntimeError( str.format("Maximum function-specific recursion depth \"{}\" exceeded.", self.__maxDepth)) self.__depthRemainder -= 1 def __exit__(self, exceptionType, exception, exceptionTraceback): self.__depthRemainder += 1
# }}} CLASSES