snakeoil.klass.util module

class snakeoil.klass.util.ClassSlotting(cls, slots)[source]

Bases: NamedTuple

cls: type

Alias for field number 0

slots: Sequence[str] | None

Alias for field number 1

snakeoil.klass.util.combine_classes(kls: type, *extra: type) type[source]

Given a set of classes, combine this as if one had wrote the class by hand

This is primarily for composing metaclasses on the fly; this:

class foo(metaclass=combine_metaclasses(kls1, kls2, kls3)): pass

is the same as if you did this:

class mkls(kls1, kls2, kls3): pass class foo(metaclass=mkls): pass

snakeoil.klass.util.copy_class_docs(source_class)[source]

Copy the docs and annotations of a target class for methods that intersect with the target.

This does not check that the prototype signatures are the same, and it exempts __init__ since that makes no sense to copy

snakeoil.klass.util.copy_docs(target)[source]

Copy the docs and annotations off of the given target

This is used for implementations that look like something (the target), but do not actually invoke the the target.

If you’re just wrapping something- a true decorator- use functools.wraps

snakeoil.klass.util.get_attrs_of(obj: Any, weakref=False, suppressions: Iterable[str] = (), _sentinel=<object object>) Iterable[tuple[str, Any]][source]

yield the attributes of a given instance.

This handles both slotted and non slotted classes- slotted classes do not have __dict__. It also handles mixed derivations, a non slotted class that inherited from a slotted class.

For an ordered __dict__ class, the ordering is not honored in what this yields.

Parameters:
  • weakref – by default, suppress that __weakref__ exists since it’s python internal bookkeeping. The only reason to enable this is for introspection tools; even state saving tools shouldn’t care about __weakref__

  • suppressions – attributes to suppress from the return. Use this as a way to avoid having to write a filter in the consumer- this already has to do filtering after all.

snakeoil.klass.util.get_instances_of(cls: type, getattribute=False) list[type][source]

Find all instances of the class in memory that are reachable by python GC.

Certain cpython types may not implement visitation correctly- they’re broke, but they will hide the instances from this. This should never happen, but if you know an instance exists and is in cpython object, this is the ‘why’

Note: this uses object.__getattribute__ directly. Even if your instance has a __getattribute__ that returns a __class__ that isn’t it’s actuall class, this will find it.

snakeoil.klass.util.get_slot_of(cls: type) ClassSlotting[source]

Return the non-inherited slotting from a class, IE specifically what that definition set.

snakeoil.klass.util.get_slots_of(kls: type) Iterable[ClassSlotting][source]

Visit a class MRO collecting all slotting

This cannot collect slotting of C objects- python builtins like object, or literal python C extensions, not unless they expose __slots__.

snakeoil.klass.util.get_subclasses_of(cls: type, only_leaf_nodes=False, ABC: None | bool = None) Iterable[type][source]

yield the subclasses of the given class.

This walks the in memory tree of a class hierarchy, yield the subclasses of the given cls after optional filtering.

Note: this cannot work on metaclasses. Python doesn’t carry the necessary bookkeeping. The first level of a metaclass will be returned, but none of it’s derivative, and it’ll be treated as a leaf node- even if it isn’t.

Parameters:
  • only_leaf_nodes – if True, only yield classes which have no subclasses

  • ABC – if True, only yield abstract classes. If False, only yield classes no longer abstract. If None- the default- do no filtering for ABC.

snakeoil.klass.util.is_metaclass(cls: type) TypeGuard[type[type]][source]

discern if something is a metaclass. This intentionally ignores function based metaclasses