Source code for pcapkit.utilities.inspect

# -*- coding: utf-8 -*-
"""Abstract Classes
======================

.. module:: pcapkit.utilities.inspect

:mod:`pcapkit.utilities.inspect` holds :func:`isabstract`, which tells an abstract
class from a concrete one without the interpreter's abstract flag, so the answer is
the same on every interpreter, during :meth:`~object.__init_subclass__` and after the
class is made (:issue:`2784`).

.. versionadded:: 1.6.0

"""
import abc
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from typing import Any

__all__ = ['isabstract']


# GraalPy freezes a class's ``__flags__`` at the first read, so setting
# ``__abstractmethods__`` afterwards never raises the abstract flag there (#2784,
# measured on GraalPy 25.4.4). :func:`inspect.isabstract` reads the flag first, so
# called from ``__init_subclass__``, before :class:`abc.ABCMeta` has set
# ``__abstractmethods__``, it leaves an abstract class reported concrete from then
# on. This reads no flag; it derives the answer as :class:`abc.ABCMeta` does.
[docs] def isabstract(cls: 'Any') -> 'bool': """Whether ``cls`` is an abstract class, without its ``__flags__``. A class made by :class:`abc.ABCMeta` is abstract when its own namespace holds an abstract method, or when a name one of its bases lists in ``__abstractmethods__`` still resolves to an abstract method on it. That is how :class:`abc.ABCMeta` computes ``__abstractmethods__``, so the answer agrees with it after the class is made, and is already the same while :meth:`~object.__init_subclass__` runs, before :class:`abc.ABCMeta` has set it. :func:`inspect.isabstract` takes the same route there, but reads the interpreter's abstract flag first. Args: cls: Object to check. Anything other than a class made by :class:`abc.ABCMeta` is not abstract. Returns: Whether ``cls`` is abstract. """ if not isinstance(cls, abc.ABCMeta): return False if any(getattr(value, '__isabstractmethod__', False) for value in vars(cls).values()): return True return any(getattr(getattr(cls, name, None), '__isabstractmethod__', False) for base in cls.__bases__ for name in getattr(base, '__abstractmethods__', ()))