Python metaprogramming
python #metaprogramming
Examples from my codebases https://github.com/barisozmen/python-cookbook/blob/main/metaclasses.py
from cli_support import graceful_keyboard_interrupt, debugger_on_error
class SingletonMetaclass(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class GracefulInterruptMetaclass(type):
def __new__(cls, name, bases, attrs):
# Wrap all public methods with graceful_keyboard_interrupt
for key, value in attrs.items():
if callable(value) and not key.startswith('_'):
attrs[key] = graceful_keyboard_interrupt(value)
return super().__new__(cls, name, bases, attrs)
class DebuggerOnErrorMetaclass(type):
def __new__(cls, name, bases, attrs):
for key, value in attrs.items():
if callable(value) and not key.startswith('_'):
attrs[key] = debugger_on_error(value)
return super().__new__(cls, name, bases, attrs)
class LoggedMetaclass(type):
"""Metaclass that logs all method calls for a class"""
def __new__(cls, name, bases, attrs):
# Wrap callable methods with logging
for key, value in attrs.items():
if callable(value) and not key.startswith('_'):
attrs[key] = cls.log_call(value)
return super().__new__(cls, name, bases, attrs)
@staticmethod
def log_call(func):
def wrapper(*args, **kwargs):
print(f'Calling: {func.__name__}')
result = func(*args, **kwargs)
print(f'Finished: {func.__name__}')
return result
return wrapper
class StaticMethodsByDefault(type):
def __new__(cls, name, bases, attrs):
# Make all methods static by default, except special methods
for key, value in attrs.items():
if callable(value) and not key.startswith('__'):
attrs[key] = staticmethod(value)
return super().__new__(cls, name, bases, attrs)
https://github.com/barisozmen/python-cookbook/blob/main/cli_support.py
import os
import sys
from functools import wraps
def graceful_keyboard_interrupt(func):
@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except KeyboardInterrupt:
print("\n\nKeyboard interrupt detected. Exiting...")
print("\nGoodbye! 👋\n")
sys.exit(1)
return wrapper
def debugger_on_error(func):
@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
import pdb; pdb.set_trace()
raise e
return wrapper
https://github.com/barisozmen/python-cookbook/blob/main/singletons.py
from metaclasses import SingletonMetaclass
class Counter(metaclass=SingletonMetaclass):
def __init__(self):
self.count = 0
@property
def next_id(self):
self.count += 1
return self.count
class Config(metaclass=SingletonMetaclass):
def __init__(self):
self._settings = {}
def set(self, key, value):
self._settings[key] = value
def get(self, key, default=None):
return self._settings.get(key, default)
Linked from
- David Beazley - Reinventing the Parser Generator - PyCon 2018 (archived) — Writing lexers and parsers is a complex problem that often involves the use of special tools and domain specific lang...
- Write a Compiler Course - Warmup Project — - his another lib is PLY, he developed SLY for enabling new [[Python metaprogramming]] features (see [[David Beazley ...