Skip to content

Instantly share code, notes, and snippets.

@Dominik1123
Created February 4, 2022 15:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Dominik1123/8359aca5c4599a0f1302b20ccc91ec78 to your computer and use it in GitHub Desktop.
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
"""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