Source code for simvx.core.graphics._slots

"""The attribute names a ``__slots__`` class stores, for the copy protocols.

:class:`~simvx.core.graphics.texture.Texture` and
:class:`~simvx.core.graphics.material.Material` both hand-roll ``__copy__``,
``__deepcopy__`` and the pickle pair, and both need the same list. One helper,
so a correction lands in one place.
"""

from __future__ import annotations

__all__ = ["slot_names"]

#: Entries a class may put in ``__slots__`` that are machinery rather than data.
#: The list is the one ``copyreg._slotnames`` drops: ``__dict__`` names the dict
#: itself (carrying it through ``setattr`` would share one dict between an
#: original and its copy) and ``__weakref__`` names the weak-reference slot.
_NOT_DATA = frozenset({"__dict__", "__weakref__"})


[docs] def slot_names(cls: type) -> tuple[str, ...]: """Every attribute name *cls* stores in a slot, base classes included. The whole MRO is walked rather than one class's ``__slots__``, so a subclass that adds slots of its own keeps them: backends subclass to add GPU state. Names are returned most-derived first and each appears once. Two spellings that a naive read of ``__slots__`` gets wrong are handled here. ``__slots__ = "x"`` is a legal single-name form that iterates as characters, and a private name is mangled by the class body that declared it, so ``__slots__ = ("__x",)`` in ``Foo`` creates a slot called ``_Foo__x``. """ names: list[str] = [] seen: set[str] = set() for klass in cls.__mro__: declared = klass.__dict__.get("__slots__", ()) if isinstance(declared, str): declared = (declared,) for name in declared: if name in _NOT_DATA: continue if name.startswith("__") and not name.endswith("__"): owner = klass.__name__.lstrip("_") if owner: name = f"_{owner}{name}" if name not in seen: seen.add(name) names.append(name) return tuple(names)