From b7ab8b071430bd69768db0a05cfdde2d85841930 Mon Sep 17 00:00:00 2001 From: leycec Date: Sat, 14 Aug 2021 01:42:22 -0400 Subject: [PATCH] Python 3.10 "NewType" x 3. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit is the last in a commit chain improving compatibility with the variant of the `typing.NewType` attribute implemented under Python ≥ 3.10, which fundamentally refactored that attribute from a function factory to a class -- significantly complicating cross-version detection of that attribute. Specifically, this commit finalizes both the implementation and testing of this attribute under Python ≥ 3.10. (*Ignominious gnomish minions!*) --- README.rst | 3 +- beartype/_cave/_cavefast.py | 6 +- beartype/{_util/data => _data}/__init__.py | 0 .../{_util/data => _data}/cls/__init__.py | 0 beartype/{_util/data => _data}/cls/datacls.py | 0 .../{_util/data => _data}/func/__init__.py | 0 .../{_util/data => _data}/func/datafunc.py | 0 .../{_util/data => _data}/hint/__init__.py | 0 .../{_util/data => _data}/hint/datahint.py | 2 +- .../data => _data}/hint/pep/__init__.py | 0 .../data => _data}/hint/pep/datapeprepr.py | 18 ++- .../hint/pep/sign}/__init__.py | 0 .../hint/pep/sign/datapepsigncls.py | 0 .../hint/pep/sign/datapepsigns.py | 4 +- .../hint/pep/sign/datapepsignset.py | 45 +++++- .../hint/pep/sign => _data/mod}/__init__.py | 0 beartype/{_util/data => _data}/mod/datamod.py | 0 beartype/_decor/_cache/cachehint.py | 4 +- beartype/_decor/_code/_pep/_pephint.py | 33 +++-- beartype/_decor/_error/_errorgeneric.py | 6 +- beartype/_decor/_error/_errorsequence.py | 4 +- beartype/_decor/_error/_errorsleuth.py | 2 +- beartype/_decor/_error/_errortype.py | 2 +- .../_error/_proposal/_errorpep484noreturn.py | 2 +- .../_error/_proposal/_errorpep484union.py | 2 +- .../_decor/_error/_proposal/_errorpep586.py | 2 +- .../_decor/_error/_proposal/_errorpep593.py | 4 +- beartype/_decor/_error/errormain.py | 6 +- beartype/_util/cls/utilclstest.py | 4 +- .../data/hint/pep/proposal/datapep484.py | 38 ----- .../data/hint/pep/proposal/datapep585.py | 30 ---- beartype/_util/data/mod/__init__.py | 0 beartype/_util/hint/pep/mod/utilmodnumpy.py | 2 +- .../{utilhintpep484.py => utilpep484.py} | 136 +++++++++++------- .../{utilhintpep544.py => utilpep544.py} | 10 +- .../{utilhintpep585.py => utilpep585.py} | 11 ++ .../{utilhintpep586.py => utilpep586.py} | 2 +- .../{utilhintpep593.py => utilpep593.py} | 4 +- beartype/_util/hint/pep/utilpepget.py | 129 +++++++---------- beartype/_util/hint/pep/utilpeptest.py | 107 +++++--------- beartype/_util/hint/utilhintget.py | 10 +- beartype/_util/hint/utilhinttest.py | 4 +- beartype/_util/mod/utilmodimport.py | 47 +++--- beartype/_util/mod/utilmodtest.py | 51 +++---- beartype/_util/py/utilpyversion.py | 8 ++ beartype/_util/text/utiltextlabel.py | 2 +- beartype/meta.py | 2 +- beartype/roar/_roarexc.py | 1 - beartype/roar/_roarwarn.py | 1 - .../a00_pep/proposal/test_utilhintpep586.py | 6 +- .../a00_pep/proposal/test_utilhintpep593.py | 6 +- .../hint/a00_pep/test_a00_utilpepget.py | 4 +- .../hint/a00_pep/test_a90_utilpeptest.py | 4 +- .../hint/a90_core/test_a00_utilhintget.py | 2 +- .../a00_util/mod/test_utilmodimport.py | 37 +++++ .../a00_unit/a00_util/mod/test_utilmodtest.py | 13 +- beartype_test/a00_unit/a10_pep/test_pep544.py | 12 +- beartype_test/a00_unit/a10_pep/test_pep585.py | 12 +- beartype_test/a00_unit/a10_pep/test_pep593.py | 4 +- .../a00_unit/a90_decor/code/test_codemain.py | 2 +- beartype_test/a00_unit/data/data_type.py | 4 +- .../data/hint/nonpep/data_hintnonpep.py | 8 +- .../hint/nonpep/mod/_data_hintmodbeartype.py | 8 +- .../nonpep/proposal/_data_hintnonpep484.py | 4 +- .../a00_unit/data/hint/pep/data_hintpep.py | 12 +- .../data/hint/pep/mod/_data_hintmodnumpy.py | 8 +- .../hint/pep/proposal/_data_hintpep544.py | 30 ++-- .../hint/pep/proposal/_data_hintpep585.py | 66 ++++----- .../hint/pep/proposal/_data_hintpep586.py | 20 +-- .../hint/pep/proposal/_data_hintpep593.py | 24 ++-- .../data/hint/pep/proposal/data_hintpep484.py | 129 +++++++++-------- .../data/hint/util/data_hintmetacls.py | 8 +- .../data/hint/util/data_hintmetatyping.py | 26 ++-- pytest | 4 +- 74 files changed, 607 insertions(+), 590 deletions(-) rename beartype/{_util/data => _data}/__init__.py (100%) rename beartype/{_util/data => _data}/cls/__init__.py (100%) rename beartype/{_util/data => _data}/cls/datacls.py (100%) rename beartype/{_util/data => _data}/func/__init__.py (100%) rename beartype/{_util/data => _data}/func/datafunc.py (100%) rename beartype/{_util/data => _data}/hint/__init__.py (100%) rename beartype/{_util/data => _data}/hint/datahint.py (96%) rename beartype/{_util/data => _data}/hint/pep/__init__.py (100%) rename beartype/{_util/data => _data}/hint/pep/datapeprepr.py (97%) rename beartype/{_util/data/hint/pep/proposal => _data/hint/pep/sign}/__init__.py (100%) rename beartype/{_util/data => _data}/hint/pep/sign/datapepsigncls.py (100%) rename beartype/{_util/data => _data}/hint/pep/sign/datapepsigns.py (98%) rename beartype/{_util/data => _data}/hint/pep/sign/datapepsignset.py (89%) rename beartype/{_util/data/hint/pep/sign => _data/mod}/__init__.py (100%) rename beartype/{_util/data => _data}/mod/datamod.py (100%) delete mode 100644 beartype/_util/data/hint/pep/proposal/datapep484.py delete mode 100644 beartype/_util/data/hint/pep/proposal/datapep585.py delete mode 100644 beartype/_util/data/mod/__init__.py rename beartype/_util/hint/pep/proposal/{utilhintpep484.py => utilpep484.py} (88%) rename beartype/_util/hint/pep/proposal/{utilhintpep544.py => utilpep544.py} (98%) rename beartype/_util/hint/pep/proposal/{utilhintpep585.py => utilpep585.py} (95%) rename beartype/_util/hint/pep/proposal/{utilhintpep586.py => utilpep586.py} (98%) rename beartype/_util/hint/pep/proposal/{utilhintpep593.py => utilpep593.py} (98%) diff --git a/README.rst b/README.rst index 167c7ede..72cad38f 100644 --- a/README.rst +++ b/README.rst @@ -2144,6 +2144,7 @@ Compliance * `PEP 589 -- TypedDict: Type Hints for Dictionaries with a Fixed Set of Keys `__. * `PEP 591 -- Adding a final qualifier to typing `__. +* `PEP 612 -- Parameter Specification Variables `__. See also the **PEP** and **typing** categories of our `features matrix `__ for further details. @@ -3518,7 +3519,7 @@ External ``beartype`` resources include: * `This list of all open-source PyPI-hosted dependents of this package `__ (i.e., third-party packages requiring ``beartype`` - as a runtime dependency), kindly provided by the `Libraries.io package + as a runtime dependency), kindly furnished by the `Libraries.io package registry `__. Related type-checking resources include: diff --git a/beartype/_cave/_cavefast.py b/beartype/_cave/_cavefast.py index 177681e8..92544282 100644 --- a/beartype/_cave/_cavefast.py +++ b/beartype/_cave/_cavefast.py @@ -891,11 +891,11 @@ class or instance attributes). by this type, higher-level PEP-specific functions *must* be called instead. These include: -* :func:`beartype._util.hint.pep.proposal.utilhintpep484.is_hint_pep484_generic`. +* :func:`beartype._util.hint.pep.proposal.utilpep484.is_hint_pep484_generic`. detecting `PEP 484`_-compliant generic type hints. -* :func:`beartype._util.hint.pep.proposal.utilhintpep585.is_hint_pep585_builtin`. +* :func:`beartype._util.hint.pep.proposal.utilpep585.is_hint_pep585_builtin`. detecting `PEP 585`_-compliant builtin type hints. -* :func:`beartype._util.hint.pep.proposal.utilhintpep585.is_hint_pep585_generic`. +* :func:`beartype._util.hint.pep.proposal.utilpep585.is_hint_pep585_generic`. detecting `PEP 585`_-compliant generic type hints. .. _PEP 484: diff --git a/beartype/_util/data/__init__.py b/beartype/_data/__init__.py similarity index 100% rename from beartype/_util/data/__init__.py rename to beartype/_data/__init__.py diff --git a/beartype/_util/data/cls/__init__.py b/beartype/_data/cls/__init__.py similarity index 100% rename from beartype/_util/data/cls/__init__.py rename to beartype/_data/cls/__init__.py diff --git a/beartype/_util/data/cls/datacls.py b/beartype/_data/cls/datacls.py similarity index 100% rename from beartype/_util/data/cls/datacls.py rename to beartype/_data/cls/datacls.py diff --git a/beartype/_util/data/func/__init__.py b/beartype/_data/func/__init__.py similarity index 100% rename from beartype/_util/data/func/__init__.py rename to beartype/_data/func/__init__.py diff --git a/beartype/_util/data/func/datafunc.py b/beartype/_data/func/datafunc.py similarity index 100% rename from beartype/_util/data/func/datafunc.py rename to beartype/_data/func/datafunc.py diff --git a/beartype/_util/data/hint/__init__.py b/beartype/_data/hint/__init__.py similarity index 100% rename from beartype/_util/data/hint/__init__.py rename to beartype/_data/hint/__init__.py diff --git a/beartype/_util/data/hint/datahint.py b/beartype/_data/hint/datahint.py similarity index 96% rename from beartype/_util/data/hint/datahint.py rename to beartype/_data/hint/datahint.py index a60055d8..d28020aa 100644 --- a/beartype/_util/data/hint/datahint.py +++ b/beartype/_data/hint/datahint.py @@ -11,7 +11,7 @@ ''' # ....................{ IMPORTS }.................... -from beartype._util.data.hint.pep.proposal.datapep484 import ( +from beartype._util.hint.pep.proposal.utilpep484 import ( HINT_PEP484_TYPE_FORWARDREF) # See the "beartype.cave" submodule for further commentary. diff --git a/beartype/_util/data/hint/pep/__init__.py b/beartype/_data/hint/pep/__init__.py similarity index 100% rename from beartype/_util/data/hint/pep/__init__.py rename to beartype/_data/hint/pep/__init__.py diff --git a/beartype/_util/data/hint/pep/datapeprepr.py b/beartype/_data/hint/pep/datapeprepr.py similarity index 97% rename from beartype/_util/data/hint/pep/datapeprepr.py rename to beartype/_data/hint/pep/datapeprepr.py index 4445e5ac..c26cee28 100644 --- a/beartype/_util/data/hint/pep/datapeprepr.py +++ b/beartype/_data/hint/pep/datapeprepr.py @@ -12,9 +12,9 @@ ''' # ....................{ IMPORTS }.................... -from beartype._util.data.hint.pep.sign import datapepsigns -from beartype._util.data.hint.pep.sign.datapepsigncls import HintSign -from beartype._util.data.hint.pep.sign.datapepsigns import ( +from beartype._data.hint.pep.sign import datapepsigns +from beartype._data.hint.pep.sign.datapepsigncls import HintSign +from beartype._data.hint.pep.sign.datapepsigns import ( HintSignAbstractSet, # HintSignAnnotated, # HintSignAny, @@ -55,7 +55,7 @@ HintSignMutableSequence, HintSignMutableSet, # HintSignNamedTuple, - # HintSignNewType, + HintSignNewType, HintSignNone, HintSignNumpyArray, # HintSignOptional, @@ -220,6 +220,14 @@ # string as PEP-compliant substantially simplifies logic throughout the # codebase, we (currently) opt to do so. 'builtins.str': HintSignForwardRef, + + # Python >= 3.10 implements PEP 484-compliant "typing.NewType" type hints + # as instances of that class. Regardless of the current Python version, + # "typing_extensions.NewType" type hints remain implemented in manner of + # Python < 3.10 -- which is to say, as closures of that function. Ergo, we + # intentionally omit "typing_extensions.NewType" here. See also: + # https://github.com/python/typing/blob/master/typing_extensions/src_py3/typing_extensions.py + 'typing.NewType': HintSignNewType, } ''' Dictionary mapping from the fully-qualified classnames of all PEP-compliant @@ -319,7 +327,7 @@ def _init() -> None: # ..................{ EXTERNALS }.................. # Defer initialization-specific imports. - from beartype._util.data.mod.datamod import TYPING_MODULE_NAMES + from beartype._data.mod.datamod import TYPING_MODULE_NAMES # Permit redefinition of these globals below. global \ diff --git a/beartype/_util/data/hint/pep/proposal/__init__.py b/beartype/_data/hint/pep/sign/__init__.py similarity index 100% rename from beartype/_util/data/hint/pep/proposal/__init__.py rename to beartype/_data/hint/pep/sign/__init__.py diff --git a/beartype/_util/data/hint/pep/sign/datapepsigncls.py b/beartype/_data/hint/pep/sign/datapepsigncls.py similarity index 100% rename from beartype/_util/data/hint/pep/sign/datapepsigncls.py rename to beartype/_data/hint/pep/sign/datapepsigncls.py diff --git a/beartype/_util/data/hint/pep/sign/datapepsigns.py b/beartype/_data/hint/pep/sign/datapepsigns.py similarity index 98% rename from beartype/_util/data/hint/pep/sign/datapepsigns.py rename to beartype/_data/hint/pep/sign/datapepsigns.py index 5ee5b8cc..9852f231 100644 --- a/beartype/_util/data/hint/pep/sign/datapepsigns.py +++ b/beartype/_data/hint/pep/sign/datapepsigns.py @@ -5,7 +5,7 @@ ''' Project-wide **Python version-agnostic signs** (i.e., instances of the -:class:`beartype._util.data.hint.pep.sign.datapepsigncls.HintSign` class +:class:`beartype._data.hint.pep.sign.datapepsigncls.HintSign` class uniquely identifying PEP-compliant type hints in a safe, non-deprecated manner regardless of the Python version targeted by the active Python interpreter). @@ -17,7 +17,7 @@ # CAUTION: Attributes imported here at module scope *MUST* be explicitly # deleted from this module's namespace below. #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -from beartype._util.data.hint.pep.sign.datapepsigncls import ( +from beartype._data.hint.pep.sign.datapepsigncls import ( HintSign as _HintSign) # ....................{ SIGNS ~ explicit }.................... diff --git a/beartype/_util/data/hint/pep/sign/datapepsignset.py b/beartype/_data/hint/pep/sign/datapepsignset.py similarity index 89% rename from beartype/_util/data/hint/pep/sign/datapepsignset.py rename to beartype/_data/hint/pep/sign/datapepsignset.py index 77e08dc6..aa183d49 100644 --- a/beartype/_util/data/hint/pep/sign/datapepsignset.py +++ b/beartype/_data/hint/pep/sign/datapepsignset.py @@ -6,7 +6,7 @@ ''' Project-wide **PEP-compliant type hint sign sets** (i.e., frozen set globals aggregating instances of the -:class:`beartype._util.data.hint.pep.sign.datapepsigncls.HintSign` class, +:class:`beartype._data.hint.pep.sign.datapepsigncls.HintSign` class, enabling efficient categorization of signs as belonging to various categories of PEP-compliant type hints). @@ -14,7 +14,7 @@ ''' # ....................{ IMPORTS }.................... -from beartype._util.data.hint.pep.sign.datapepsigns import ( +from beartype._data.hint.pep.sign.datapepsigns import ( HintSignAbstractSet, HintSignAnnotated, HintSignAny, @@ -149,7 +149,7 @@ :func:`beartype.beartype` decorator). ''' -# ....................{ SIGNS ~ origin }.................... +# ....................{ SIGNS ~ type }.................... HINT_SIGNS_TYPE_STDLIB = frozenset(( # ..................{ PEP (484|585) }.................. HintSignAbstractSet, @@ -226,6 +226,45 @@ :func:`beartype.beartype` decorator. ''' + +HINT_SIGNS_TYPE_MIMIC = frozenset(( + # ..................{ PEP 484 }.................. + HintSignNewType, + + # ..................{ PEP 593 }.................. + HintSignAnnotated, +)) +''' +Frozen set of all signs uniquely identifying **PEP-compliant type hint mimics** +(i.e., hints maliciously masquerading as another type by explicitly overriding +their ``__module__`` dunder instance variable to that of that type). + +Notably, this set contains the signs of: + +* :pep:`484`-compliant :attr:`typing.NewType` type hints under Python >= 3.10, + which badly masquerade as their first passed argument to such an extreme + degree that they even intentionally prefix their machine-readable + representation by the fully-qualified name of the caller's module: e.g., + + .. code-block:: python + + # Under Python >= 3.10: + >>> import typing + >>> new_type = typing.NewType('List', bool) + >>> repr(new_type) + __main__.List # <---- this is genuine bollocks + +* :pep:`593`-compliant :attr:`typing.Annotated` type hints, which badly + masquerade as their first subscripted argument (e.g., the :class:`int` in + ``typing.Annotated[int, 63]``) such that the value of the ``__module__`` + attributes of these hints is that of that argument rather than their own. + Oddly, their machine-readable representation remains prefixed by + ``"typing."``, enabling an efficient test that also generalizes to all other + outlier edge cases that are probably lurking about. + +I have no code and I must scream. +''' + # ....................{ SETS ~ supported }.................... _HINT_SIGNS_SUPPORTED_SHALLOW = frozenset(( # ..................{ PEP 484 }.................. diff --git a/beartype/_util/data/hint/pep/sign/__init__.py b/beartype/_data/mod/__init__.py similarity index 100% rename from beartype/_util/data/hint/pep/sign/__init__.py rename to beartype/_data/mod/__init__.py diff --git a/beartype/_util/data/mod/datamod.py b/beartype/_data/mod/datamod.py similarity index 100% rename from beartype/_util/data/mod/datamod.py rename to beartype/_data/mod/datamod.py diff --git a/beartype/_decor/_cache/cachehint.py b/beartype/_decor/_cache/cachehint.py index 2c02a9f0..13e70f92 100644 --- a/beartype/_decor/_cache/cachehint.py +++ b/beartype/_decor/_cache/cachehint.py @@ -40,7 +40,7 @@ # strongly benefit from some form of memoization or caching. Since this edge # case should be fairly rare, even a dictionary would probably be overkill. # Just implementing something resembling the following memoized getter -# in the "utilhintpep544" submodule would probably suffice: +# in the "utilpep544" submodule would probably suffice: # @callable_cached # def get_pep544_protocol_checkable_from_protocol_uncheckable( # protocol_uncheckable: object) -> Protocol: @@ -51,7 +51,7 @@ # ....................{ IMPORTS }.................... from beartype._cave._cavefast import NotImplementedType, NoneType from beartype._util.hint.utilhinttest import die_unless_hint -from beartype._util.data.func.datafunc import METHOD_NAMES_BINARY_DUNDER +from beartype._data.func.datafunc import METHOD_NAMES_BINARY_DUNDER from collections.abc import Callable from typing import Any, Dict, Union diff --git a/beartype/_decor/_code/_pep/_pephint.py b/beartype/_decor/_code/_pep/_pephint.py index 729aa4c2..a8d24acd 100644 --- a/beartype/_decor/_code/_pep/_pephint.py +++ b/beartype/_decor/_code/_pep/_pephint.py @@ -85,14 +85,14 @@ acquire_object_typed, release_object_typed, ) -from beartype._util.data.hint.pep.sign.datapepsigns import ( +from beartype._data.hint.pep.sign.datapepsigns import ( HintSignAnnotated, HintSignForwardRef, HintSignGeneric, HintSignLiteral, HintSignTuple, ) -from beartype._util.data.hint.pep.sign.datapepsignset import ( +from beartype._data.hint.pep.sign.datapepsignset import ( HINT_SIGNS_SEQUENCE_ARGS_1, HINT_SIGNS_SUPPORTED_DEEP, HINT_SIGNS_TYPE_STDLIB, @@ -105,13 +105,13 @@ add_func_scope_types, ) from beartype._util.hint.utilhintget import get_hint_reduced -from beartype._util.hint.pep.proposal.utilhintpep484 import ( +from beartype._util.hint.pep.proposal.utilpep484 import ( get_hint_pep484_generic_base_erased_from_unerased) -from beartype._util.hint.pep.proposal.utilhintpep585 import ( +from beartype._util.hint.pep.proposal.utilpep585 import ( is_hint_pep585_builtin) -from beartype._util.hint.pep.proposal.utilhintpep586 import ( +from beartype._util.hint.pep.proposal.utilpep586 import ( die_unless_hint_pep586) -from beartype._util.hint.pep.proposal.utilhintpep593 import ( +from beartype._util.hint.pep.proposal.utilpep593 import ( get_hint_pep593_metadata, get_hint_pep593_metahint, ) @@ -973,7 +973,7 @@ def _enqueue_hint_child(pith_child_expr: str) -> str: # for that attribute *MUST* also be added to the parallel: # * "beartype._util.hint.pep.errormain" submodule, which # raises exceptions on the current pith failing this check. - # * "beartype._util.data.hint.pep.sign.datapepsignset.HINT_SIGNS_SUPPORTED_DEEP" + # * "beartype._data.hint.pep.sign.datapepsignset.HINT_SIGNS_SUPPORTED_DEEP" # frozen set of all signs for which this function generates # deeply type-checking code. #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -1662,12 +1662,11 @@ def _enqueue_hint_child(pith_child_expr: str) -> str: # # If this pseudo-superclass is neither a PEP 585-compliant # type hint *NOR* a PEP-compliant type hint defined by the - # "typing" module, this pseudo-superclass *MUST* be a PEP - # 585-noncompliant user-defined pseudo-superclass. In this - # case, reduce this pseudo-superclass to the corresponding - # actual superclass originating this pseudo-superclass. - # - # Note that: + # "typing" module, this pseudo-superclass *MUST* be a + # user-defined pseudo-superclass *NOT* compliant with PEP + # 585. In this case, reduce this pseudo-superclass to the + # corresponding actual superclass originating this + # pseudo-superclass. Note that: # * This horrible, irrational, and unintuitive edge case # arises *ONLY* for user-defined PEP 484-compliant # generics and PEP 544-compliant protocols subclassing @@ -1684,7 +1683,7 @@ def _enqueue_hint_child(pith_child_expr: str) -> str: # >>> isinstance(UserProtocolUnerased, type) # False # * PEP 585-compliant generics suffer no such issues: - # >>> from beartype._util.hint.pep.proposal.utilhintpep585 import is_hint_pep585_builtin + # >>> from beartype._util.hint.pep.proposal.utilpep585 import is_hint_pep585_builtin # >>> class UserGeneric(list[int]): pass # >>> class UserSubgeneric(UserGeneric[int]): pass # >>> UserSubgeneric.__orig_bases__ @@ -1717,15 +1716,15 @@ def _enqueue_hint_child(pith_child_expr: str) -> str: # other means of recursing into the possibly relevant # superclasses of this erased superclass. # - # Note that, in theory, we could deeply refactor this + # Note that, in theory, we could deeply refactor this whole # algorithm to support the notion of child hints that # should be ignored for purposes of type-checking but # nonetheless recursed into. In practice, the current # approach only introduces mild runtime inefficiencies # while preserving sanity throughout this algorithm. # - # Specifically, perform this awful reduction *ONLY* if - # this child hint is a PEP 484- or 544-compliant + # Specifically, perform this awful reduction *ONLY* if this + # pseudo-superclass is a PEP 484- or 544-compliant # user-defined pseudo-superclass that is neither... elif not ( # A PEP 585-compliant pseudo-superclass *NOR*... diff --git a/beartype/_decor/_error/_errorgeneric.py b/beartype/_decor/_error/_errorgeneric.py index faf627ae..71608f7c 100644 --- a/beartype/_decor/_error/_errorgeneric.py +++ b/beartype/_decor/_error/_errorgeneric.py @@ -16,10 +16,10 @@ from beartype._decor._error._errortype import ( get_cause_or_none_type) from beartype._decor._error._errorsleuth import CauseSleuth -from beartype._util.data.hint.pep.sign.datapepsigns import HintSignGeneric -from beartype._util.hint.pep.proposal.utilhintpep484 import ( +from beartype._data.hint.pep.sign.datapepsigns import HintSignGeneric +from beartype._util.hint.pep.proposal.utilpep484 import ( get_hint_pep484_generic_base_erased_from_unerased) -from beartype._util.hint.pep.proposal.utilhintpep585 import ( +from beartype._util.hint.pep.proposal.utilpep585 import ( is_hint_pep585_builtin) from beartype._util.hint.pep.utilpepget import ( get_hint_pep_generic_type_or_none) diff --git a/beartype/_decor/_error/_errorsequence.py b/beartype/_decor/_error/_errorsequence.py index 188ae21c..14413c04 100644 --- a/beartype/_decor/_error/_errorsequence.py +++ b/beartype/_decor/_error/_errorsequence.py @@ -14,8 +14,8 @@ # ....................{ IMPORTS }.................... from beartype._decor._error._errorsleuth import CauseSleuth from beartype._decor._error._errortype import get_cause_or_none_type_stdlib -from beartype._util.data.hint.pep.sign.datapepsigns import HintSignTuple -from beartype._util.data.hint.pep.sign.datapepsignset import ( +from beartype._data.hint.pep.sign.datapepsigns import HintSignTuple +from beartype._data.hint.pep.sign.datapepsignset import ( HINT_SIGNS_SEQUENCE_ARGS_1) from beartype._util.hint.pep.utilpeptest import is_hint_pep_tuple_empty from beartype._util.hint.utilhinttest import is_hint_ignorable diff --git a/beartype/_decor/_error/_errorsleuth.py b/beartype/_decor/_error/_errorsleuth.py index dd67a2ee..5a24c8e4 100644 --- a/beartype/_decor/_error/_errorsleuth.py +++ b/beartype/_decor/_error/_errorsleuth.py @@ -15,7 +15,7 @@ # ....................{ IMPORTS }.................... from beartype.roar._roarexc import _BeartypeCallHintPepRaiseException from beartype._cave._cavemap import NoneTypeOr -from beartype._util.data.hint.pep.sign.datapepsignset import ( +from beartype._data.hint.pep.sign.datapepsignset import ( HINT_SIGNS_SUPPORTED_DEEP, HINT_SIGNS_TYPE_STDLIB, ) diff --git a/beartype/_decor/_error/_errortype.py b/beartype/_decor/_error/_errortype.py index c20d3eb9..6f16f290 100644 --- a/beartype/_decor/_error/_errortype.py +++ b/beartype/_decor/_error/_errortype.py @@ -14,7 +14,7 @@ # ....................{ IMPORTS }.................... from beartype.roar._roarexc import _BeartypeCallHintPepRaiseException from beartype._decor._error._errorsleuth import CauseSleuth -from beartype._util.data.hint.pep.sign.datapepsigns import HintSignForwardRef +from beartype._data.hint.pep.sign.datapepsigns import HintSignForwardRef from beartype._util.hint.nonpep.utilnonpeptest import ( die_unless_hint_nonpep_tuple) from beartype._util.hint.pep.utilpepget import ( diff --git a/beartype/_decor/_error/_proposal/_errorpep484noreturn.py b/beartype/_decor/_error/_proposal/_errorpep484noreturn.py index ae0e489e..1ff62ee4 100644 --- a/beartype/_decor/_error/_proposal/_errorpep484noreturn.py +++ b/beartype/_decor/_error/_proposal/_errorpep484noreturn.py @@ -15,7 +15,7 @@ # ....................{ IMPORTS }.................... from beartype._decor._error._errorsleuth import CauseSleuth -from beartype._util.data.hint.pep.sign.datapepsigns import HintSignNoReturn +from beartype._data.hint.pep.sign.datapepsigns import HintSignNoReturn from beartype._util.text.utiltextlabel import label_callable # See the "beartype.cave" submodule for further commentary. diff --git a/beartype/_decor/_error/_proposal/_errorpep484union.py b/beartype/_decor/_error/_proposal/_errorpep484union.py index 5d5b9e26..7eb0f90f 100644 --- a/beartype/_decor/_error/_proposal/_errorpep484union.py +++ b/beartype/_decor/_error/_proposal/_errorpep484union.py @@ -16,7 +16,7 @@ # ....................{ IMPORTS }.................... from beartype.roar._roarexc import _BeartypeCallHintPepRaiseException from beartype._decor._error._errorsleuth import CauseSleuth -from beartype._util.data.hint.pep.sign.datapepsignset import HINT_SIGNS_UNION +from beartype._data.hint.pep.sign.datapepsignset import HINT_SIGNS_UNION from beartype._util.hint.pep.utilpepget import ( get_hint_pep_type_stdlib_or_none) from beartype._util.hint.pep.utilpeptest import is_hint_pep diff --git a/beartype/_decor/_error/_proposal/_errorpep586.py b/beartype/_decor/_error/_proposal/_errorpep586.py index 7d1b23d1..6ca7d8ad 100644 --- a/beartype/_decor/_error/_proposal/_errorpep586.py +++ b/beartype/_decor/_error/_proposal/_errorpep586.py @@ -15,7 +15,7 @@ # ....................{ IMPORTS }.................... from beartype._decor._error._errorsleuth import CauseSleuth -from beartype._util.data.hint.pep.sign.datapepsigns import HintSignLiteral +from beartype._data.hint.pep.sign.datapepsigns import HintSignLiteral from beartype._util.text.utiltextjoin import join_delimited_disjunction from beartype._util.text.utiltextrepr import represent_object from typing import Optional diff --git a/beartype/_decor/_error/_proposal/_errorpep593.py b/beartype/_decor/_error/_proposal/_errorpep593.py index 349956dd..ac97739d 100644 --- a/beartype/_decor/_error/_proposal/_errorpep593.py +++ b/beartype/_decor/_error/_proposal/_errorpep593.py @@ -16,8 +16,8 @@ # ....................{ IMPORTS }.................... from beartype.roar._roarexc import _BeartypeCallHintPepRaiseException from beartype._decor._error._errorsleuth import CauseSleuth -from beartype._util.data.hint.pep.sign.datapepsigns import HintSignAnnotated -from beartype._util.hint.pep.proposal.utilhintpep593 import ( +from beartype._data.hint.pep.sign.datapepsigns import HintSignAnnotated +from beartype._util.hint.pep.proposal.utilpep593 import ( get_hint_pep593_metadata, get_hint_pep593_metahint, ) diff --git a/beartype/_decor/_error/errormain.py b/beartype/_decor/_error/errormain.py index 82d5f17a..7dcdef7c 100644 --- a/beartype/_decor/_error/errormain.py +++ b/beartype/_decor/_error/errormain.py @@ -93,8 +93,8 @@ get_cause_or_none_literal) from beartype._decor._error._proposal._errorpep593 import ( get_cause_or_none_annotated) -from beartype._util.data.hint.pep.sign.datapepsigncls import HintSign -from beartype._util.data.hint.pep.sign.datapepsigns import ( +from beartype._data.hint.pep.sign.datapepsigncls import HintSign +from beartype._data.hint.pep.sign.datapepsigns import ( HintSignAnnotated, HintSignForwardRef, HintSignGeneric, @@ -102,7 +102,7 @@ HintSignNoReturn, HintSignTuple, ) -from beartype._util.data.hint.pep.sign.datapepsignset import ( +from beartype._data.hint.pep.sign.datapepsignset import ( HINT_SIGNS_SEQUENCE_ARGS_1, HINT_SIGNS_TYPE_STDLIB, HINT_SIGNS_UNION, diff --git a/beartype/_util/cls/utilclstest.py b/beartype/_util/cls/utilclstest.py index 09a47b7e..5d2ae987 100644 --- a/beartype/_util/cls/utilclstest.py +++ b/beartype/_util/cls/utilclstest.py @@ -15,8 +15,8 @@ BeartypeDecorHintPep3119Exception, _BeartypeUtilTypeException, ) -from beartype._util.data.cls.datacls import TYPES_BUILTIN_FAKE -from beartype._util.data.mod.datamod import BUILTINS_MODULE_NAME +from beartype._data.cls.datacls import TYPES_BUILTIN_FAKE +from beartype._data.mod.datamod import BUILTINS_MODULE_NAME from typing import Type # ....................{ VALIDATORS }.................... diff --git a/beartype/_util/data/hint/pep/proposal/datapep484.py b/beartype/_util/data/hint/pep/proposal/datapep484.py deleted file mode 100644 index e54b2089..00000000 --- a/beartype/_util/data/hint/pep/proposal/datapep484.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python3 -# --------------------( LICENSE )-------------------- -# Copyright (c) 2014-2021 Beartype authors. -# See "LICENSE" for further details. - -''' -Project-wide :pep:`484`-compliant **type hint data.** - -This private submodule is *not* intended for importation by downstream callers. -''' - -# ....................{ IMPORTS }.................... -import typing -from beartype._util.py.utilpyversion import IS_PYTHON_AT_LEAST_3_7 -from typing import Tuple - -# See the "beartype.cave" submodule for further commentary. -__all__ = ['STAR_IMPORTS_CONSIDERED_HARMFUL'] - -# ....................{ HINTS }.................... -HINT_PEP484_TUPLE_EMPTY = Tuple[()] -''' -:pep:`484`-compliant empty fixed-length tuple type hint. -''' - - -# Conditionally add the "typing.ForwardRef" superclass depending on the -# current Python version, as this superclass was thankfully publicized -# under Python >= 3.7 after its initial privatization under Python <= 3.6. -HINT_PEP484_TYPE_FORWARDREF = ( - typing.ForwardRef if IS_PYTHON_AT_LEAST_3_7 else - typing._ForwardRef # type: ignore [attr-defined] -) -''' -**Forward reference sign** (i.e., arbitrary objects uniquely identifying a -:pep:`484`-compliant type hint unifying one or more subscripted type hint -arguments into a disjunctive set union of these arguments). -''' diff --git a/beartype/_util/data/hint/pep/proposal/datapep585.py b/beartype/_util/data/hint/pep/proposal/datapep585.py deleted file mode 100644 index 30f1cf92..00000000 --- a/beartype/_util/data/hint/pep/proposal/datapep585.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env python3 -# --------------------( LICENSE )-------------------- -# Copyright (c) 2014-2021 Beartype authors. -# See "LICENSE" for further details. - -''' -**Beartype :pep:`585`**-compliant type hint data.** - -This private submodule is *not* intended for importation by downstream callers. - -.. _PEP 585: - https://www.python.org/dev/peps/pep-0585 -''' - -# ....................{ IMPORTS }.................... -from beartype._util.py.utilpyversion import IS_PYTHON_AT_LEAST_3_9 -from beartype._util.utilobject import Iota - -# See the "beartype.cave" submodule for further commentary. -__all__ = ['STAR_IMPORTS_CONSIDERED_HARMFUL'] - -# ....................{ HINTS }.................... -HINT_PEP585_TUPLE_EMPTY = ( - tuple[()] if IS_PYTHON_AT_LEAST_3_9 else Iota()) # type: ignore[misc] -''' -:pep:`585`-compliant empty fixed-length tuple type hint if the active Python -interpreter supports at least Python 3.9 and thus :pep:`585` *or* a unique -placeholder object otherwise to guarantee failure when comparing arbitrary -objects against this object via equality tests. -''' diff --git a/beartype/_util/data/mod/__init__.py b/beartype/_util/data/mod/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/beartype/_util/hint/pep/mod/utilmodnumpy.py b/beartype/_util/hint/pep/mod/utilmodnumpy.py index 2d214553..864d0e7b 100644 --- a/beartype/_util/hint/pep/mod/utilmodnumpy.py +++ b/beartype/_util/hint/pep/mod/utilmodnumpy.py @@ -21,7 +21,7 @@ from beartype.roar import BeartypeDecorHintNonPepNumPyException from beartype.vale import IsAttr, IsEqual # from beartype._util.cache.utilcachecall import callable_cached -from beartype._util.data.hint.pep.sign.datapepsigns import HintSignNumpyArray +from beartype._data.hint.pep.sign.datapepsigns import HintSignNumpyArray from beartype._util.mod.utilmodimport import import_module_typing_any_attr from beartype._util.hint.pep.utilpepget import ( get_hint_pep_args, diff --git a/beartype/_util/hint/pep/proposal/utilhintpep484.py b/beartype/_util/hint/pep/proposal/utilpep484.py similarity index 88% rename from beartype/_util/hint/pep/proposal/utilhintpep484.py rename to beartype/_util/hint/pep/proposal/utilpep484.py index 76a638c2..243b572e 100644 --- a/beartype/_util/hint/pep/proposal/utilhintpep484.py +++ b/beartype/_util/hint/pep/proposal/utilpep484.py @@ -10,28 +10,49 @@ ''' # ....................{ IMPORTS }.................... +import typing from beartype.roar import ( BeartypeDecorHintForwardRefException, BeartypeDecorHintPep484Exception, ) from beartype._util.cache.utilcachecall import callable_cached -from beartype._util.data.hint.pep.proposal.datapep484 import ( - HINT_PEP484_TYPE_FORWARDREF, -) -from beartype._util.data.hint.pep.sign.datapepsigncls import HintSign -from beartype._util.data.hint.pep.sign.datapepsigns import ( +from beartype._data.hint.pep.sign.datapepsigncls import HintSign +from beartype._data.hint.pep.sign.datapepsigns import ( HintSignGeneric, HintSignNewType, ) -from beartype._util.data.hint.pep.sign.datapepsignset import HINT_SIGNS_UNION -from beartype._util.py.utilpyversion import IS_PYTHON_AT_LEAST_3_7 +from beartype._data.hint.pep.sign.datapepsignset import HINT_SIGNS_UNION +from beartype._util.py.utilpyversion import ( + IS_PYTHON_AT_LEAST_3_10, + IS_PYTHON_AT_LEAST_3_7, +) from beartype._util.utilobject import is_object_subclass from types import FunctionType -from typing import Any, Generic, Optional +from typing import Any, Generic, Optional, Tuple # See the "beartype.cave" submodule for further commentary. __all__ = ['STAR_IMPORTS_CONSIDERED_HARMFUL'] +# ....................{ HINTS }.................... +HINT_PEP484_TUPLE_EMPTY = Tuple[()] +''' +:pep:`484`-compliant empty fixed-length tuple type hint. +''' + + +# Conditionally define the "typing.ForwardRef" superclass depending on the +# current Python version. This superclass was thankfully publicized under +# Python >= 3.7 after its initial privatization under Python <= 3.6. +HINT_PEP484_TYPE_FORWARDREF = ( + typing.ForwardRef if IS_PYTHON_AT_LEAST_3_7 else + typing._ForwardRef # type: ignore [attr-defined] +) +''' +**Forward reference sign** (i.e., arbitrary objects uniquely identifying a +:pep:`484`-compliant type hint unifying one or more subscripted type hint +arguments into a disjunctive set union of these arguments). +''' + # ....................{ TESTERS ~ ignorable }.................... def is_hint_pep484_ignorable_or_none( hint: object, hint_sign: HintSign) -> Optional[bool]: @@ -117,9 +138,6 @@ def noop(param_hint_ignorable: Generic[T]) -> T: pass # print(f'Testing PEP 484 hint {repr(hint)} [{repr(hint_sign)}] deep ignorability...') # If this hint is a PEP 484-compliant generic... - # - # Note that the "beartype._util.data.hint.pep.proposal.datapep484" - # submodule already ignores the unsubscripted "typing.Generic" ABC itself. if hint_sign is HintSignGeneric: # print(f'Testing generic hint {repr(hint)} deep ignorability...') # If this generic is the "typing.Generic" superclass directly @@ -361,8 +379,58 @@ class superficially subclassing at least one non-class PEP-compliant ''' # ....................{ TESTERS ~ newtype }.................... -def is_hint_pep484_newtype(hint: object) -> bool: - ''' +# If the active Python interpreter targets Python >= 3.10 and thus defines +# "typing.NewType" type hints as instances of that class, implement this tester +# unique to prior Python versions to raise an exception. +if IS_PYTHON_AT_LEAST_3_10: + def is_hint_pep484_newtype_pre_python3_10(hint: object) -> bool: + raise BeartypeDecorHintPep484Exception( + 'is_hint_pep484_newtype_pre_python3_10() assumes Python < 3.10, ' + 'but current Python interpreter targets Python >= 3.10.' + ) +# Else, the active Python interpreter targets Python < 3.10 and thus defines +# "typing.NewType" type hints as closures returned by that function. Since +# these closures are sufficiently dissimilar from all other type hints to +# require unique detection, implement this tester unique to this obsolete +# Python version to detect these closures. +else: + def is_hint_pep484_newtype_pre_python3_10(hint: object) -> bool: + + # Return true only if... + return ( + # This hint is a pure-Python function *AND*... + # + # Note that we intentionally do *NOT* call the callable() builtin + # here, as that builtin erroneously returns false positives for + # non-standard classes defining the __call__() dunder method to + # unconditionally raise exceptions. Critically, this includes most + # PEP 484-compliant type hints, which naturally fail to define both + # the "__module__" *AND* "__qualname__" dunder instance variables + # accessed below. Shoot me now, fam. + isinstance(hint, FunctionType) and + # This callable is a closure created and returned by the + # typing.NewType() function. Note that: + # + # * The "__module__" and "__qualname__" dunder instance variables + # are *NOT* generally defined for arbitrary objects but are + # specifically defined for callables. + # * "__qualname__" is safely available under Python >= 3.3. + # * This test derives from the observation that the concatenation + # of this callable's "__qualname__" and "__module" dunder + # instance variables suffices to produce a string unambiguously + # identifying whether this hint is a "NewType"-generated closure: + # >>> from typing import NewType + # >>> UserId = t.NewType('UserId', int) + # >>> UserId.__qualname__ + # >>> 'NewType..new_type' + # >>> UserId.__module__ + # >>> 'typing' + f'{hint.__module__}.{hint.__qualname__}'.startswith( + 'typing.NewType.') + ) + + +is_hint_pep484_newtype_pre_python3_10.__doc__ = ''' ``True`` only if the passed object either is a :pep:`484`-compliant **new type** (i.e., closure created and returned by the :func:`typing.NewType` closure factory function). @@ -396,39 +464,6 @@ def is_hint_pep484_newtype(hint: object) -> bool: ``True`` only if this object is a :pep:`484`-compliant new type. ''' - # Return true only if... - return ( - # This hint is a pure-Python function *AND*... - # - # Note that we intentionally do *NOT* call the callable() builtin here, - # as that builtin erroneously returns false positives for non-standard - # classes defining the __call__() dunder method to unconditionally - # raise exceptions. Critically, this includes most PEP 484-compliant - # type hints, which naturally fail to define both the "__module__" - # *AND* "__qualname__" dunder instance variables accessed below. - # - # Shoot me now, fam. - isinstance(hint, FunctionType) and - # This callable is a closure created and returned by the - # typing.NewType() function. Note that: - # - # * The "__module__" and "__qualname__" dunder instance variables are - # *NOT* generally defined for arbitrary objects but are specifically - # defined for callables. - # * "__qualname__" is safely available under Python >= 3.3. - # * This test derives from the observation that the concatenation of - # this callable's "__qualname__" and "__module" dunder instance - # variables suffices to produce a string unambiguously identifying - # whether this hint is a "NewType"-generated closure: e.g., - # >>> from typing import NewType - # >>> UserId = t.NewType('UserId', int) - # >>> UserId.__qualname__ - # >>> 'NewType..new_type' - # >>> UserId.__module__ - # >>> 'typing' - f'{hint.__module__}.{hint.__qualname__}'.startswith('typing.NewType.') - ) - # ....................{ GETTERS ~ forwardref }.................... @callable_cached def get_hint_pep484_forwardref_type_basename(hint: Any) -> str: @@ -515,12 +550,15 @@ def get_hint_pep484_newtype_class(hint: Any) -> type: Further commentary. ''' - # If this object is *NOT* a PEP 484-compliant new type hint, raise an + # Avoid circular import dependencies. + from beartype._util.hint.pep.utilpepget import get_hint_pep_sign + + # If this object is *NOT* a PEP 484-compliant "NewType" hint, raise an # exception. - if not is_hint_pep484_newtype(hint): + if get_hint_pep_sign(hint) is not HintSignNewType: raise BeartypeDecorHintPep484Exception( f'Type hint {repr(hint)} not "typing.NewType".') - # Else, this object is a PEP 484-compliant new type hint. + # Else, this object is a PEP 484-compliant "NewType" hint. # Return the unqualified classname referred to by this reference. Note # that this requires violating privacy encapsulation by accessing a dunder diff --git a/beartype/_util/hint/pep/proposal/utilhintpep544.py b/beartype/_util/hint/pep/proposal/utilpep544.py similarity index 98% rename from beartype/_util/hint/pep/proposal/utilhintpep544.py rename to beartype/_util/hint/pep/proposal/utilpep544.py index efdcdaf6..8b99f140 100644 --- a/beartype/_util/hint/pep/proposal/utilhintpep544.py +++ b/beartype/_util/hint/pep/proposal/utilpep544.py @@ -12,7 +12,7 @@ # ....................{ IMPORTS }.................... from abc import abstractmethod from beartype.roar import BeartypeDecorHintPep544Exception -from beartype._util.data.hint.pep.sign.datapepsigncls import HintSign +from beartype._data.hint.pep.sign.datapepsigncls import HintSign from beartype._util.cls.utilclstest import is_type_builtin from beartype._util.py.utilpyversion import IS_PYTHON_AT_LEAST_3_8 from beartype._util.utilobject import is_object_subclass @@ -226,7 +226,7 @@ def noop(param_hint_ignorable: Protocol[T]) -> T: pass the :mod:`typing` module effectively unusable at runtime due to botched implementation details) that is losslessly replaceable with a useful :pep:`544`-compliant :mod:`beartype` **IO protocol** (i.e., either - :class:`beartype._util.data.hint.pep.proposal.datapep544._Pep544IO` itself + :class:`beartype._data.hint.pep.proposal.datapep544._Pep544IO` itself *or* a subclass of that class defined by this submodule intentionally designed to be usable at runtime). @@ -247,7 +247,7 @@ def noop(param_hint_ignorable: Protocol[T]) -> T: pass See Also ---------- - :class:`beartype._util.data.hint.pep.proposal.datapep544._Pep544IO` + :class:`beartype._data.hint.pep.proposal.datapep544._Pep544IO` Further commentary. ''' @@ -275,7 +275,7 @@ def noop(param_hint_ignorable: Protocol[T]) -> T: pass def reduce_hint_pep484_generic_io_to_pep544_protocol(hint: type) -> type: ''' :pep:`544`-compliant :mod:`beartype` **IO protocol** (i.e., either - :class:`beartype._util.data.hint.pep.proposal.datapep544._Pep544IO` + :class:`beartype._data.hint.pep.proposal.datapep544._Pep544IO` itself *or* a subclass of that class defined by this submodule intentionally designed to be usable at runtime) corresponding to the passed :pep:`484`-compliant :mod:`typing` **IO generic base class** (i.e., either @@ -306,7 +306,7 @@ def reduce_hint_pep484_generic_io_to_pep544_protocol(hint: type) -> type: See Also ---------- - :class:`beartype._util.data.hint.pep.proposal.datapep544._Pep544IO` + :class:`beartype._data.hint.pep.proposal.datapep544._Pep544IO` Further commentary. ''' diff --git a/beartype/_util/hint/pep/proposal/utilhintpep585.py b/beartype/_util/hint/pep/proposal/utilpep585.py similarity index 95% rename from beartype/_util/hint/pep/proposal/utilhintpep585.py rename to beartype/_util/hint/pep/proposal/utilpep585.py index 00c50ea6..f713026d 100644 --- a/beartype/_util/hint/pep/proposal/utilhintpep585.py +++ b/beartype/_util/hint/pep/proposal/utilpep585.py @@ -14,11 +14,22 @@ from beartype._cave._cavefast import HintGenericSubscriptedType from beartype._util.cache.utilcachecall import callable_cached from beartype._util.py.utilpyversion import IS_PYTHON_AT_LEAST_3_9 +from beartype._util.utilobject import Iota from typing import Any, Set, Tuple # See the "beartype.cave" submodule for further commentary. __all__ = ['STAR_IMPORTS_CONSIDERED_HARMFUL'] +# ....................{ HINTS }.................... +HINT_PEP585_TUPLE_EMPTY = ( + tuple[()] if IS_PYTHON_AT_LEAST_3_9 else Iota()) # type: ignore[misc] +''' +:pep:`585`-compliant empty fixed-length tuple type hint if the active Python +interpreter supports at least Python 3.9 and thus :pep:`585` *or* a unique +placeholder object otherwise to guarantee failure when comparing arbitrary +objects against this object via equality tests. +''' + # ....................{ VALIDATORS }.................... def die_unless_hint_pep585_generic(hint: object) -> None: ''' diff --git a/beartype/_util/hint/pep/proposal/utilhintpep586.py b/beartype/_util/hint/pep/proposal/utilpep586.py similarity index 98% rename from beartype/_util/hint/pep/proposal/utilhintpep586.py rename to beartype/_util/hint/pep/proposal/utilpep586.py index fe2134a3..461b794c 100644 --- a/beartype/_util/hint/pep/proposal/utilhintpep586.py +++ b/beartype/_util/hint/pep/proposal/utilpep586.py @@ -12,7 +12,7 @@ # ....................{ IMPORTS }.................... from beartype.roar import BeartypeDecorHintPep586Exception from beartype._cave._cavefast import EnumMemberType, NoneType -from beartype._util.data.hint.pep.sign.datapepsigns import HintSignLiteral +from beartype._data.hint.pep.sign.datapepsigns import HintSignLiteral from beartype._util.text.utiltextjoin import join_delimited_disjunction_classes from typing import Any diff --git a/beartype/_util/hint/pep/proposal/utilhintpep593.py b/beartype/_util/hint/pep/proposal/utilpep593.py similarity index 98% rename from beartype/_util/hint/pep/proposal/utilhintpep593.py rename to beartype/_util/hint/pep/proposal/utilpep593.py index 9b85ba2c..84d242f8 100644 --- a/beartype/_util/hint/pep/proposal/utilhintpep593.py +++ b/beartype/_util/hint/pep/proposal/utilpep593.py @@ -11,8 +11,8 @@ # ....................{ IMPORTS }.................... from beartype.roar import BeartypeDecorHintPep593Exception -from beartype._util.data.hint.pep.sign.datapepsigncls import HintSign -from beartype._util.data.hint.pep.sign.datapepsigns import HintSignAnnotated +from beartype._data.hint.pep.sign.datapepsigncls import HintSign +from beartype._data.hint.pep.sign.datapepsigns import HintSignAnnotated from beartype._vale._valesub import _SubscriptedIs from typing import Any, Optional, Tuple diff --git a/beartype/_util/hint/pep/utilpepget.py b/beartype/_util/hint/pep/utilpepget.py index 51b1fac3..ce3bc989 100644 --- a/beartype/_util/hint/pep/utilpepget.py +++ b/beartype/_util/hint/pep/utilpepget.py @@ -19,29 +19,30 @@ BeartypeDecorHintPepSignException, ) from beartype._util.cache.utilcachecall import callable_cached -from beartype._util.data.hint.pep.datapeprepr import ( +from beartype._data.hint.pep.datapeprepr import ( HINT_REPR_PREFIX_ARGS_0_OR_MORE_TO_SIGN, HINT_REPR_PREFIX_ARGS_1_OR_MORE_TO_SIGN, HINT_TYPE_NAME_TO_SIGN, ) -from beartype._util.data.hint.pep.sign.datapepsigncls import HintSign -from beartype._util.data.hint.pep.sign.datapepsigns import ( +from beartype._data.hint.pep.sign.datapepsigncls import HintSign +from beartype._data.hint.pep.sign.datapepsigns import ( HintSignGeneric, HintSignNewType, ) -from beartype._util.data.hint.pep.sign.datapepsignset import ( +from beartype._data.hint.pep.sign.datapepsignset import ( HINT_SIGNS_TYPE_STDLIB, ) -from beartype._util.hint.pep.proposal.utilhintpep484 import ( +from beartype._util.hint.pep.proposal.utilpep484 import ( get_hint_pep484_generic_bases_unerased, - is_hint_pep484_newtype, + is_hint_pep484_newtype_pre_python3_10, ) -from beartype._util.hint.pep.proposal.utilhintpep585 import ( +from beartype._util.hint.pep.proposal.utilpep585 import ( get_hint_pep585_generic_bases_unerased, get_hint_pep585_generic_typevars, is_hint_pep585_generic, ) from beartype._util.py.utilpyversion import ( + IS_PYTHON_AT_MOST_3_9, IS_PYTHON_AT_LEAST_3_9, IS_PYTHON_AT_LEAST_3_7, ) @@ -341,7 +342,7 @@ def get_hint_pep_sign(hint: Any) -> HintSign: # dramatically simpler approach, we no longer require the excessive glut of # PEP-specific testers we previously required. #* Merge the contents of all now minimal -# "beartype._util.data.hint.pep.proposal.*" submodules into their parent +# "beartype._data.hint.pep.proposal.*" submodules into their parent # "beartype._util.hint.pep.proposal.*" submodules. There's no longer any # demonstrable benefit to separating the two, so please cease doing so. @callable_cached @@ -433,6 +434,7 @@ class like :class:`list` or :class:`tuple` *or* an abstract base class typing.Iterable ''' + # ..................{ IMPORTS }.................. # Avoid circular import dependencies. from beartype._util.hint.pep.utilpeptest import is_hint_pep_generic @@ -445,6 +447,42 @@ class like :class:`list` or :class:`tuple` *or* an abstract base class # underlying all higher-level hint validation functions! Calling the latter # here would thus induce infinite recursion, which would be very bad. # + # ..................{ PHASE ~ classname }.................. + # This phase attempts to map from the fully-qualified classname of this + # hint to a sign identifying *ALL* hints that are instances of that class. + # + # Since the "object.__class__.__qualname__" attribute is both guaranteed to + # exist and be efficiently accessible for all hints, this phase is the + # fastest and thus performed first. Although this phase identifies only a + # small subset of hints, those hints are extremely common. + # + # More importantly, some of these classes are implemented as maliciously + # masquerading as other classes entirely -- including __repr__() methods + # synthesizing erroneous machine-readable representations. To avoid false + # positives, this phase *MUST* thus be performed before repr()-based tests + # regardless of efficiency concerns: e.g., + # # Under Python >= 3.10: + # >>> import typing + # >>> bad_guy_type_hint = typing.NewType('List', bool) + # >>> bad_guy_type_hint.__module__ = 'typing' + # >>> repr(bad_guy_type_hint) + # typing.List # <---- this is genuine bollocks + + # Class of this hint. + hint_type = hint.__class__ + + # Fully-qualified name of this class. + hint_type_name = f'{hint_type.__module__}.{hint_type.__qualname__}' + + # Sign identifying this hint if this hint is identifiable by its classname + # *OR* "None" otherwise. + hint_sign = HINT_TYPE_NAME_TO_SIGN.get(hint_type_name) + + # If this hint is identifiable by its classname, return this sign. + if hint_sign is not None: + return hint_sign + # Else, this hint is *NOT* identifiable by its classname. + # ..................{ PHASE ~ repr }.................. # This phase attempts to map from the unsubscripted machine-readable # representation of this hint to a sign identifying *ALL* hints of that @@ -494,30 +532,6 @@ class like :class:`list` or :class:`tuple` *or* an abstract base class # representation. # Else, this representation (and thus this hint) is unsubscripted. - # ..................{ PHASE ~ classname }.................. - # This phase attempts to map from the fully-qualified classname of this - # hint to a sign identifying *ALL* hints that are instances of that class. - # - # Since the "object.__class__.__qualname__" attribute is both guaranteed to - # exist and efficiently accessible for all hints, this phase is the fastest - # and thus performed first. Although this phase identifies only a small - # subset of hints, those hints are extremely common. - - # Class of this hint. - hint_type = hint.__class__ - - # Fully-qualified name of this class. - hint_type_name = f'{hint_type.__module__}.{hint_type.__qualname__}' - - # Sign identifying this hint if this hint is identifiable by its classname - # *OR* "None" otherwise. - hint_sign = HINT_TYPE_NAME_TO_SIGN.get(hint_type_name) - - # If this hint is identifiable by its classname, return this sign. - if hint_sign is not None: - return hint_sign - # Else, this hint is *NOT* identifiable by its classname. - # ..................{ PHASE ~ manual }.................. # This phase attempts to manually identify the signs of all hints *NOT* # efficiently identifiably by the prior phases. @@ -551,59 +565,24 @@ class like :class:`list` or :class:`tuple` *or* an abstract base class # Ergo, the "typing.Generic" ABC uniquely identifies many but *NOT* all # generics. While non-ideal, the failure of PEP 585-compliant generics to # subclass a common superclass leaves us with little alternative. - elif is_hint_pep_generic(hint): + if is_hint_pep_generic(hint): return HintSignGeneric # Else, this hint is *NOT* a PEP 484- or 585-compliant generic. # - # If this hint is a PEP 484-compliant new type, return that sign. + # If the active Python interpreter targets Python < 3.10 (and thus defines + # PEP 484-compliant "NewType" type hints as closures returned by that + # function that are sufficiently dissimilar from all other type hints to + # require unique detection) *AND* this hint is such a hint, return the + # corresponding sign. # - # Note that these types *CANNOT* be detected by the general-purpose logic + # Note that these hints *CANNOT* be detected by the general-purpose logic # performed above, as the __repr__() dunder methods of the closures created # and returned by the NewType() closure factory function returns a standard # representation rather than string prefixed by "typing.": e.g., # >>> import typing as t # >>> repr(t.NewType('FakeStr', str)) # '.new_type at 0x7fca39388050>' - - #FIXME: *UGH.* Python 3.10 has yet again fundamentally broken the public - #API of a "typing" type. The "typing.NewType" attribute is now a class - #rather than a function. That's fine. What is *NOT* fine is that this class - #now attempts to masquerade as its underlying type, which makes detection - #extremely problematic: - # >>> import typing as t - # >>> nt = t.NewType('TotallyNotAStr', str) - # >>> repr(nt) - # '__main__.TotallyNotAStr' - # - #In a way, this is better, because the new "typing.NewType" implementation - #is easier to detect -- *MUCH* easier. Still, it's a huge pain, because we - #now need to two fundamentally divergent implementations as follows: - #* Rename the is_hint_pep484_newtype() function to - # is_hint_pep484_newtype_obsolete(). - #* In that function, raise an exception if IS_PYTHON_AT_LEAST_3_10. - #* Refactor the existing call to that function in "utilhintpep484" to - # instead call this function and compare the resulting sign to - # "HintSignNewType". - #* Guard the call to is_hint_pep484_newtype_obsolete() right here with a - # Python version guard resembling: - # elif IS_PYTHON_AT_LEAST_3_9 and is_hint_pep484_newtype_obsolete(hint): - #* Add new "HINT_TYPE_NAME_TO_SIGN" entries for both "typing.NewType" and - # "typing_extensions.NewType" under Python >= 3.10. Actually... maybe only - # "typing.NewType". How does "typing_extensions" implement that? *sigh* - # - #That should do it. Still. Super-super annoying, guys. - #FIXME: While that *WOULD* technically work, maybe, we'll probably get - #issues with "typing_extensions.NewType". Basically, we instead just want - #to generalize this is_hint_pep484_newtype() tester to handle both types of - #"NewType" objects without reference to the current Python version, which - #isn't really a reliable way of distinguishing these types: e.g., - # isinstance(hint, type) and hint.__name__ == 'NewType' # <-- trivial! - #Also, we still need to do this: - #* Add new "HINT_TYPE_NAME_TO_SIGN" entries for both "typing.NewType" and - # "typing_extensions.NewType". Don't worry about the Python version here. - #FIXME; Likewise, we'll probably need to refactor the - #get_hint_pep484_newtype_class() getter, too. - elif is_hint_pep484_newtype(hint): + elif IS_PYTHON_AT_MOST_3_9 and is_hint_pep484_newtype_pre_python3_10(hint): return HintSignNewType # ..................{ ERROR }.................. diff --git a/beartype/_util/hint/pep/utilpeptest.py b/beartype/_util/hint/pep/utilpeptest.py index 422cb1eb..01bc53d5 100644 --- a/beartype/_util/hint/pep/utilpeptest.py +++ b/beartype/_util/hint/pep/utilpeptest.py @@ -19,35 +19,35 @@ ) from beartype._cave._cavefast import HintGenericSubscriptedType from beartype._util.cache.utilcachecall import callable_cached -from beartype._util.data.hint.pep.datapeprepr import ( - HINTS_REPR_PREFIX_DEPRECATED, +from beartype._data.hint.pep.datapeprepr import ( HINTS_PEP484_REPR_PREFIX_DEPRECATED, + HINTS_REPR_PREFIX_DEPRECATED, ) -from beartype._util.data.hint.pep.proposal.datapep484 import ( - HINT_PEP484_TUPLE_EMPTY) -from beartype._util.data.hint.pep.proposal.datapep585 import ( - HINT_PEP585_TUPLE_EMPTY) -from beartype._util.data.hint.pep.sign.datapepsigncls import HintSign -from beartype._util.data.hint.pep.sign.datapepsigns import ( +from beartype._data.hint.pep.sign.datapepsigncls import HintSign +from beartype._data.hint.pep.sign.datapepsigns import ( HintSignTypeVar, ) -from beartype._util.data.hint.pep.sign.datapepsignset import ( - HINT_SIGNS_SUPPORTED) -from beartype._util.data.mod.datamod import ( +from beartype._data.hint.pep.sign.datapepsignset import ( + HINT_SIGNS_SUPPORTED, + HINT_SIGNS_TYPE_MIMIC, +) +from beartype._data.mod.datamod import ( TYPING_MODULE_NAMES, TYPING_MODULE_NAMES_DOTTED, ) -from beartype._util.hint.pep.proposal.utilhintpep484 import ( +from beartype._util.hint.pep.proposal.utilpep484 import ( + HINT_PEP484_TUPLE_EMPTY, is_hint_pep484_generic, is_hint_pep484_ignorable_or_none, ) -from beartype._util.hint.pep.proposal.utilhintpep544 import ( +from beartype._util.hint.pep.proposal.utilpep544 import ( is_hint_pep544_ignorable_or_none) -from beartype._util.hint.pep.proposal.utilhintpep585 import ( +from beartype._util.hint.pep.proposal.utilpep585 import ( + HINT_PEP585_TUPLE_EMPTY, is_hint_pep585_builtin, is_hint_pep585_generic, ) -from beartype._util.hint.pep.proposal.utilhintpep593 import ( +from beartype._util.hint.pep.proposal.utilpep593 import ( is_hint_pep593_ignorable_or_none) from beartype._util.mod.utilmodule import ( get_object_module_name, @@ -719,8 +719,8 @@ def is_hint_pep_supported(hint: object) -> bool: # ....................{ TESTERS ~ typing }.................... #FIXME: Replace all hardcoded "'typing" strings throughout the codebase with #access of "TYPING_MODULE_NAMES" instead. We only see two remaining in: -#* beartype/_util/hint/pep/proposal/utilhintpep544.py -#* beartype/_util/hint/pep/proposal/utilhintpep484.py +#* beartype/_util/hint/pep/proposal/utilpep544.py +#* beartype/_util/hint/pep/proposal/utilpep484.py #Thankfully, nobody really cares about generalizing these two edge cases to #"testing_extensions", so they're mostly fine for various definitions of fine. @callable_cached @@ -745,51 +745,19 @@ def is_hint_pep_typing(hint: object) -> bool: ''' # print(f'is_hint_pep_typing({repr(hint)}') - # If this is any PEP-compliant type hint defined by a typing module (except - # PEP 593-compliant type hints), return true. - if get_object_module_name_or_none(hint) in TYPING_MODULE_NAMES: - # print(f'typing hint: {repr(hint)}') - return True - # Else, this is either a PEP 593-compliant type hint (e.g., - # "typing.Annotated[str, int]") *OR* not a PEP-compliant hint at all. - - # Parse the machine-readable representation of this hint into: - # * "hint_repr_prefix", the substring of this representation preceding the - # first "[" delimiter if this representation contains that delimiter *OR* - # this representation as is otherwise. - # * "hint_repr_subscripted", the "[" delimiter if this representation - # contains that delimiter *OR* the empty string otherwise. - # - # Note that the str.partition() method has been profiled to be the - # optimally efficient means of parsing trivial prefixes like these. - hint_repr_prefix, hint_repr_subscripted, _ = repr(hint).partition('[') - - # If this hint is subscripted... - if hint_repr_subscripted: - # print(f'hint repr: {repr(hint)}') - # Return true only if the machine-readable representation of this hint - # is prefixed by a string indicative of a typing module. - # - # This is specifically required for PEP 593-compliant type hints. For - # inexplicable (and presumably indefensible) reasons, these hints badly - # masquerade as their first subscripted PEP-compliant type hints (e.g., - # the "int" in "typing.Annotated[int, 63]"). Ergo, the value of the - # "__module__" attribute of this hint is that of its first subscripted - # PEP-compliant type hint rather than its own. Nonetheless, its - # machine-readable representation remains prefixed by "typing.", - # enabling an efficient test that also generalizes to all other outlier - # edge cases that are probably lurking about. - # - # I have no code and I must scream. - return any( - hint_repr_prefix.startswith(typing_module_name_dotted) - for typing_module_name_dotted in TYPING_MODULE_NAMES_DOTTED - ) - # Else, this hint is unsubscripted, in which case this hint *CANNOT* by - # definition be a PEP 593-compliant type hint. By the above syllogism, this - # hint *MUST* thus be PEP-noncompliant. In this case, return false. + # Avoid circular import dependencies. + from beartype._util.hint.pep.utilpepget import ( + get_hint_pep_sign_or_none) - return False + # Return true only if this hint is either... + return ( + # Any PEP-compliant type hint defined by a typing module (except those + # maliciously masquerading as another type entirely) *OR*... + get_object_module_name_or_none(hint) in TYPING_MODULE_NAMES or + # Any PEP-compliant type hint defined by a typing module maliciously + # masquerading as another type entirely. + get_hint_pep_sign_or_none(hint) in HINT_SIGNS_TYPE_MIMIC + ) # If the active Python interpreter targets at least Python 3.7 and is thus @@ -804,13 +772,12 @@ def is_hint_pep_type_typing(hint: object) -> bool: # Return true only if this type is defined by a typing module. # # Note that this implementation could probably be reduced to the - # leading portion of the body of the get_hint_pep_sign() function - # testing this object's representation. While certainly more compact - # and convenient than the current approach, that refactored approach - # would also be considerably more fragile, failure-prone, and subject - # to whimsical "improvements" in the already overly hostile "typing" - # API. Why? Because the get_hint_pep_sign() function: - # + # leading portion of the body of the get_hint_pep_sign_or_none() + # function testing this object's representation. While certainly more + # compact and convenient than the current approach, that refactored + # approach would also be considerably more fragile, failure-prone, and + # subject to whimsical "improvements" in the already overly hostile + # "typing" API. Why? Because the get_hint_pep_sign_or_none() function: # * Parses the machine-readable string returned by the __repr__() # dunder method of "typing" types. Since that string is *NOT* # standardized by PEP 484 or any other PEP, "typing" authors remain @@ -829,7 +796,6 @@ def is_hint_pep_type_typing(hint: object) -> bool: # against whimsical destruction by "typing" authors. Note that there # might exist an alternate means of deciding this boolean, documented # here merely for completeness: - # # try: # isinstance(obj, object) # return False @@ -840,11 +806,10 @@ def is_hint_pep_type_typing(hint: object) -> bool: # The above effectively implements an Aikido throw by using the fact # that "typing" types prohibit isinstance() calls against those types. # While clever (and deliciously obnoxious), the above logic: - # # * Requires catching exceptions in the common case and is thus *MUCH* # less efficient than the preferable approach implemented here. # * Assumes that *ALL* "typing" types prohibit such calls. Sadly, only - # a proper subset of such types prohibit such calls. + # a proper subset of these types prohibit such calls. # * Assumes that those "typing" types that do prohibit such calls raise # exceptions with reliable messages across *ALL* Python versions. # diff --git a/beartype/_util/hint/utilhintget.py b/beartype/_util/hint/utilhintget.py index 9ed4df32..68b6e6e2 100644 --- a/beartype/_util/hint/utilhintget.py +++ b/beartype/_util/hint/utilhintget.py @@ -13,7 +13,7 @@ # ....................{ IMPORTS }.................... from beartype._cave._cavefast import NoneType -from beartype._util.data.hint.pep.sign.datapepsigns import ( +from beartype._data.hint.pep.sign.datapepsigns import ( HintSignAnnotated, HintSignNewType, HintSignNumpyArray, @@ -77,7 +77,7 @@ def get_hint_reduced( # Avoid circular import dependencies. from beartype._util.hint.pep.utilpepget import get_hint_pep_sign_or_none - from beartype._util.hint.pep.proposal.utilhintpep544 import ( + from beartype._util.hint.pep.proposal.utilpep544 import ( reduce_hint_pep484_generic_io_to_pep544_protocol, is_hint_pep484_generic_io, ) @@ -112,7 +112,7 @@ def get_hint_reduced( # validation API, metahints are extremely common and thus detected next. elif hint_sign is HintSignAnnotated: # Avoid circular import dependencies. - from beartype._util.hint.pep.proposal.utilhintpep593 import ( + from beartype._util.hint.pep.proposal.utilpep593 import ( get_hint_pep593_metahint, is_hint_pep593_beartype, ) @@ -146,7 +146,7 @@ def get_hint_reduced( # thus fairly rare in the wild. Ergo, detect these late. elif hint_sign is HintSignNewType: # Avoid circular import dependencies. - from beartype._util.hint.pep.proposal.utilhintpep484 import ( + from beartype._util.hint.pep.proposal.utilpep484 import ( get_hint_pep484_newtype_class) hint = get_hint_pep484_newtype_class(hint) # ..................{ PEP 484 ~ io }.................. @@ -215,7 +215,7 @@ class that typically has yet to be defined). # Avoid circular import dependencies. from beartype._util.hint.utilhinttest import die_unless_hint_forwardref - from beartype._util.hint.pep.proposal.utilhintpep484 import ( + from beartype._util.hint.pep.proposal.utilpep484 import ( get_hint_pep484_forwardref_type_basename, is_hint_pep484_forwardref, ) diff --git a/beartype/_util/hint/utilhinttest.py b/beartype/_util/hint/utilhinttest.py index ad9eaa04..f2e22093 100644 --- a/beartype/_util/hint/utilhinttest.py +++ b/beartype/_util/hint/utilhinttest.py @@ -22,8 +22,8 @@ is_hint_pep, is_hint_pep_supported, ) -from beartype._util.data.hint.datahint import HINT_BASES_FORWARDREF -from beartype._util.data.hint.pep.datapeprepr import HINTS_REPR_IGNORABLE_SHALLOW +from beartype._data.hint.datahint import HINT_BASES_FORWARDREF +from beartype._data.hint.pep.datapeprepr import HINTS_REPR_IGNORABLE_SHALLOW # See the "beartype.cave" submodule for further commentary. __all__ = ['STAR_IMPORTS_CONSIDERED_HARMFUL'] diff --git a/beartype/_util/mod/utilmodimport.py b/beartype/_util/mod/utilmodimport.py index a8122905..e53a594a 100644 --- a/beartype/_util/mod/utilmodimport.py +++ b/beartype/_util/mod/utilmodimport.py @@ -15,6 +15,7 @@ from beartype.roar._roarexc import _BeartypeUtilModuleException from beartype._util.cache.utilcachecall import callable_cached from importlib import import_module as importlib_import_module +from sys import modules as sys_modules from types import ModuleType from typing import Any, Optional, Type from warnings import warn @@ -51,27 +52,26 @@ # If no module with this name exists. # Exception # If a module with this name exists *but* that module is unimportable -# due to module-scoped side effects at importation time. Since modules -# may perform arbitrary Turing-complete logic from module scope, callers -# should be prepared to handle *any* possible exception that might arise. +# due to raising module-scoped exceptions at importation time. Since +# modules may perform arbitrary Turing-complete logic at module scope, +# callers should be prepared to handle *any* possible exception. # ''' -# assert isinstance(module_name, str), f'{repr(module_name)} not string.' -# assert isinstance(exception_cls, type), f'{repr(exception_cls)} not type.' +# assert isinstance(exception_cls, type), ( +# f'{repr(exception_cls)} not type.') # -# # Attempt to dynamically import and return this module. -# try: -# return importlib_import_module(module_name) -# # If this module does *NOT* exist, raise a beartype-specific exception -# # wrapping this beartype-agnostic exception to sanitize our public API, -# # particularly from the higher-level import_module_attr() function calling -# # this lower-level function and raising the same exception class under a -# # wider variety of fatal edge cases. -# except ModuleNotFoundError as exception: +# # Module with this name if this module is importable *OR* "None" otherwise. +# module = import_module_or_none(module_name) +# +# # If this module is unimportable, raise an exception. +# if module is None: # raise exception_cls( # f'Module "{module_name}" not found.') from exception +# # Else, this module is importable. +# +# # Return this module. +# return module -#FIXME: Unit test us up. def import_module_or_none(module_name: str) -> Optional[ModuleType]: ''' Dynamically import and return the module, package, or C extension with the @@ -91,10 +91,19 @@ def import_module_or_none(module_name: str) -> Optional[ModuleType]: ---------- BeartypeModuleUnimportableWarning If a module with this name exists *but* that module is unimportable - due to module-scoped side effects at importation time. + due to raising module-scoped exceptions at importation time. ''' assert isinstance(module_name, str), f'{repr(module_name)} not string.' + # Module cached with "sys.modules" if this module has already been imported + # elsewhere under the active Python interpreter *OR* "None" otherwise. + module = sys_modules.get(module_name) + + # If this module has already been imported, return this cached module. + if module is not None: + return module + # Else, this module has yet to be imported. + # Attempt to dynamically import and return this module. try: return importlib_import_module(module_name) @@ -228,7 +237,7 @@ def import_module_attr_or_none( ---------- BeartypeModuleUnimportableWarning If a module with this name exists *but* that module is unimportable - due to module-scoped side effects at importation time. + due to raising module-scoped exceptions at importation time. ''' # Avoid circular import dependencies. @@ -319,8 +328,8 @@ def import_module_typing_any_attr( ---------- BeartypeModuleUnimportableWarning If a module with this name exists *but* that module is unimportable - due to module-scoped side effects at importation time. That said, the - :mod:`typing` and :mod:`typing_extensions` modules are scrupulously + due to raising module-scoped exceptions at importation time. That said, + the :mod:`typing` and :mod:`typing_extensions` modules are scrupulously tested and thus unlikely to raise exceptions on initial importation. ''' diff --git a/beartype/_util/mod/utilmodtest.py b/beartype/_util/mod/utilmodtest.py index 5b15b2ee..ec1485d8 100644 --- a/beartype/_util/mod/utilmodtest.py +++ b/beartype/_util/mod/utilmodtest.py @@ -71,8 +71,7 @@ def die_unless_module_attr_name( ''' assert isinstance(module_attr_label, str), ( f'{repr(module_attr_label)} not string.') - assert isinstance(exception_cls, type), ( - f'{repr(exception_cls)} not type.') + assert isinstance(exception_cls, type), f'{repr(exception_cls)} not type.' # Avoid circular import dependencies. from beartype._util.text.utiltextidentifier import is_identifier @@ -126,33 +125,21 @@ def is_module(module_name: str) -> bool: bool ``True`` only if this module is importable. - Raises + Warns ---------- - Exception - If a module with this name exists *but* this module is unimportable - due to module-scoped side effects at importation time. Since modules - may perform arbitrary Turing-complete logic from module scope, callers - should be prepared to handle *any* possible exception that might arise. + BeartypeModuleUnimportableWarning + If a module with this name exists *but* that module is unimportable + due to raising module-scoped exceptions at importation time. ''' - assert isinstance(module_name, str), f'{repr(module_name)} not string.' - - # If this module has already been imported, return true. - if module_name in sys_modules: - return True - # Else, this module has yet to be imported. - - # Attempt to... - try: - # Dynamically import this module. - importlib_import_module(module_name) - - # Return true, since this importation succeeded. - return True - # If no module this this name exists, return false. - except ModuleNotFoundError: - return False - # If any other exception was raised, silently permit that exception to - # unwind the call stack. + + # Avoid circular import dependencies. + from beartype._util.mod.utilmodimport import import_module_or_none + + # Module with this name if this module is importable *OR* "None" otherwise. + module = import_module_or_none(module_name) + + # Return true only if this module is importable. + return module is not None #FIXME: Unit test us up against "setuptools", the only third-party package @@ -184,13 +171,11 @@ def is_module_version_at_least(module_name: str, version_minimum: str) -> bool: * This module is importable. * This module's version is at least the passed version. - Raises + Warns ---------- - Exception - If a module with this name exists *but* this module is unimportable - due to module-scoped side effects at importation time. Since modules - may perform arbitrary Turing-complete logic from module scope, callers - should be prepared to handle *any* possible exception that might arise. + BeartypeModuleUnimportableWarning + If a module with this name exists *but* that module is unimportable + due to raising module-scoped exceptions at importation time. ''' # If it is *NOT* the case that... diff --git a/beartype/_util/py/utilpyversion.py b/beartype/_util/py/utilpyversion.py index 05b4cdb6..84d2d8c5 100644 --- a/beartype/_util/py/utilpyversion.py +++ b/beartype/_util/py/utilpyversion.py @@ -33,6 +33,14 @@ ''' +#FIXME: After dropping Python 3.9 support: +#* Remove this global. +IS_PYTHON_AT_MOST_3_9 = not IS_PYTHON_AT_LEAST_3_10 +''' +``True`` only if the active Python interpreter targets at most Python 3.9.x. +''' + + #FIXME: After dropping Python 3.8 support: #* Refactor all code conditionally testing this global to be unconditional. #* Remove this global. diff --git a/beartype/_util/text/utiltextlabel.py b/beartype/_util/text/utiltextlabel.py index ca17dcd6..dcbce675 100644 --- a/beartype/_util/text/utiltextlabel.py +++ b/beartype/_util/text/utiltextlabel.py @@ -262,7 +262,7 @@ def label_class(cls: type) -> str: # Avoid circular import dependencies. from beartype._util.cls.utilclstest import is_type_builtin - from beartype._util.hint.pep.proposal.utilhintpep544 import ( + from beartype._util.hint.pep.proposal.utilpep544 import ( is_hint_pep544_protocol) # Label to be returned, initialized to this class' fully-qualified name. diff --git a/beartype/meta.py b/beartype/meta.py index 5bb5fb8a..797a0b1f 100644 --- a/beartype/meta.py +++ b/beartype/meta.py @@ -66,7 +66,7 @@ #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # WARNING: Changes to this section *MUST* be synchronized with: # * Signs declared by the private -# "beartype._util.data.hint.pep.datapepsign" submodule, which *MUST* +# "beartype._data.hint.pep.datapepsign" submodule, which *MUST* # be synchronized against the "__all__" dunder list global of the "typing" # module bundled with the most recent CPython release. # * Continuous integration test matrices, including: diff --git a/beartype/roar/_roarexc.py b/beartype/roar/_roarexc.py index 27cb7e08..5596ff66 100644 --- a/beartype/roar/_roarexc.py +++ b/beartype/roar/_roarexc.py @@ -19,7 +19,6 @@ # names (e.g., "from argparse import ArgumentParser as _ArgumentParser" rather # than merely "from argparse import ArgumentParser"). #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - from abc import ABCMeta as _ABCMeta # See the "beartype.cave" submodule for further commentary. diff --git a/beartype/roar/_roarwarn.py b/beartype/roar/_roarwarn.py index 35b75da9..3b6db87d 100644 --- a/beartype/roar/_roarwarn.py +++ b/beartype/roar/_roarwarn.py @@ -19,7 +19,6 @@ # names (e.g., "from argparse import ArgumentParser as _ArgumentParser" rather # than merely "from argparse import ArgumentParser"). #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - from abc import ABCMeta as _ABCMeta # See the "beartype.cave" submodule for further commentary. diff --git a/beartype_test/a00_unit/a00_util/hint/a00_pep/proposal/test_utilhintpep586.py b/beartype_test/a00_unit/a00_util/hint/a00_pep/proposal/test_utilhintpep586.py index 9596fd95..c54c23d6 100644 --- a/beartype_test/a00_unit/a00_util/hint/a00_pep/proposal/test_utilhintpep586.py +++ b/beartype_test/a00_unit/a00_util/hint/a00_pep/proposal/test_utilhintpep586.py @@ -7,7 +7,7 @@ **Beartype** :pep:`586` **type hint utility unit tests.** This submodule unit tests the public API of the private -:mod:`beartype._util.hint.pep.proposal.utilhintpep586` submodule. +:mod:`beartype._util.hint.pep.proposal.utilpep586` submodule. ''' # ....................{ IMPORTS }.................... @@ -51,14 +51,14 @@ class _Color(Enum): def test_is_hint_pep586() -> None: ''' Test the - :func:`beartype._util.hint.pep.proposal.utilhintpep586.die_unless_hint_pep586` + :func:`beartype._util.hint.pep.proposal.utilpep586.die_unless_hint_pep586` validator. ''' # Defer heavyweight imports. from beartype.roar import BeartypeDecorHintPep586Exception from beartype._util.py.utilpyversion import IS_PYTHON_AT_LEAST_3_9 - from beartype._util.hint.pep.proposal.utilhintpep586 import ( + from beartype._util.hint.pep.proposal.utilpep586 import ( die_unless_hint_pep586) from typing import Optional diff --git a/beartype_test/a00_unit/a00_util/hint/a00_pep/proposal/test_utilhintpep593.py b/beartype_test/a00_unit/a00_util/hint/a00_pep/proposal/test_utilhintpep593.py index 63c6be64..d687f25a 100644 --- a/beartype_test/a00_unit/a00_util/hint/a00_pep/proposal/test_utilhintpep593.py +++ b/beartype_test/a00_unit/a00_util/hint/a00_pep/proposal/test_utilhintpep593.py @@ -7,7 +7,7 @@ **Beartype** :pep:`593` **type hint utility unit tests.** This submodule unit tests the public API of the private -:mod:`beartype._util.hint.pep.proposal.utilhintpep593` submodule. +:mod:`beartype._util.hint.pep.proposal.utilpep593` submodule. ''' # ....................{ IMPORTS }.................... @@ -21,7 +21,7 @@ def test_is_hint_pep593_beartype() -> None: ''' Test usage of the private - :mod:`beartype._util.hint.pep.proposal.utilhintpep593.is_hint_pep593_beartype` + :mod:`beartype._util.hint.pep.proposal.utilpep593.is_hint_pep593_beartype` tester. ''' @@ -31,7 +31,7 @@ def test_is_hint_pep593_beartype() -> None: BeartypeValeLambdaWarning, ) from beartype.vale import Is - from beartype._util.hint.pep.proposal.utilhintpep593 import ( + from beartype._util.hint.pep.proposal.utilpep593 import ( is_hint_pep593_beartype) from beartype._util.py.utilpyversion import IS_PYTHON_AT_LEAST_3_9 diff --git a/beartype_test/a00_unit/a00_util/hint/a00_pep/test_a00_utilpepget.py b/beartype_test/a00_unit/a00_util/hint/a00_pep/test_a00_utilpepget.py index a24938e6..e35768f9 100644 --- a/beartype_test/a00_unit/a00_util/hint/a00_pep/test_a00_utilpepget.py +++ b/beartype_test/a00_unit/a00_util/hint/a00_pep/test_a00_utilpepget.py @@ -60,7 +60,7 @@ def test_get_hint_pep_generic_type_or_none() -> None: ''' # Defer heavyweight imports. - from beartype._util.data.hint.pep.sign.datapepsigns import HintSignGeneric + from beartype._data.hint.pep.sign.datapepsigns import HintSignGeneric from beartype._util.hint.pep.utilpepget import ( get_hint_pep_generic_type_or_none) from beartype_test.a00_unit.data.hint.pep.data_hintpep import ( @@ -163,7 +163,7 @@ def test_get_hint_pep_generic_bases_unerased() -> None: # Defer heavyweight imports. from beartype.roar import BeartypeDecorHintPepException - from beartype._util.data.hint.pep.sign.datapepsigns import HintSignGeneric + from beartype._data.hint.pep.sign.datapepsigns import HintSignGeneric from beartype._util.hint.pep.utilpepget import ( get_hint_pep_generic_bases_unerased) from beartype._util.hint.pep.utilpeptest import is_hint_pep_type_typing diff --git a/beartype_test/a00_unit/a00_util/hint/a00_pep/test_a90_utilpeptest.py b/beartype_test/a00_unit/a00_util/hint/a00_pep/test_a90_utilpeptest.py index f8c26cd0..ee7499db 100644 --- a/beartype_test/a00_unit/a00_util/hint/a00_pep/test_a90_utilpeptest.py +++ b/beartype_test/a00_unit/a00_util/hint/a00_pep/test_a90_utilpeptest.py @@ -29,7 +29,7 @@ def test_is_hint_pep_generic() -> None: ''' # Defer heavyweight imports. - from beartype._util.data.hint.pep.sign.datapepsigns import HintSignGeneric + from beartype._data.hint.pep.sign.datapepsigns import HintSignGeneric from beartype._util.hint.pep.utilpeptest import is_hint_pep_generic from beartype_test.a00_unit.data.hint.data_hint import NOT_HINTS_PEP from beartype_test.a00_unit.data.hint.pep.data_hintpep import ( @@ -312,7 +312,7 @@ def test_die_if_hint_pep_sign_unsupported() -> None: BeartypeDecorHintPepException, BeartypeDecorHintPepUnsupportedException, ) - from beartype._util.data.hint.pep.sign.datapepsignset import ( + from beartype._data.hint.pep.sign.datapepsignset import ( HINT_SIGNS_SUPPORTED) from beartype._util.hint.pep.utilpeptest import ( die_if_hint_pep_sign_unsupported) diff --git a/beartype_test/a00_unit/a00_util/hint/a90_core/test_a00_utilhintget.py b/beartype_test/a00_unit/a00_util/hint/a90_core/test_a00_utilhintget.py index 0411f2b6..ddc4a05b 100644 --- a/beartype_test/a00_unit/a00_util/hint/a90_core/test_a00_utilhintget.py +++ b/beartype_test/a00_unit/a00_util/hint/a90_core/test_a00_utilhintget.py @@ -29,7 +29,7 @@ def test_get_hint_reduced() -> None: from beartype.roar import BeartypeDecorHintNonPepNumPyException from beartype.vale import IsEqual from beartype._cave._cavefast import NoneType - from beartype._util.data.hint.pep.sign.datapepsigns import ( + from beartype._data.hint.pep.sign.datapepsigns import ( HintSignAnnotated, ) from beartype._util.hint.utilhintget import get_hint_reduced diff --git a/beartype_test/a00_unit/a00_util/mod/test_utilmodimport.py b/beartype_test/a00_unit/a00_util/mod/test_utilmodimport.py index 663dca31..f76c1702 100644 --- a/beartype_test/a00_unit/a00_util/mod/test_utilmodimport.py +++ b/beartype_test/a00_unit/a00_util/mod/test_utilmodimport.py @@ -17,6 +17,43 @@ #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! from pytest import raises, warns +# ....................{ TESTS }.................... +def test_import_module_or_none() -> None: + ''' + Test the + :func:`beartype._util.mod.utilmodule.import_module_or_none` function. + ''' + + # Defer heavyweight imports. + import beartype + from beartype.roar import BeartypeModuleUnimportableWarning + from beartype.roar._roarexc import _BeartypeUtilModuleException + from beartype._util.mod.utilmodimport import import_module_or_none + + # Assert this function returns the expected module when passed the + # fully-qualified name of a previously imported module. + assert import_module_or_none('beartype') is beartype + + # Assert this function returns the expected module when passed the + # fully-qualified name of a module effectively guaranteed to *NOT* have + # been previously imported by virtue of its complete and utter uselessness. + turtledemo_peace = import_module_or_none('turtledemo.peace') + from turtledemo import peace + assert turtledemo_peace is peace + + # Assert this function returns "None" when passed the fully-qualified name + # of a module effectively guaranteed to *NEVER* exist by virtue of the + # excess inscrutability, stupidity, and verbosity of its name. + assert import_module_or_none( + 'phnglui_mglwnafh_Cthulhu_Rlyeh_wgahnagl_fhtagn') is None + + # Assert this function emits the expected warning when passed the + # fully-qualified name of an unimportable module. + with warns(BeartypeModuleUnimportableWarning): + assert import_module_or_none( + 'beartype_test.a00_unit.data.util.py.data_utilpymodule_bad') is ( + None) + # ....................{ TESTS ~ attr }.................... def test_import_module_attr() -> None: ''' diff --git a/beartype_test/a00_unit/a00_util/mod/test_utilmodtest.py b/beartype_test/a00_unit/a00_util/mod/test_utilmodtest.py index 318ebd41..78954e6b 100644 --- a/beartype_test/a00_unit/a00_util/mod/test_utilmodtest.py +++ b/beartype_test/a00_unit/a00_util/mod/test_utilmodtest.py @@ -15,7 +15,7 @@ # WARNING: To raise human-readable test errors, avoid importing from # package-specific submodules at module scope. #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -from pytest import raises +from pytest import raises, warns # ....................{ TESTS ~ tester }.................... def test_is_module() -> None: @@ -24,6 +24,7 @@ def test_is_module() -> None: ''' # Defer heavyweight imports. + from beartype.roar import BeartypeModuleUnimportableWarning from beartype._util.mod.utilmodtest import is_module # Assert this tester accepts the name of a (possibly unimported) existing @@ -41,7 +42,9 @@ def test_is_module() -> None: 'beartype_test.a00_unit.data.util.py.data_utilpymodule_nonexistent' ) is False - # Assert this function raises the expected exception when passed the name - # of an existing unimportable module. - with raises(ValueError): - is_module('beartype_test.a00_unit.data.util.py.data_utilpymodule_bad') + # Assert this function emits the expected warning when passed the name of + # an existing unimportable module. + with warns(BeartypeModuleUnimportableWarning): + assert is_module( + 'beartype_test.a00_unit.data.util.py.data_utilpymodule_bad') is ( + False) diff --git a/beartype_test/a00_unit/a10_pep/test_pep544.py b/beartype_test/a00_unit/a10_pep/test_pep544.py index ffa3e1c2..fe016fbd 100644 --- a/beartype_test/a00_unit/a10_pep/test_pep544.py +++ b/beartype_test/a00_unit/a10_pep/test_pep544.py @@ -102,12 +102,12 @@ def times_of_old(god_slept: TwoTrees) -> str: def test_is_hint_pep544_protocol() -> None: ''' Test the - :func:`beartype._util.hint.pep.proposal.utilhintpep544.is_hint_pep544_protocol` + :func:`beartype._util.hint.pep.proposal.utilpep544.is_hint_pep544_protocol` tester. ''' # Defer heavyweight imports. - from beartype._util.hint.pep.proposal.utilhintpep544 import ( + from beartype._util.hint.pep.proposal.utilpep544 import ( is_hint_pep544_protocol) from beartype._util.py.utilpyversion import IS_PYTHON_AT_LEAST_3_8 from beartype_test.a00_unit.data.data_type import TYPES_BUILTIN @@ -150,12 +150,12 @@ def test_is_hint_pep544_protocol() -> None: def test_is_hint_pep544_io_generic() -> None: ''' Test the - :func:`beartype._util.hint.pep.proposal.utilhintpep544.is_hint_pep484_generic_io` + :func:`beartype._util.hint.pep.proposal.utilpep544.is_hint_pep484_generic_io` tester. ''' # Defer heavyweight imports. - from beartype._util.hint.pep.proposal.utilhintpep544 import ( + from beartype._util.hint.pep.proposal.utilpep544 import ( is_hint_pep484_generic_io) from beartype._util.py.utilpyversion import IS_PYTHON_AT_LEAST_3_8 from beartype_test.a00_unit.data.hint.pep.proposal.data_hintpep484 import ( @@ -175,13 +175,13 @@ def test_is_hint_pep544_io_generic() -> None: def test_get_hint_pep544_io_protocol_from_generic() -> None: ''' Test the - :func:`beartype._util.hint.pep.proposal.utilhintpep544.reduce_hint_pep484_generic_io_to_pep544_protocol` + :func:`beartype._util.hint.pep.proposal.utilpep544.reduce_hint_pep484_generic_io_to_pep544_protocol` tester. ''' # Defer heavyweight imports. from beartype.roar import BeartypeDecorHintPep544Exception - from beartype._util.hint.pep.proposal.utilhintpep544 import ( + from beartype._util.hint.pep.proposal.utilpep544 import ( reduce_hint_pep484_generic_io_to_pep544_protocol) from beartype._util.py.utilpyversion import IS_PYTHON_AT_LEAST_3_8 from beartype._util.utilobject import is_object_subclass diff --git a/beartype_test/a00_unit/a10_pep/test_pep585.py b/beartype_test/a00_unit/a10_pep/test_pep585.py index 9c4215f3..34388450 100644 --- a/beartype_test/a00_unit/a10_pep/test_pep585.py +++ b/beartype_test/a00_unit/a10_pep/test_pep585.py @@ -24,12 +24,12 @@ def test_is_hint_pep585_builtin() -> None: ''' Test the - :func:`beartype._util.hint.pep.proposal.utilhintpep585.is_hint_pep585_builtin` + :func:`beartype._util.hint.pep.proposal.utilpep585.is_hint_pep585_builtin` function. ''' # Defer heavyweight imports. - from beartype._util.hint.pep.proposal.utilhintpep585 import ( + from beartype._util.hint.pep.proposal.utilpep585 import ( is_hint_pep585_builtin) from beartype_test.a00_unit.data.hint.pep.data_hintpep import ( HINTS_PEP_META) @@ -43,12 +43,12 @@ def test_is_hint_pep585_builtin() -> None: def test_is_hint_pep585_generic() -> None: ''' Test the - :func:`beartype._util.hint.pep.proposal.utilhintpep585.is_hint_pep585_generic` + :func:`beartype._util.hint.pep.proposal.utilpep585.is_hint_pep585_generic` function. ''' # Defer heavyweight imports. - from beartype._util.hint.pep.proposal.utilhintpep585 import ( + from beartype._util.hint.pep.proposal.utilpep585 import ( is_hint_pep585_generic) from beartype_test.a00_unit.data.hint.pep.data_hintpep import ( HINTS_PEP_META) @@ -62,13 +62,13 @@ def test_is_hint_pep585_generic() -> None: def test_get_hint_pep585_generic_typevars() -> None: ''' Test the - :func:`beartype._util.hint.pep.proposal.utilhintpep585.get_hint_pep585_generic_typevars` + :func:`beartype._util.hint.pep.proposal.utilpep585.get_hint_pep585_generic_typevars` function. ''' # Defer heavyweight imports. from beartype.roar import BeartypeDecorHintPep585Exception - from beartype._util.hint.pep.proposal.utilhintpep585 import ( + from beartype._util.hint.pep.proposal.utilpep585 import ( get_hint_pep585_generic_typevars) from beartype_test.a00_unit.data.hint.pep.data_hintpep import ( HINTS_PEP_META) diff --git a/beartype_test/a00_unit/a10_pep/test_pep593.py b/beartype_test/a00_unit/a10_pep/test_pep593.py index 75e66143..d7217d06 100644 --- a/beartype_test/a00_unit/a10_pep/test_pep593.py +++ b/beartype_test/a00_unit/a10_pep/test_pep593.py @@ -19,13 +19,13 @@ # ....................{ TESTS }.................... def test_is_hint_pep593() -> None: ''' - Test the :beartype._util.hint.pep.proposal.utilhintpep593.is_hint_pep593` + Test the :beartype._util.hint.pep.proposal.utilpep593.is_hint_pep593` tester. ''' # Defer heavyweight imports. from beartype._util.py.utilpyversion import IS_PYTHON_AT_LEAST_3_9 - from beartype._util.hint.pep.proposal.utilhintpep593 import ( + from beartype._util.hint.pep.proposal.utilpep593 import ( is_hint_pep593) from typing import Optional diff --git a/beartype_test/a00_unit/a90_decor/code/test_codemain.py b/beartype_test/a00_unit/a90_decor/code/test_codemain.py index ead0d434..61319c2f 100644 --- a/beartype_test/a00_unit/a90_decor/code/test_codemain.py +++ b/beartype_test/a00_unit/a90_decor/code/test_codemain.py @@ -106,7 +106,7 @@ def func_untyped(hint_param: hint_meta.hint) -> hint_meta.hint: # # decorated callable under a context manager asserting this # # declaration to emit non-fatal deprecation warnings. # if ( - # isinstance(hint_meta, PepHintMetadata) and + # isinstance(hint_meta, HintPepMetadata) and # hint_meta.pep_sign in HINT_PEP_ATTRS_DEPRECATED # ): # with pytest.warns(BeartypeDecorHintPepDeprecatedWarning): diff --git a/beartype_test/a00_unit/data/data_type.py b/beartype_test/a00_unit/data/data_type.py index e10f344c..3396605c 100644 --- a/beartype_test/a00_unit/data/data_type.py +++ b/beartype_test/a00_unit/data/data_type.py @@ -12,7 +12,7 @@ # ....................{ IMPORTS }.................... import builtins -from beartype._util.data.mod.datamod import BUILTINS_MODULE_NAME +from beartype._data.mod.datamod import BUILTINS_MODULE_NAME from sys import exc_info, implementation from typing import Callable @@ -241,7 +241,7 @@ async def coroutine_factory(): See Also ---------- -:data:`beartype._util.data.cls.datacls.TYPES_BUILTIN_FAKE` +:data:`beartype._data.cls.datacls.TYPES_BUILTIN_FAKE` Related runtime set. Whereas that runtime-specific set is efficiently defined explicitly by listing all non-builtin builtin mimic types, this test-specific set is inefficiently defined implicitly by introspecting the diff --git a/beartype_test/a00_unit/data/hint/nonpep/data_hintnonpep.py b/beartype_test/a00_unit/data/hint/nonpep/data_hintnonpep.py index 77a5c00e..f764838c 100644 --- a/beartype_test/a00_unit/data/hint/nonpep/data_hintnonpep.py +++ b/beartype_test/a00_unit/data/hint/nonpep/data_hintnonpep.py @@ -16,7 +16,7 @@ HINTS_NONPEP_META = [] ''' Tuple of **PEP-noncompliant type hint metadata** (i.e., -:class:`NonPepHintMetadata` instances describing test-specific PEP-noncompliant +:class:`HintNonPepMetadata` instances describing test-specific PEP-noncompliant type hints with metadata leveraged by various testing scenarios). ''' @@ -29,7 +29,7 @@ def _init() -> None: # Defer function-specific imports. import sys from beartype_test.a00_unit.data.hint.util.data_hintmetacls import ( - NonPepHintMetadata) + HintNonPepMetadata) from beartype_test.a00_unit.data.hint.nonpep.proposal import ( _data_hintnonpep484) @@ -54,10 +54,10 @@ def _init() -> None: # Assert this global to contain only instances of its expected dataclass. assert ( - isinstance(hint_nonpep_meta, NonPepHintMetadata) + isinstance(hint_nonpep_meta, HintNonPepMetadata) for hint_nonpep_meta in HINTS_NONPEP_META ), (f'{repr(HINTS_NONPEP_META)} not iterable of ' - f'"NonPepHintMetadata" instances.') + f'"HintNonPepMetadata" instances.') # Frozen sets defined *AFTER* initializing these private submodules and # thus the lower-level globals required by these sets. diff --git a/beartype_test/a00_unit/data/hint/nonpep/mod/_data_hintmodbeartype.py b/beartype_test/a00_unit/data/hint/nonpep/mod/_data_hintmodbeartype.py index e838f597..e474848a 100644 --- a/beartype_test/a00_unit/data/hint/nonpep/mod/_data_hintmodbeartype.py +++ b/beartype_test/a00_unit/data/hint/nonpep/mod/_data_hintmodbeartype.py @@ -15,7 +15,7 @@ # ....................{ IMPORTS }.................... from beartype_test.a00_unit.data.hint.util.data_hintmetacls import ( - NonPepHintMetadata, + HintNonPepMetadata, HintPithSatisfiedMetadata, HintPithUnsatisfiedMetadata, ) @@ -38,7 +38,7 @@ def add_data(data_module: 'ModuleType') -> None: data_module.HINTS_NONPEP_META.extend(( # ................{ TUPLE UNION }................ # Tuple union of one standard class. - NonPepHintMetadata( + HintNonPepMetadata( hint=(str,), piths_satisfied_meta=( # String constant. @@ -64,7 +64,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Tuple union of two or more standard classes. - NonPepHintMetadata( + HintNonPepMetadata( hint=(int, str), piths_satisfied_meta=( # Integer constant. @@ -94,7 +94,7 @@ def add_data(data_module: 'ModuleType') -> None: # ................{ TYPE }................ # Builtin type. - NonPepHintMetadata( + HintNonPepMetadata( hint=str, piths_satisfied_meta=( # String constant. diff --git a/beartype_test/a00_unit/data/hint/nonpep/proposal/_data_hintnonpep484.py b/beartype_test/a00_unit/data/hint/nonpep/proposal/_data_hintnonpep484.py index 1cdc3d16..3f07f00f 100644 --- a/beartype_test/a00_unit/data/hint/nonpep/proposal/_data_hintnonpep484.py +++ b/beartype_test/a00_unit/data/hint/nonpep/proposal/_data_hintnonpep484.py @@ -18,7 +18,7 @@ # ....................{ IMPORTS }.................... from beartype_test.a00_unit.data.hint.util.data_hintmetacls import ( - NonPepHintMetadata, + HintNonPepMetadata, HintPithSatisfiedMetadata, HintPithUnsatisfiedMetadata, ) @@ -53,7 +53,7 @@ def add_data(data_module: 'ModuleType') -> None: # ................{ NAMEDTUPLE }................ # "NamedTuple" instances transparently reduce to standard tuples and # *MUST* thus be handled as non-"typing" type hints. - NonPepHintMetadata( + HintNonPepMetadata( hint=NamedTupleType, piths_satisfied_meta=( # Named tuple containing correctly typed items. diff --git a/beartype_test/a00_unit/data/hint/pep/data_hintpep.py b/beartype_test/a00_unit/data/hint/pep/data_hintpep.py index ec76543e..9b894952 100644 --- a/beartype_test/a00_unit/data/hint/pep/data_hintpep.py +++ b/beartype_test/a00_unit/data/hint/pep/data_hintpep.py @@ -40,7 +40,7 @@ Frozen set of **shallowly ignorable PEP-compliant type hints** (i.e., PEP-compliant type hints that are shallowly ignorable and whose machine-readable representations are in the low-level -:attr:`beartype._util.data.hint.pep.datapeprepr.HINTS_REPR_IGNORABLE_SHALLOW` +:attr:`beartype._data.hint.pep.datapeprepr.HINTS_REPR_IGNORABLE_SHALLOW` set, but which are typically *not* safely instantiable from those representations and thus require explicit instantiation here). ''' @@ -61,14 +61,14 @@ # Initialized by the _init() function below. HINTS_PEP_META = [] ''' -Tuple of **PEP-compliant type hint metadata** (i.e., :class:`PepHintMetadata` +Tuple of **PEP-compliant type hint metadata** (i.e., :class:`HintPepMetadata` instances describing test-specific PEP-compliant type hints with metadata leveraged by various testing scenarios). Design ---------- This tuple was initially designed as a dictionary mapping from PEP-compliant -type hints to :class:`PepHintMetadata` instances describing those hints, until +type hints to :class:`HintPepMetadata` instances describing those hints, until :mod:`beartype` added support for PEPs enabling unhashable PEP-compliant type hints (e.g., ``collections.abc.Callable[[], str]`` under :pep:`585`) impermissible for use as dictionary keys or set members. @@ -83,7 +83,7 @@ def _init() -> None: # Defer function-specific imports. import sys from beartype._util.utilobject import is_object_hashable - from beartype_test.a00_unit.data.hint.util.data_hintmetacls import PepHintMetadata + from beartype_test.a00_unit.data.hint.util.data_hintmetacls import HintPepMetadata from beartype_test.a00_unit.data.hint.pep.mod import ( _data_hintmodnumpy, ) @@ -129,9 +129,9 @@ def _init() -> None: # Assert this global to contain only instances of its expected dataclass. assert ( - isinstance(hint_pep_meta, PepHintMetadata) + isinstance(hint_pep_meta, HintPepMetadata) for hint_pep_meta in HINTS_PEP_META - ), f'{repr(HINTS_PEP_META)} not iterable of "PepHintMetadata" instances.' + ), f'{repr(HINTS_PEP_META)} not iterable of "HintPepMetadata" instances.' # Frozen sets defined *AFTER* initializing these private submodules and # thus the lower-level globals required by these sets. diff --git a/beartype_test/a00_unit/data/hint/pep/mod/_data_hintmodnumpy.py b/beartype_test/a00_unit/data/hint/pep/mod/_data_hintmodnumpy.py index b8cd715c..a673cd61 100644 --- a/beartype_test/a00_unit/data/hint/pep/mod/_data_hintmodnumpy.py +++ b/beartype_test/a00_unit/data/hint/pep/mod/_data_hintmodnumpy.py @@ -21,11 +21,11 @@ # ....................{ IMPORTS }.................... from beartype._util.py.utilpyversion import IS_PYTHON_AT_LEAST_3_9 -from beartype._util.data.hint.pep.sign.datapepsigns import ( +from beartype._data.hint.pep.sign.datapepsigns import ( HintSignNumpyArray, ) from beartype_test.a00_unit.data.hint.util.data_hintmetacls import ( - PepHintMetadata, + HintPepMetadata, HintPithSatisfiedMetadata, HintPithUnsatisfiedMetadata, ) @@ -61,7 +61,7 @@ def add_data(data_module: 'ModuleType') -> None: data_module.HINTS_PEP_META.extend(( # ................{ NUMPY ~ array }................ # NumPy array subscripted by a true data type. - PepHintMetadata( + HintPepMetadata( hint=NDArray[dtype(float64)], pep_sign=HintSignNumpyArray, # "NDArray" is implemented as: @@ -96,7 +96,7 @@ def add_data(data_module: 'ModuleType') -> None: # NumPy array subscripted by a scalar data type. Since scalar data # types are *NOT* true data types, this constitutes an edge case. - PepHintMetadata( + HintPepMetadata( hint=NDArray[float64], pep_sign=HintSignNumpyArray, is_pep585_builtin=IS_PYTHON_AT_LEAST_3_9, diff --git a/beartype_test/a00_unit/data/hint/pep/proposal/_data_hintpep544.py b/beartype_test/a00_unit/data/hint/pep/proposal/_data_hintpep544.py index 3b3552ae..9ba497a5 100644 --- a/beartype_test/a00_unit/data/hint/pep/proposal/_data_hintpep544.py +++ b/beartype_test/a00_unit/data/hint/pep/proposal/_data_hintpep544.py @@ -15,10 +15,10 @@ # ....................{ IMPORTS }.................... import pathlib from abc import abstractmethod -from beartype._util.data.hint.pep.sign.datapepsigns import HintSignGeneric +from beartype._data.hint.pep.sign.datapepsigns import HintSignGeneric from beartype._util.py.utilpyversion import IS_PYTHON_AT_LEAST_3_8 from beartype_test.a00_unit.data.hint.util.data_hintmetacls import ( - PepHintMetadata, + HintPepMetadata, HintPithSatisfiedMetadata, HintPithUnsatisfiedMetadata, ) @@ -145,7 +145,7 @@ def __int__(self) -> int: data_module.HINTS_PEP_META.extend(( # ................{ GENERICS ~ io }................ # Unsubscripted "IO" abstract base class (ABC). - PepHintMetadata( + HintPepMetadata( hint=IO, pep_sign=HintSignGeneric, generic_type=IO, @@ -165,7 +165,7 @@ def __int__(self) -> int: ), # Unsubscripted "BinaryIO" abstract base class (ABC). - PepHintMetadata( + HintPepMetadata( hint=BinaryIO, pep_sign=HintSignGeneric, generic_type=BinaryIO, @@ -185,7 +185,7 @@ def __int__(self) -> int: ), # Unsubscripted "TextIO" abstract base class (ABC). - PepHintMetadata( + HintPepMetadata( hint=TextIO, pep_sign=HintSignGeneric, generic_type=TextIO, @@ -206,7 +206,7 @@ def __int__(self) -> int: # ................{ PROTOCOLS ~ supports }................ # Unsubscripted "SupportsAbs" abstract base class (ABC). - PepHintMetadata( + HintPepMetadata( hint=SupportsAbs, pep_sign=HintSignGeneric, generic_type=SupportsAbs, @@ -224,7 +224,7 @@ def __int__(self) -> int: ), # Unsubscripted "SupportsBytes" abstract base class (ABC). - PepHintMetadata( + HintPepMetadata( hint=SupportsBytes, pep_sign=HintSignGeneric, generic_type=SupportsBytes, @@ -256,7 +256,7 @@ def __int__(self) -> int: #define a new custom "ProtocolSupportsComplex" class as we do above for #the "ProtocolSupportsInt" class. *shrug* # # Unsubscripted "SupportsComplex" abstract base class (ABC). - # SupportsComplex: PepHintMetadata( + # SupportsComplex: HintPepMetadata( # pep_sign=Generic, # piths_satisfied_meta=( # # Integer constant. @@ -269,7 +269,7 @@ def __int__(self) -> int: # ), # Unsubscripted "SupportsFloat" abstract base class (ABC). - PepHintMetadata( + HintPepMetadata( hint=SupportsFloat, pep_sign=HintSignGeneric, generic_type=SupportsFloat, @@ -286,7 +286,7 @@ def __int__(self) -> int: # Unsubscripted "SupportsIndex" abstract base class (ABC) first # introduced by Python 3.8.0. - PepHintMetadata( + HintPepMetadata( hint=SupportsIndex, pep_sign=HintSignGeneric, generic_type=SupportsIndex, @@ -302,7 +302,7 @@ def __int__(self) -> int: ), # Unsubscripted "SupportsInt" abstract base class (ABC). - PepHintMetadata( + HintPepMetadata( hint=SupportsInt, pep_sign=HintSignGeneric, generic_type=SupportsInt, @@ -321,7 +321,7 @@ def __int__(self) -> int: ), # Unsubscripted "SupportsRound" abstract base class (ABC). - PepHintMetadata( + HintPepMetadata( hint=SupportsRound, pep_sign=HintSignGeneric, generic_type=SupportsRound, @@ -349,7 +349,7 @@ def __int__(self) -> int: # Python >= 3.4 or so. # User-defined protocol parametrized by *NO* type variables. - PepHintMetadata( + HintPepMetadata( hint=ProtocolCustomUntypevared, pep_sign=HintSignGeneric, generic_type=ProtocolCustomUntypevared, @@ -365,7 +365,7 @@ def __int__(self) -> int: ), # User-defined protocol parametrized by a type variable. - PepHintMetadata( + HintPepMetadata( hint=ProtocolCustomTypevared, pep_sign=HintSignGeneric, generic_type=ProtocolCustomTypevared, @@ -382,7 +382,7 @@ def __int__(self) -> int: # User-defined protocol parametrized by a type variable, itself # parametrized by the same type variables in the same order. - PepHintMetadata( + HintPepMetadata( hint=ProtocolCustomTypevared[T], pep_sign=HintSignGeneric, generic_type=ProtocolCustomTypevared, diff --git a/beartype_test/a00_unit/data/hint/pep/proposal/_data_hintpep585.py b/beartype_test/a00_unit/data/hint/pep/proposal/_data_hintpep585.py index 16e240f3..04a8cf54 100644 --- a/beartype_test/a00_unit/data/hint/pep/proposal/_data_hintpep585.py +++ b/beartype_test/a00_unit/data/hint/pep/proposal/_data_hintpep585.py @@ -10,7 +10,7 @@ # ....................{ IMPORTS }.................... import re from beartype._cave._cavefast import IntType -from beartype._util.data.hint.pep.sign.datapepsigns import ( +from beartype._data.hint.pep.sign.datapepsigns import ( HintSignByteString, HintSignCallable, HintSignContextManager, @@ -27,7 +27,7 @@ ) from beartype._util.py.utilpyversion import IS_PYTHON_AT_LEAST_3_9 from beartype_test.a00_unit.data.hint.util.data_hintmetacls import ( - PepHintMetadata, + HintPepMetadata, HintPithSatisfiedMetadata, HintPithUnsatisfiedMetadata, ) @@ -250,7 +250,7 @@ def __len__(self) -> bool: # the standard integer protocol raises a runtime error from @beartype. # Yes, this means that subscripting "collections.abc.ByteString" # conveys no information and is thus nonsensical. Welcome to PEP 585. - PepHintMetadata( + HintPepMetadata( hint=ByteString[int], pep_sign=HintSignByteString, stdlib_type=ByteString, @@ -267,7 +267,7 @@ def __len__(self) -> bool: # Byte string of integer constants satisfying the stdlib # "numbers.Integral" protocol. - PepHintMetadata( + HintPepMetadata( hint=ByteString[IntType], pep_sign=HintSignByteString, stdlib_type=ByteString, @@ -285,7 +285,7 @@ def __len__(self) -> bool: # ................{ CALLABLE }................ # Callable accepting no parameters and returning a string. - PepHintMetadata( + HintPepMetadata( hint=Callable[[], str], pep_sign=HintSignCallable, stdlib_type=Callable, @@ -302,7 +302,7 @@ def __len__(self) -> bool: # ................{ CONTEXTMANAGER }................ # Context manager yielding strings. - PepHintMetadata( + HintPepMetadata( hint=AbstractContextManager[str], pep_sign=HintSignContextManager, stdlib_type=AbstractContextManager, @@ -324,7 +324,7 @@ def __len__(self) -> bool: # ................{ DICT }................ # Flat dictionary. - PepHintMetadata( + HintPepMetadata( hint=dict[int, str], pep_sign=HintSignDict, stdlib_type=dict, @@ -344,7 +344,7 @@ def __len__(self) -> bool: ), # Generic dictionary. - PepHintMetadata( + HintPepMetadata( hint=dict[S, T], pep_sign=HintSignDict, stdlib_type=dict, @@ -365,7 +365,7 @@ def __len__(self) -> bool: # ................{ GENERATOR }................ # Flat generator. - PepHintMetadata( + HintPepMetadata( hint=Generator[int, float, str], pep_sign=HintSignGenerator, stdlib_type=Generator, @@ -389,7 +389,7 @@ def __len__(self) -> bool: # "is_pep585_builtin=True," below. # Generic subclassing a single unparametrized builtin container. - PepHintMetadata( + HintPepMetadata( hint=Pep585GenericUntypevaredSingle, pep_sign=HintSignGeneric, generic_type=Pep585GenericUntypevaredSingle, @@ -415,7 +415,7 @@ def __len__(self) -> bool: ), # Generic subclassing a single parametrized builtin containerr. - PepHintMetadata( + HintPepMetadata( hint=Pep585GenericTypevaredSingle, pep_sign=HintSignGeneric, generic_type=Pep585GenericTypevaredSingle, @@ -442,7 +442,7 @@ def __len__(self) -> bool: # Generic subclassing a single parametrized builtin container, itself # parametrized by the same type variables in the same order. - PepHintMetadata( + HintPepMetadata( hint=Pep585GenericTypevaredSingle[S, T], pep_sign=HintSignGeneric, generic_type=Pep585GenericTypevaredSingle, @@ -470,7 +470,7 @@ def __len__(self) -> bool: # ................{ GENERICS ~ multiple }................ # Generic subclassing multiple unparametrized "collection.abc" abstract # base class (ABCs) *AND* an unsubscripted "collection.abc" ABC. - PepHintMetadata( + HintPepMetadata( hint=Pep585GenericUntypevaredMultiple, pep_sign=HintSignGeneric, generic_type=Pep585GenericUntypevaredMultiple, @@ -496,7 +496,7 @@ def __len__(self) -> bool: # Generic subclassing multiple parametrized "collections.abc" abstract # base classes (ABCs). - PepHintMetadata( + HintPepMetadata( hint=Pep585GenericTypevaredShallowMultiple, pep_sign=HintSignGeneric, generic_type=Pep585GenericTypevaredShallowMultiple, @@ -520,7 +520,7 @@ def __len__(self) -> bool: # Generic subclassing multiple indirectly parametrized # "collections.abc" abstract base classes (ABCs) *AND* an # unparametrized "collections.abc" ABC. - PepHintMetadata( + HintPepMetadata( hint=Pep585GenericTypevaredDeepMultiple, pep_sign=HintSignGeneric, generic_type=Pep585GenericTypevaredDeepMultiple, @@ -550,7 +550,7 @@ def __len__(self) -> bool: ), # Nested list of PEP 585-compliant generics. - PepHintMetadata( + HintPepMetadata( hint=list[Pep585GenericUntypevaredMultiple], pep_sign=HintSignList, stdlib_type=list, @@ -585,7 +585,7 @@ def __len__(self) -> bool: # ................{ LIST }................ # List of ignorable objects. - PepHintMetadata( + HintPepMetadata( hint=list[object], pep_sign=HintSignList, stdlib_type=list, @@ -608,7 +608,7 @@ def __len__(self) -> bool: ), # List of non-"typing" objects. - PepHintMetadata( + HintPepMetadata( hint=list[str], pep_sign=HintSignList, stdlib_type=list, @@ -643,7 +643,7 @@ def __len__(self) -> bool: ), # Generic list. - PepHintMetadata( + HintPepMetadata( hint=list[T], pep_sign=HintSignList, stdlib_type=list, @@ -667,7 +667,7 @@ def __len__(self) -> bool: # ................{ REGEX ~ match }................ # Regular expression match of only strings. - PepHintMetadata( + HintPepMetadata( hint=Match[str], pep_sign=HintSignMatch, stdlib_type=Match, @@ -687,7 +687,7 @@ def __len__(self) -> bool: # ................{ REGEX ~ pattern }................ # Regular expression pattern of only strings. - PepHintMetadata( + HintPepMetadata( hint=Pattern[str], pep_sign=HintSignPattern, stdlib_type=Pattern, @@ -712,7 +712,7 @@ def __len__(self) -> bool: # TypeError: Too few parameters for List; actual 0, expected 1 # >>> List[[]] # TypeError: Parameters to generic types must be types. Got []. - PepHintMetadata( + HintPepMetadata( hint=tuple[()], pep_sign=HintSignTuple, stdlib_type=tuple, @@ -738,7 +738,7 @@ def __len__(self) -> bool: ), # Fixed-length tuple of only ignorable child hints. - PepHintMetadata( + HintPepMetadata( hint=tuple[Any, object,], pep_sign=HintSignTuple, stdlib_type=tuple, @@ -764,7 +764,7 @@ def __len__(self) -> bool: ), # Fixed-length tuple of at least one ignorable child hint. - PepHintMetadata( + HintPepMetadata( hint=tuple[float, Any, str,], pep_sign=HintSignTuple, stdlib_type=tuple, @@ -814,7 +814,7 @@ def __len__(self) -> bool: ), # Nested fixed-length tuple of at least one ignorable child hint. - PepHintMetadata( + HintPepMetadata( hint=tuple[tuple[float, Any, str,], ...], pep_sign=HintSignTuple, stdlib_type=tuple, @@ -866,7 +866,7 @@ def __len__(self) -> bool: ), # Generic fixed-length tuple. - PepHintMetadata( + HintPepMetadata( hint=tuple[S, T], pep_sign=HintSignTuple, stdlib_type=tuple, @@ -892,7 +892,7 @@ def __len__(self) -> bool: # ................{ TUPLE ~ variadic }................ # Variadic tuple. - PepHintMetadata( + HintPepMetadata( hint=tuple[str, ...], pep_sign=HintSignTuple, stdlib_type=tuple, @@ -925,7 +925,7 @@ def __len__(self) -> bool: ), # Generic variadic tuple. - PepHintMetadata( + HintPepMetadata( hint=tuple[T, ...], pep_sign=HintSignTuple, stdlib_type=tuple, @@ -947,7 +947,7 @@ def __len__(self) -> bool: # ................{ TYPE }................ # Builtin type. - PepHintMetadata( + HintPepMetadata( hint=type[dict], pep_sign=HintSignType, stdlib_type=type, @@ -963,7 +963,7 @@ def __len__(self) -> bool: ), # Generic type. - PepHintMetadata( + HintPepMetadata( hint=type[T], pep_sign=HintSignType, stdlib_type=type, @@ -984,7 +984,7 @@ def __len__(self) -> bool: # optimizations leveraging PEP 572-style assignment expressions. # Nested union of multiple non-"typing" types. - PepHintMetadata( + HintPepMetadata( hint=list[Union[int, str,]], pep_sign=HintSignList, stdlib_type=list, @@ -1035,7 +1035,7 @@ def __len__(self) -> bool: ), # Nested union of one non-"typing" type and one "typing" type. - PepHintMetadata( + HintPepMetadata( hint=Sequence[Union[str, ByteString]], pep_sign=HintSignSequence, stdlib_type=Sequence, @@ -1084,7 +1084,7 @@ def __len__(self) -> bool: ), # Nested union of no non-"typing" type and multiple "typing" types. - PepHintMetadata( + HintPepMetadata( hint=MutableSequence[Union[ByteString, Callable]], pep_sign=HintSignMutableSequence, stdlib_type=MutableSequence, diff --git a/beartype_test/a00_unit/data/hint/pep/proposal/_data_hintpep586.py b/beartype_test/a00_unit/data/hint/pep/proposal/_data_hintpep586.py index f242605a..c5ce6b86 100644 --- a/beartype_test/a00_unit/data/hint/pep/proposal/_data_hintpep586.py +++ b/beartype_test/a00_unit/data/hint/pep/proposal/_data_hintpep586.py @@ -8,13 +8,13 @@ ''' # ....................{ IMPORTS }.................... -from beartype._util.data.hint.pep.sign.datapepsigns import ( +from beartype._data.hint.pep.sign.datapepsigns import ( HintSignList, HintSignLiteral, ) from beartype._util.py.utilpyversion import IS_PYTHON_AT_LEAST_3_9 from beartype_test.a00_unit.data.hint.util.data_hintmetacls import ( - PepHintMetadata, + HintPepMetadata, HintPithSatisfiedMetadata, HintPithUnsatisfiedMetadata, ) @@ -59,7 +59,7 @@ def add_data(data_module: 'ModuleType') -> None: data_module.HINTS_PEP_META.extend(( # ................{ LITERALS }................ # Literal "None" singleton. Look, this is ridiculous. What can you do? - PepHintMetadata( + HintPepMetadata( hint=Literal[None], pep_sign=HintSignLiteral, piths_satisfied_meta=( @@ -82,7 +82,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Literal arbitrary boolean. (Not that there are many of those...) - PepHintMetadata( + HintPepMetadata( hint=Literal[True], pep_sign=HintSignLiteral, piths_satisfied_meta=( @@ -112,7 +112,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Literal arbitrary integer. - PepHintMetadata( + HintPepMetadata( hint=Literal[0x2a], pep_sign=HintSignLiteral, piths_satisfied_meta=( @@ -142,7 +142,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Literal arbitrary byte string. - PepHintMetadata( + HintPepMetadata( hint=Literal[ b"Worthy, 'vain truthiness of (very invective-elected)"], pep_sign=HintSignLiteral, @@ -180,7 +180,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Literal arbitrary Unicode string. - PepHintMetadata( + HintPepMetadata( hint=Literal['Thanklessly classed, nominal'], pep_sign=HintSignLiteral, piths_satisfied_meta=( @@ -213,7 +213,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Literal arbitrary enumeration member. - PepHintMetadata( + HintPepMetadata( hint=Literal[ _MasterlessDecreeVenomlessWhich.NOMENCLATURE_WEATHER_VANES_OF], pep_sign=HintSignLiteral, @@ -249,7 +249,7 @@ def add_data(data_module: 'ModuleType') -> None: # ................{ LITERALS ~ nested }................ # List of literal arbitrary Unicode strings. - PepHintMetadata( + HintPepMetadata( hint=list[Literal[ 'ç‐omically gnomical whitebellied burden’s empathy of']], pep_sign=HintSignList, @@ -294,7 +294,7 @@ def add_data(data_module: 'ModuleType') -> None: # ................{ LITERALS ~ union }................ # Literal union of two or more arbitrary literal objects. - PepHintMetadata( + HintPepMetadata( hint=Literal[ None, True, diff --git a/beartype_test/a00_unit/data/hint/pep/proposal/_data_hintpep593.py b/beartype_test/a00_unit/data/hint/pep/proposal/_data_hintpep593.py index c1721eb7..54a151f0 100644 --- a/beartype_test/a00_unit/data/hint/pep/proposal/_data_hintpep593.py +++ b/beartype_test/a00_unit/data/hint/pep/proposal/_data_hintpep593.py @@ -9,12 +9,12 @@ # ....................{ IMPORTS }.................... from beartype._util.py.utilpyversion import IS_PYTHON_AT_LEAST_3_9 -from beartype._util.data.hint.pep.sign.datapepsigns import ( +from beartype._data.hint.pep.sign.datapepsigns import ( HintSignAnnotated, HintSignList, ) from beartype_test.a00_unit.data.hint.util.data_hintmetacls import ( - PepHintMetadata, + HintPepMetadata, HintPithSatisfiedMetadata, HintPithUnsatisfiedMetadata, ) @@ -130,7 +130,7 @@ def __init__(self) -> None: # ................{ ANNOTATED }................ # Hashable annotated of a non-"typing" type annotated by an arbitrary # hashable object. - PepHintMetadata( + HintPepMetadata( hint=Annotated[str, int], pep_sign=HintSignAnnotated, piths_satisfied_meta=( @@ -147,7 +147,7 @@ def __init__(self) -> None: # Unhashable annotated of a non-"typing" type annotated by an # unhashable mutable container. - PepHintMetadata( + HintPepMetadata( hint=Annotated[str, []], pep_sign=HintSignAnnotated, piths_satisfied_meta=( @@ -163,7 +163,7 @@ def __init__(self) -> None: ), # Annotated of a "typing" type. - PepHintMetadata( + HintPepMetadata( hint=Annotated[list[str], int], pep_sign=HintSignAnnotated, piths_satisfied_meta=( @@ -182,7 +182,7 @@ def __init__(self) -> None: # ................{ ANNOTATED ~ beartype : is }................ # Annotated of a non-"typing" type annotated by one beartype-specific # validator defined as a lambda function. - PepHintMetadata( + HintPepMetadata( hint=Annotated[str, IsLengthy], pep_sign=HintSignAnnotated, piths_satisfied_meta=( @@ -206,7 +206,7 @@ def __init__(self) -> None: # Annotated of a listed of a nested non-"typing" type annotated by one # beartype-specific validator defined as a lambda function. - PepHintMetadata( + HintPepMetadata( hint=list[Annotated[str, IsLengthy]], pep_sign=HintSignList, stdlib_type=list, @@ -240,7 +240,7 @@ def __init__(self) -> None: # Annotated of a non-"typing" type annotated by two or more # beartype-specific data validators all defined as functions, specified # with comma-delimited list syntax. - PepHintMetadata( + HintPepMetadata( hint=Annotated[str, IsLengthy, IsSentence, IsQuoted], pep_sign=HintSignAnnotated, piths_satisfied_meta=( @@ -275,7 +275,7 @@ def __init__(self) -> None: # Annotated of a non-"typing" type annotated by two or more # beartype-specific data validators all defined as functions, specified # with "&"-delimited operator syntax. - PepHintMetadata( + HintPepMetadata( hint=Annotated[str, IsLengthy & IsSentence & IsQuoted], pep_sign=HintSignAnnotated, piths_satisfied_meta=( @@ -309,7 +309,7 @@ def __init__(self) -> None: # Annotated of a non-"typing" type annotated by one beartype-specific # validator synthesized from all possible operators. - PepHintMetadata( + HintPepMetadata( hint=Annotated[str, IsLengthyOrUnquotedSentence], pep_sign=HintSignAnnotated, piths_satisfied_meta=( @@ -344,7 +344,7 @@ def __init__(self) -> None: # ................{ ANNOTATED ~ beartype : isequal }................ # Annotated of a non-"typing" type annotated by one beartype-specific # equality validator. - PepHintMetadata( + HintPepMetadata( hint=Annotated[list[str], IsEqualAmplyImpish], pep_sign=HintSignAnnotated, piths_satisfied_meta=( @@ -375,7 +375,7 @@ def __init__(self) -> None: # ................{ ANNOTATED ~ beartype : isattr }................ # Annotated of a non-"typing" type annotated by one beartype-specific # attribute validator. - PepHintMetadata( + HintPepMetadata( hint=Annotated[ CathecticallyEnsconceYouIn, IsAttrThisMobbedTristeOf], pep_sign=HintSignAnnotated, diff --git a/beartype_test/a00_unit/data/hint/pep/proposal/data_hintpep484.py b/beartype_test/a00_unit/data/hint/pep/proposal/data_hintpep484.py index ba384291..399d8420 100644 --- a/beartype_test/a00_unit/data/hint/pep/proposal/data_hintpep484.py +++ b/beartype_test/a00_unit/data/hint/pep/proposal/data_hintpep484.py @@ -31,9 +31,7 @@ RegexMatchType, RegexCompiledType, ) -from beartype._util.data.hint.pep.proposal.datapep484 import ( - HINT_PEP484_TYPE_FORWARDREF) -from beartype._util.data.hint.pep.sign.datapepsigns import ( +from beartype._data.hint.pep.sign.datapepsigns import ( HintSignAny, HintSignByteString, HintSignCallable, @@ -57,15 +55,18 @@ HintSignTypeVar, HintSignUnion, ) +from beartype._util.hint.pep.proposal.utilpep484 import ( + HINT_PEP484_TYPE_FORWARDREF) from beartype._util.py.utilpyversion import ( - IS_PYTHON_3_6, - IS_PYTHON_AT_LEAST_3_7, + IS_PYTHON_AT_LEAST_3_10, IS_PYTHON_AT_LEAST_3_9, + IS_PYTHON_AT_LEAST_3_7, + IS_PYTHON_3_6, ) from beartype_test.a00_unit.data.hint.util.data_hintmetacls import ( - PepHintMetadata, HintPithSatisfiedMetadata, HintPithUnsatisfiedMetadata, + HintPepMetadata, ) from beartype_test.a00_unit.data.hint.util.data_hintmetatyping import ( make_hints_metadata_typing) @@ -353,7 +354,7 @@ def add_data(data_module: 'ModuleType') -> None: # Since neither PEP 484 nor 585 comment on "ByteString" in detail (or # at all, really), this non-orthogonality remains inexplicable, # frustrating, and utterly unsurprising. We elect to merely shrug. - PepHintMetadata( + HintPepMetadata( hint=ByteString, pep_sign=HintSignByteString, stdlib_type=collections_abc.ByteString, @@ -377,7 +378,7 @@ def add_data(data_module: 'ModuleType') -> None: # module, PEP 484 explicitly supports this singleton: # When used in a type hint, the expression None is considered # equivalent to type(None). - PepHintMetadata( + HintPepMetadata( hint=None, pep_sign=HintSignNone, is_type_typing=False, @@ -400,7 +401,7 @@ def add_data(data_module: 'ModuleType') -> None: # See the "data_hintref" submodule for the latter. # Unsubscripted forward reference defined as a simple string. - PepHintMetadata( + HintPepMetadata( hint='profile.Profile', pep_sign=HintSignForwardRef, is_subscripted=False, @@ -417,7 +418,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Unsubscripted forward reference defined as a typing object. - PepHintMetadata( + HintPepMetadata( hint=HINT_PEP484_TYPE_FORWARDREF('profile.Profile'), pep_sign=HintSignForwardRef, is_subscripted=False, @@ -433,7 +434,7 @@ def add_data(data_module: 'ModuleType') -> None: # ................{ UNSUBSCRIPTED ~ typevar }................ # Generic type variable. - PepHintMetadata( + HintPepMetadata( hint=T, pep_sign=HintSignTypeVar, #FIXME: Remove after fully supporting type variables. @@ -453,7 +454,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # String type variable. - PepHintMetadata( + HintPepMetadata( hint=AnyStr, pep_sign=HintSignTypeVar, #FIXME: Remove after fully supporting type variables. @@ -479,7 +480,7 @@ def add_data(data_module: 'ModuleType') -> None: # ................{ CALLABLE }................ # Callable accepting no parameters and returning a string. - PepHintMetadata( + HintPepMetadata( hint=Callable[[], str], pep_sign=HintSignCallable, stdlib_type=collections_abc.Callable, @@ -495,7 +496,7 @@ def add_data(data_module: 'ModuleType') -> None: # ................{ CONTEXTMANAGER }................ # Context manager yielding strings. - PepHintMetadata( + HintPepMetadata( hint=ContextManager[str], pep_sign=HintSignContextManager, stdlib_type=contextlib.AbstractContextManager, @@ -516,7 +517,7 @@ def add_data(data_module: 'ModuleType') -> None: # ................{ DICT }................ # Unsubscripted "Dict" attribute. - PepHintMetadata( + HintPepMetadata( hint=Dict, pep_sign=HintSignDict, is_typevared=_IS_SIGN_TYPEVARED, @@ -538,7 +539,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Flat dictionary. - PepHintMetadata( + HintPepMetadata( hint=Dict[int, str], pep_sign=HintSignDict, stdlib_type=dict, @@ -557,7 +558,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Generic dictionary. - PepHintMetadata( + HintPepMetadata( hint=Dict[S, T], pep_sign=HintSignDict, is_typevared=True, @@ -577,7 +578,7 @@ def add_data(data_module: 'ModuleType') -> None: # ................{ GENERATOR }................ # Flat generator. - PepHintMetadata( + HintPepMetadata( hint=Generator[int, float, str], pep_sign=HintSignGenerator, stdlib_type=collections_abc.Generator, @@ -595,7 +596,7 @@ def add_data(data_module: 'ModuleType') -> None: # ................{ GENERICS ~ single }................ # Generic subclassing a single unsubscripted "typing" type. - PepHintMetadata( + HintPepMetadata( hint=Pep484GenericUnsubscriptedSingle, pep_sign=HintSignGeneric, generic_type=Pep484GenericUnsubscriptedSingle, @@ -621,7 +622,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Generic subclassing a single unparametrized "typing" type. - PepHintMetadata( + HintPepMetadata( hint=Pep484GenericUntypevaredSingle, pep_sign=HintSignGeneric, generic_type=Pep484GenericUntypevaredSingle, @@ -647,7 +648,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Generic subclassing a single parametrized "typing" type. - PepHintMetadata( + HintPepMetadata( hint=Pep484GenericTypevaredSingle, pep_sign=HintSignGeneric, generic_type=Pep484GenericTypevaredSingle, @@ -668,7 +669,7 @@ def add_data(data_module: 'ModuleType') -> None: # Generic subclassing a single parametrized "typing" type, itself # parametrized by the same type variables in the same order. - PepHintMetadata( + HintPepMetadata( hint=Pep484GenericTypevaredSingle[S, T], pep_sign=HintSignGeneric, generic_type=Pep484GenericTypevaredSingle, @@ -694,7 +695,7 @@ def add_data(data_module: 'ModuleType') -> None: # ................{ GENERICS ~ multiple }................ # Generic subclassing multiple unparametrized "typing" types *AND* a # non-"typing" abstract base class (ABC). - PepHintMetadata( + HintPepMetadata( hint=Pep484GenericUntypevaredMultiple, pep_sign=HintSignGeneric, generic_type=Pep484GenericUntypevaredMultiple, @@ -719,7 +720,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Generic subclassing multiple parametrized "typing" types. - PepHintMetadata( + HintPepMetadata( hint=Pep484GenericTypevaredShallowMultiple, pep_sign=HintSignGeneric, generic_type=Pep484GenericTypevaredShallowMultiple, @@ -742,7 +743,7 @@ def add_data(data_module: 'ModuleType') -> None: # Generic subclassing multiple indirectly parametrized "typing" types # *AND* a non-"typing" abstract base class (ABC). - PepHintMetadata( + HintPepMetadata( hint=Pep484GenericTypevaredDeepMultiple, pep_sign=HintSignGeneric, generic_type=Pep484GenericTypevaredDeepMultiple, @@ -771,7 +772,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Nested list of PEP 484-compliant generics. - PepHintMetadata( + HintPepMetadata( hint=List[Pep484GenericUntypevaredMultiple], pep_sign=HintSignList, stdlib_type=list, @@ -805,7 +806,7 @@ def add_data(data_module: 'ModuleType') -> None: # ................{ LIST }................ # Unsubscripted "List" attribute. - PepHintMetadata( + HintPepMetadata( hint=List, pep_sign=HintSignList, stdlib_type=list, @@ -833,7 +834,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # List of ignorable objects. - PepHintMetadata( + HintPepMetadata( hint=List[object], pep_sign=HintSignList, stdlib_type=list, @@ -855,7 +856,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # List of non-"typing" objects. - PepHintMetadata( + HintPepMetadata( hint=List[str], pep_sign=HintSignList, stdlib_type=list, @@ -889,7 +890,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Generic list. - PepHintMetadata( + HintPepMetadata( hint=List[T], pep_sign=HintSignList, stdlib_type=list, @@ -912,15 +913,19 @@ def add_data(data_module: 'ModuleType') -> None: # ................{ NEWTYPE }................ # New type aliasing a non-ignorable type. - PepHintMetadata( + HintPepMetadata( hint=NewType('TotallyNotAStr', str), pep_sign=HintSignNewType, is_subscripted=False, - # New types are merely pure-Python functions of the pure-Python - # function type, which is *NOT* defined by the "typing" module. - is_type_typing=False, - # Conversely, new types themselves *ARE* defined by that module. + # "typing.NewType" type hints are always declared by that module. is_typing=True, + # If the active Python interpreter targets: + # * Python >= 3.10, "typing.NewType" type hints are instances of + # that class -- which is thus declared by the "typing" module. + # * Else, "typing.NewType" type hints are merely pure-Python + # closures of the pure-Python function type -- which is *NOT* + # declared by the "typing" module. + is_type_typing=IS_PYTHON_AT_LEAST_3_10, piths_satisfied_meta=( # String constant. HintPithSatisfiedMetadata('Ishmælite‐ish, aberrant control'), @@ -936,7 +941,7 @@ def add_data(data_module: 'ModuleType') -> None: # ................{ REGEX ~ match }................ # Regular expression match of either strings or byte strings. - PepHintMetadata( + HintPepMetadata( hint=Match, pep_sign=HintSignMatch, stdlib_type=RegexMatchType, @@ -956,7 +961,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Regular expression match of only strings. - PepHintMetadata( + HintPepMetadata( hint=Match[str], pep_sign=HintSignMatch, stdlib_type=RegexMatchType, @@ -975,7 +980,7 @@ def add_data(data_module: 'ModuleType') -> None: # ................{ REGEX ~ pattern }................ # Regular expression pattern of either strings or byte strings. - PepHintMetadata( + HintPepMetadata( hint=Pattern, pep_sign=HintSignPattern, stdlib_type=RegexCompiledType, @@ -992,7 +997,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Regular expression pattern of only strings. - PepHintMetadata( + HintPepMetadata( hint=Pattern[str], pep_sign=HintSignPattern, stdlib_type=RegexCompiledType, @@ -1012,7 +1017,7 @@ def add_data(data_module: 'ModuleType') -> None: # parametrized by one or more type variables under any Python version, # unlike most other unsubscripted "typing" attributes originating from # container types. Non-orthogonality, thy name is the "typing" module. - PepHintMetadata( + HintPepMetadata( hint=Tuple, pep_sign=HintSignTuple, stdlib_type=tuple, @@ -1041,7 +1046,7 @@ def add_data(data_module: 'ModuleType') -> None: # TypeError: Too few parameters for List; actual 0, expected 1 # >>> List[[]] # TypeError: Parameters to generic types must be types. Got []. - PepHintMetadata( + HintPepMetadata( hint=Tuple[()], pep_sign=HintSignTuple, stdlib_type=tuple, @@ -1066,7 +1071,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Fixed-length tuple of only ignorable child hints. - PepHintMetadata( + HintPepMetadata( hint=Tuple[Any, object,], pep_sign=HintSignTuple, stdlib_type=tuple, @@ -1091,7 +1096,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Fixed-length tuple of at least one ignorable child hint. - PepHintMetadata( + HintPepMetadata( hint=Tuple[float, Any, str,], pep_sign=HintSignTuple, stdlib_type=tuple, @@ -1140,7 +1145,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Nested fixed-length tuple of at least one ignorable child hint. - PepHintMetadata( + HintPepMetadata( hint=Tuple[Tuple[float, Any, str,], ...], pep_sign=HintSignTuple, stdlib_type=tuple, @@ -1192,7 +1197,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Generic fixed-length tuple. - PepHintMetadata( + HintPepMetadata( hint=Tuple[S, T], pep_sign=HintSignTuple, is_typevared=True, @@ -1217,7 +1222,7 @@ def add_data(data_module: 'ModuleType') -> None: # ................{ TUPLE ~ variadic }................ # Variadic tuple. - PepHintMetadata( + HintPepMetadata( hint=Tuple[str, ...], pep_sign=HintSignTuple, stdlib_type=tuple, @@ -1250,7 +1255,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Generic variadic tuple. - PepHintMetadata( + HintPepMetadata( hint=Tuple[T, ...], pep_sign=HintSignTuple, is_typevared=True, @@ -1271,7 +1276,7 @@ def add_data(data_module: 'ModuleType') -> None: # ................{ TYPE }................ # Unsubscripted "Type" singleton. - PepHintMetadata( + HintPepMetadata( hint=Type, pep_sign=HintSignType, is_typevared=_IS_SIGN_TYPEVARED, @@ -1289,7 +1294,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Builtin type. - PepHintMetadata( + HintPepMetadata( hint=Type[dict], pep_sign=HintSignType, stdlib_type=type, @@ -1304,7 +1309,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Generic type. - PepHintMetadata( + HintPepMetadata( hint=Type[T], pep_sign=HintSignType, is_typevared=True, @@ -1339,7 +1344,7 @@ def add_data(data_module: 'ModuleType') -> None: # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # Ignorable unsubscripted "Union" attribute. - PepHintMetadata( + HintPepMetadata( hint=Union, pep_sign=HintSignUnion, is_ignorable=True, @@ -1349,7 +1354,7 @@ def add_data(data_module: 'ModuleType') -> None: # exercising a prominent edge case when raising human-readable # exceptions describing the failure of passed parameters or returned # values to satisfy this union. - PepHintMetadata( + HintPepMetadata( hint=Union[int, Sequence[str]], pep_sign=HintSignUnion, piths_satisfied_meta=( @@ -1405,7 +1410,7 @@ def add_data(data_module: 'ModuleType') -> None: # exercising a prominent edge case when raising human-readable # exceptions describing the failure of passed parameters or returned # values to satisfy this union. - PepHintMetadata( + HintPepMetadata( hint=Union[dict, float, int, Sequence[Union[dict, float, int, MutableSequence[str]]]], pep_sign=HintSignUnion, @@ -1493,7 +1498,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Union of one non-"typing" type and one concrete generic. - PepHintMetadata( + HintPepMetadata( hint=Union[str, Iterable[Tuple[S, T]]], pep_sign=HintSignUnion, is_typevared=True, @@ -1504,7 +1509,7 @@ def add_data(data_module: 'ModuleType') -> None: # optimizations leveraging PEP 572-style assignment expressions. # Nested union of multiple non-"typing" types. - PepHintMetadata( + HintPepMetadata( hint=List[Union[int, str,]], pep_sign=HintSignList, stdlib_type=list, @@ -1554,7 +1559,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Nested union of one non-"typing" type and one "typing" type. - PepHintMetadata( + HintPepMetadata( hint=Sequence[Union[str, ByteString]], pep_sign=HintSignSequence, stdlib_type=collections_abc.Sequence, @@ -1602,7 +1607,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Nested union of no non-"typing" type and multiple "typing" types. - PepHintMetadata( + HintPepMetadata( hint=MutableSequence[Union[ByteString, Callable]], pep_sign=HintSignMutableSequence, stdlib_type=collections_abc.MutableSequence, @@ -1653,14 +1658,14 @@ def add_data(data_module: 'ModuleType') -> None: # ................{ UNION ~ optional }................ # Ignorable unsubscripted "Optional" attribute. - PepHintMetadata( + HintPepMetadata( hint=Optional, pep_sign=HintSignOptional, is_ignorable=True, ), # Optional isinstance()-able "typing" type. - PepHintMetadata( + HintPepMetadata( hint=Optional[Sequence[str]], # Subscriptions of the "Optional" attribute reduce to # fundamentally different unsubscripted typing attributes depending @@ -1714,7 +1719,7 @@ def add_data(data_module: 'ModuleType') -> None: data_module.HINTS_PEP_META.extend(( # ..............{ UNSUBSCRIPTED }.............. # Unsubscripted "Hashable" attribute. - PepHintMetadata( + HintPepMetadata( hint=Hashable, pep_sign=HintSignHashable, stdlib_type=collections_abc.Hashable, @@ -1738,7 +1743,7 @@ def add_data(data_module: 'ModuleType') -> None: ), # Unsubscripted "Sized" attribute. - PepHintMetadata( + HintPepMetadata( hint=Sized, pep_sign=HintSignSized, stdlib_type=collections_abc.Sized, @@ -1766,7 +1771,7 @@ def add_data(data_module: 'ModuleType') -> None: # supported *ONLY* under Python >= 3.9, which implements these # tests in an ambiguous (albeit efficient) manner effectively # indistinguishable from PEP 585-compliant type hints. - PepHintMetadata( + HintPepMetadata( hint=Pep484GenericUnsubscriptedSingle[str], pep_sign=HintSignGeneric, generic_type=Pep484GenericUnsubscriptedSingle, diff --git a/beartype_test/a00_unit/data/hint/util/data_hintmetacls.py b/beartype_test/a00_unit/data/hint/util/data_hintmetacls.py index 7deb5ad8..8e95d73a 100644 --- a/beartype_test/a00_unit/data/hint/util/data_hintmetacls.py +++ b/beartype_test/a00_unit/data/hint/util/data_hintmetacls.py @@ -10,7 +10,7 @@ ''' # ....................{ IMPORTS }.................... -from beartype._util.data.hint.pep.sign.datapepsigncls import HintSign +from beartype._data.hint.pep.sign.datapepsigncls import HintSign from typing import Optional # ....................{ HINTS }.................... @@ -20,7 +20,7 @@ ''' # ....................{ CLASSES ~ hint : superclass }.................... -class NonPepHintMetadata(object): +class HintNonPepMetadata(object): ''' **PEP-noncompliant type hint metadata** (i.e., dataclass whose instance variables describe a type hint that is either PEP-noncompliant or *mostly* @@ -110,7 +110,7 @@ def __repr__(self) -> str: )) # ....................{ CLASSES ~ hint : subclass }.................... -class PepHintMetadata(NonPepHintMetadata): +class HintPepMetadata(HintNonPepMetadata): ''' **PEP-compliant type hint metadata** (i.e., dataclass whose instance variables describe a PEP-compliant type hint with metadata applicable to @@ -176,7 +176,7 @@ class PepHintMetadata(NonPepHintMetadata): Defaults to ``None``. All remaining keyword arguments are passed as is to the superclass - :meth:`NonPepHintMetadata.__init__` method. + :meth:`HintNonPepMetadata.__init__` method. ''' # ..................{ INITIALIZERS }.................. diff --git a/beartype_test/a00_unit/data/hint/util/data_hintmetatyping.py b/beartype_test/a00_unit/data/hint/util/data_hintmetatyping.py index 11e0d012..1f55fcbf 100644 --- a/beartype_test/a00_unit/data/hint/util/data_hintmetatyping.py +++ b/beartype_test/a00_unit/data/hint/util/data_hintmetatyping.py @@ -14,7 +14,7 @@ import_module_typingextensions_attr_or_none, ) from beartype_test.a00_unit.data.hint.util.data_hintmetacls import ( - PepHintMetadata) + HintPepMetadata) from typing import Any, Callable, Dict, Tuple # ....................{ CONSTANTS }.................... @@ -34,10 +34,10 @@ def make_hints_metadata_typing( # Optional parameters. hint_maker: Callable[[Any,], Any] = lambda hint: hint, -) -> Tuple[PepHintMetadata]: +) -> Tuple[HintPepMetadata]: ''' Create and return a tuple of zero or more **typing type hint metadata - objects** (i.e., :class:`PepHintMetadata` instances describing type hints + objects** (i.e., :class:`HintPepMetadata` instances describing type hints originating from typing modules available under the active Python interpreter), depending on which typing modules are importable and which attributes importable from those typing modules. @@ -45,8 +45,8 @@ def make_hints_metadata_typing( Specifically, this function returns a tuple containing: * If the :mod:`typing` module declares an attribute with the passed name, - a new :class:`PepHintMetadata` instance created by passing the - :meth:`PepHintMetadata.__init__` method: + a new :class:`HintPepMetadata` instance created by passing the + :meth:`HintPepMetadata.__init__` method: * A ``hint`` parameter whose value is that returned by calling the passed ``hint_maker`` callable passed that attribute imported from that @@ -56,8 +56,8 @@ def make_hints_metadata_typing( * If the third-party :mod:`typing_extensions` module is both importable *and* declares an attribute with the passed name, - a new :class:`PepHintMetadata` instance created by passing the - :meth:`PepHintMetadata.__init__` method similar parameters. + a new :class:`HintPepMetadata` instance created by passing the + :meth:`HintPepMetadata.__init__` method similar parameters. Attributes ---------- @@ -65,8 +65,8 @@ def make_hints_metadata_typing( Unqualified name of the attribute to be imported from a typing module. hint_metadata: Dict[str, Any] Dictionary of additional keyword arguments to be passed to the - :meth:`PepHintMetadata.__init__` method for each - :class:`PepHintMetadata` instance created by this function. + :meth:`HintPepMetadata.__init__` method for each + :class:`HintPepMetadata` instance created by this function. hint_maker : Callable[[Any,], Any] **PEP-compliant type hint factory** (i.e., callable accepting this attribute imported from a typing module and returning a PEP-compliant @@ -76,7 +76,7 @@ def make_hints_metadata_typing( Returns ---------- - Tuple[PepHintMetadata] + Tuple[HintPepMetadata] Tuple of zero or more typing type hint metadata objects. ''' assert isinstance(typing_attr_basename, str), ( @@ -93,7 +93,7 @@ def make_hints_metadata_typing( func=hint_maker, func_args_len_flexible=1) # Else, this hint factory accepts exactly one argument. - # List of all "PepHintMetadata" instances to be returned as a tuple. + # List of all "HintPepMetadata" instances to be returned as a tuple. hints_metadata_typing = [] # For each function importing typing attributes from a given module... @@ -107,8 +107,8 @@ def make_hints_metadata_typing( # Type hint synthesized from this attribute by this hint factory. hint = hint_maker(typing_attr) - # Append a new "PepHintMetadata" instance encapsulating this hint. - hint_metadata_typing = PepHintMetadata(hint=hint, **hint_metadata) + # Append a new "HintPepMetadata" instance encapsulating this hint. + hint_metadata_typing = HintPepMetadata(hint=hint, **hint_metadata) # Append this instance to this list. hints_metadata_typing.append(hint_metadata_typing) diff --git a/pytest b/pytest index 5b9e6170..a9ccf584 100755 --- a/pytest +++ b/pytest @@ -24,11 +24,11 @@ # Array of all arguments with which to invoke Python. Dismantled, this is: # * "-X dev", enabling the Python Development Mode (PDM). See also commentary # for the ${PYTHONDEVMODE} shell variable in the "tox.ini" file. -# PYTHON_ARGS=( command python3 -X dev ) +PYTHON_ARGS=( command python3 -X dev ) # PYTHON_ARGS=( command python3.6 -X dev ) # PYTHON_ARGS=( command python3.7 -X dev ) # PYTHON_ARGS=( command python3.8 -X dev ) -PYTHON_ARGS=( command python3.9 -X dev ) +# PYTHON_ARGS=( command python3.9 -X dev ) # PYTHON_ARGS=( command python3.10 -X dev ) # PYTHON_ARGS=( command pypy3.7 -X dev )