# _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