Created
February 4, 2022 15:38
-
-
Save Dominik1123/8359aca5c4599a0f1302b20ccc91ec78 to your computer and use it in GitHub Desktop.
Collection of all __dunder__ attributes/methods and functionality for creating a class that intercepts all __dunder__ methods
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""Collection of all __dunder__ attributes/methods and functionality for creating a class that intercepts all __dunder__ methods. | |
Example usage: | |
$ python -i dunder.py | |
>>> T = create_intercept_class() | |
>>> T() | |
__new__: args=(), kwargs={} | |
__init__: args=(), kwargs={} | |
__repr__: args=(), kwargs={} | |
<__main__.Test object at 0x7f56c3791690> | |
""" | |
PYTHON_VERSION = (3, 10) | |
CLASS_ATTRIBUTE_NAMES = [ | |
'__name__', | |
'__module__', | |
'__dict__', | |
'__bases__', | |
'__doc__', | |
'__annotations__', | |
'__mro__', | |
] | |
CLASS_METHOD_NAMES = [ | |
'__instancecheck__', | |
'__mro_entries__', | |
'__prepare__', | |
'__subclasscheck__', | |
] | |
FUNC_ATTRIBUTE_NAMES = [ | |
'__doc__', | |
'__name__', | |
'__qualname__', | |
'__module__', | |
'__defaults__', | |
'__code__', | |
'__globals__', | |
'__dict__', | |
'__closure__', | |
'__annotations__', | |
'__kwdefaults__', | |
] | |
METHOD_ATTRIBUTE_NAMES = [ | |
'__func__', | |
'__self__', | |
] | |
MODULE_ATTRIBUTE_NAMES = [ | |
'__name__', | |
'__doc__', | |
'__file__', | |
'__annotations__', | |
] | |
OBJECT_ATTRIBUTE_NAMES = [ | |
'__class__', | |
'__dict__', | |
'__match_args__', | |
'__name__', | |
'__slots__', | |
'__weakref__', | |
] | |
OBJECT_METHOD_NAMES = [ | |
'__abs__', | |
'__add__', | |
'__aenter__', | |
'__aexit__', | |
'__aiter__', | |
'__and__', | |
'__anext__', | |
'__await__', | |
'__bool__', | |
'__bytes__', | |
'__call__', | |
'__ceil__', | |
'__class_getitem__', | |
'__complex__', | |
'__contains__', | |
'__del__', | |
'__delattr__', | |
'__delete__', | |
'__delitem__', | |
'__dir__', | |
'__divmod__', | |
'__enter__', | |
'__eq__', | |
'__exit__', | |
'__float__', | |
'__floor__', | |
'__floordiv__', | |
'__format__', | |
'__ge__', | |
'__get__', | |
'__getattr__', | |
'__getattribute__', | |
'__getitem__', | |
'__gt__', | |
'__hash__', | |
'__iadd__', | |
'__iand__', | |
'__ifloordiv__', | |
'__ilshift__', | |
'__imatmul__', | |
'__imod__', | |
'__imul__', | |
'__index__', | |
'__init__', | |
'__init_subclass__', | |
'__int__', | |
'__invert__', | |
'__ior__', | |
'__ipow__', | |
'__irshift__', | |
'__isub__', | |
'__iter__', | |
'__itruediv__', | |
'__ixor__', | |
'__le__', | |
'__len__', | |
'__length_hint__', | |
'__lshift__', | |
'__lt__', | |
'__matmul__', | |
'__missing__', | |
'__mod__', | |
'__mul__', | |
'__ne__', | |
'__neg__', | |
'__new__', | |
'__next__', | |
'__or__', | |
'__pos__', | |
'__pow__', | |
'__radd__', | |
'__rand__', | |
'__rdivmod__', | |
'__repr__', | |
'__reversed__', | |
'__rfloordiv__', | |
'__rlshift__', | |
'__rmatmul__', | |
'__rmod__', | |
'__rmul__', | |
'__ror__', | |
'__round__', | |
'__rpow__', | |
'__rrshift__', | |
'__rshift__', | |
'__rsub__', | |
'__rtruediv__', | |
'__rxor__', | |
'__set__', | |
'__set_name__', | |
'__setattr__', | |
'__setitem__', | |
'__str__', | |
'__sub__', | |
'__truediv__', | |
'__trunc__', | |
'__xor__', | |
] | |
_DECORATORS = { | |
'__class_getitem__': '@classmethod', | |
'__init_subclass__': '@classmethod', | |
} | |
def verify(): | |
from pprint import pformat | |
import re | |
from urllib.request import urlopen | |
ignore = { | |
'__classcell__', | |
'__future__', | |
'__import__', | |
'__objclass__', | |
'__traceback__', | |
} | |
url = 'https://docs.python.org/3/reference/datamodel.html' | |
pattern = re.compile('__[a-z0-9_]+__') | |
found = set(pattern.findall(urlopen(url).read().decode())) | |
names = ( | |
set(CLASS_METHOD_NAMES) | |
| set(CLASS_ATTRIBUTE_NAMES) | |
| set(FUNC_ATTRIBUTE_NAMES) | |
| set(METHOD_ATTRIBUTE_NAMES) | |
| set(MODULE_ATTRIBUTE_NAMES) | |
| set(OBJECT_METHOD_NAMES) | |
| set(OBJECT_ATTRIBUTE_NAMES) | |
) | |
obsolete = names - found | |
missing = found - ignore - names | |
print(f'Obsolete: {pformat(obsolete)}') | |
print(f'Missing: {pformat(missing)}') | |
def create_intercept_class(*, name: str = 'Test', bases: str = '', callback=None): | |
global _callback | |
import textwrap | |
if bases: | |
bases = f'({bases.lstrip("(").rstrip(")")})' | |
if callback is None: | |
callback = lambda name, *args, **kwargs: print(f'{name}: args={args}, kwargs={kwargs}') | |
_callback = callback | |
class_body = '\n'.join( | |
f'{_DECORATORS.get(name, "")}\n' | |
f'def {name}({"self, " if _DECORATORS.get(name) != "@staticmethod" else ""}*args, **kwargs):\n' | |
f' _callback({name!r}, *args, **kwargs)\n' | |
f' return super().{name}({"self, " if name == "__new__" else ""}*args, **kwargs)\n' | |
for name in OBJECT_METHOD_NAMES | |
) | |
exec(f'class {name}{bases}:\n{textwrap.indent(class_body, 4*" ")}') | |
return locals()[name] | |
if __name__ == '__main__': | |
from argparse import ArgumentParser | |
parser = ArgumentParser() | |
parser.add_argument('--verify', action='store_true') | |
parser.add_argument('--intercept', action='store_true') | |
args = parser.parse_args() | |
if args.verify: | |
verify() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment