Skip to content

Instantly share code, notes, and snippets.

@clintval
Last active April 26, 2024 05:19
Show Gist options
  • Save clintval/6019589173e046116ba0931be7e1b575 to your computer and use it in GitHub Desktop.
Save clintval/6019589173e046116ba0931be7e1b575 to your computer and use it in GitHub Desktop.
def is_classmethod(method: FunctionType) -> bool:
"""Determine if a method is a classmethod or not by searching for a classmethod sentinel."""
bound_to = getattr(method, "__self__", None)
if not isinstance(bound_to, type):
return False
name = method.__name__
for clazz in bound_to.__mro__:
descriptor = vars(clazz).get(name)
if descriptor is not None:
return isinstance(descriptor, classmethod)
return False
def _patch_base_generic_alias_getattr_for_generic_classmethods() -> None:
def __getattr__(self: Any, name: str) -> Any: # type: ignore[misc]
"""A generic classmethod aware patch for __getattr__ of all base generic classes."""
if is_classmethod(class_def := original_getattr(self, name)):
param_to_args = dict(zip(self.__origin__.__parameters__, self.__args__, strict=True))
function = class_def.__func__
return FunctionType(
function.__code__,
function.__globals__,
function.__name__,
function.__defaults__,
tuple(
CellType(
param_to_args.get(value, value) if isinstance(value, Hashable) else value
)
for value in map(attrgetter("cell_contents"), function.__closure__)
),
).__get__(class_def.__self__)
return class_def
if _BaseGenericAlias.__getattr__ is not __getattr__:
original_getattr = _BaseGenericAlias.__getattr__
_BaseGenericAlias.__getattr__ = __getattr__
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment