def test_get_hint_pep_sign_fail() -> None:
    '''
    Test unsuccessful usage of the
    :func:`beartype._util.hint.pep.utilhintpepget.get_hint_pep_sign` getter.
    '''

    # Defer heavyweight imports.
    from beartype.roar import (
        BeartypeDecorHintPepException,
        BeartypeDecorHintPepSignException,
    )
    from beartype._util.hint.pep.utilhintpepget import get_hint_pep_sign
    from beartype_test.unit.data.hint.data_hint import (NOT_HINTS_PEP,
                                                        NonPepCustomFakeTyping)

    # Assert this getter raises the expected exception for an instance of a
    # class erroneously masquerading as a "typing" class.
    with raises(BeartypeDecorHintPepSignException):
        # Localize this return value to simplify debugging.
        hint_pep_sign = get_hint_pep_sign(NonPepCustomFakeTyping())

    # Assert this getter raises the expected exception for non-"typing" hints.
    for not_hint_pep in NOT_HINTS_PEP:
        with raises(BeartypeDecorHintPepException):
            # Localize this return value to simplify debugging.
            hint_pep_sign = get_hint_pep_sign(not_hint_pep)
def test_get_hint_pep_sign_pass() -> None:
    '''
    Test successful usage of the
    :func:`beartype._util.hint.pep.utilhintpepget.get_hint_pep_sign` getter.
    '''

    # Defer heavyweight imports.
    from beartype._util.hint.pep.utilhintpepget import get_hint_pep_sign
    from beartype_test.unit.data.hint.pep.data_hintpep import HINTS_PEP_META

    # Assert this getter returns the expected unsubscripted "typing" attribute
    # for all PEP-compliant type hints associated with such an attribute.
    for hint_pep_meta in HINTS_PEP_META:
        assert get_hint_pep_sign(
            hint_pep_meta.hint) == (hint_pep_meta.pep_sign)
Exemple #3
0
def is_hint_pep_supported(hint: object) -> bool:
    '''
    ``True`` only if the passed object is a **PEP-compliant supported type
    hint** (i.e., :mod:`beartype`-agnostic annotation compliant with
    annotation-centric PEPs currently supported by the
    :func:`beartype.beartype` decorator).

    This tester is memoized for efficiency.

    Caveats
    ----------
    **This tester only shallowly inspects this object.** If this object is a
    subscripted PEP-compliant type hint (e.g., ``Union[str, List[int]]``), this
    tester ignores all subscripted arguments (e.g., ``List[int]``) on this hint
    and may thus return false positives for hints that are directly supported
    but whose subscripted arguments are not.

    To deeply inspect this object, iteratively call this tester during a
    recursive traversal over each subscripted argument of this object.

    Parameters
    ----------
    hint : object
        Object to be inspected.

    Returns
    ----------
    bool
        ``True`` only if this object is a supported PEP-compliant type hint.
    '''

    # If this hint is *NOT* PEP-compliant, immediately return false.
    if not is_hint_pep(hint):
        return False
    # Else, this hint is PEP-compliant.

    # Avoid circular import dependencies.
    from beartype._util.hint.pep.utilhintpepget import get_hint_pep_sign

    # Sign uniquely identifying this hint.
    hint_pep_sign = get_hint_pep_sign(hint)

    # Return true only if this sign is supported.
    return is_hint_pep_sign_supported(hint_pep_sign)
Exemple #4
0
    def __init__(
        self,
        func: object,
        pith: object,
        hint: object,
        cause_indent: str,
        exception_label: str,
    ) -> None:
        '''
        Initialize this object.
        '''
        assert callable(func), f'{repr(func)} not callable.'
        assert isinstance(cause_indent,
                          str), (f'{repr(cause_indent)} not string.')
        assert isinstance(exception_label,
                          str), (f'{repr(exception_label)} not string.')

        # Classify all passed parameters.
        self.func = func
        self.pith = pith
        self.hint = hint
        self.cause_indent = cause_indent
        self.exception_label = exception_label

        # Nullify all remaining parameters for safety.
        self.hint_sign = None
        self.hint_childs = None

        # ................{ REDUCTION                         }................
        #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        # CAVEATS: Synchronize changes here with the corresponding block of the
        # beartype._decor._code._pep._pephint.pep_code_check_hint() function.
        #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        # This logic reduces the currently visited hint to an arbitrary object
        # associated with this hint when this hint conditionally satisfies any
        # of various conditions.
        #
        # ................{ REDUCTION ~ pep 484               }................
        # If this is a PEP 484-compliant new type hint, reduce this hint to the
        # user-defined class aliased by this hint. Although this logic could
        # also be performed below, doing so here simplifies matters.
        if is_hint_pep484_newtype(self.hint):
            self.hint = get_hint_pep484_newtype_class(self.hint)
        # ................{ REDUCTION ~ pep 544               }................
        # If this is a PEP 484-compliant IO generic base class *AND* the active
        # Python interpreter targets at least Python >= 3.8 and thus supports
        # PEP 544-compliant protocols, reduce this functionally useless hint to
        # the corresponding functionally useful beartype-specific PEP
        # 544-compliant protocol implementing this hint.
        elif is_hint_pep544_io_generic(self.hint):
            self.hint = get_hint_pep544_io_protocol_from_generic(self.hint)
        # ................{ REDUCTION ~ pep 593               }................
        # If this is a PEP 593-compliant type metahint, ignore all annotations
        # on this hint (i.e., "hint_curr.__metadata__" tuple) by reducing this
        # hint to its origin (e.g., "str" in "Annotated[str, 50, False]").
        elif is_hint_pep593(self.hint):
            self.hint = get_hint_pep593_hint(self.hint)
        # ................{ REDUCTION ~ end                   }................

        # If this hint is PEP-compliant...
        if is_hint_pep(self.hint):
            # Arbitrary object uniquely identifying this hint.
            self.hint_sign = get_hint_pep_sign(self.hint)

            # Tuple of either...
            self.hint_childs = (
                # If this hint is a generic, the one or more unerased
                # pseudo-superclasses originally subclassed by this hint.
                get_hint_pep_generic_bases_unerased(self.hint)
                if is_hint_pep_generic(self.hint) else
                # Else, the zero or more arguments subscripting this hint.
                get_hint_pep_args(self.hint))
Exemple #5
0
    def hint(self, hint: Any) -> None:
        '''
        Set the type hint to validate this object against.
        '''

        # ................{ REDUCTION                         }................
        #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        # CAVEATS: Synchronize changes here with the corresponding block of the
        # beartype._decor._code._pep._pephint.pep_code_check_hint() function.
        #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        #
        # This logic reduces the currently visited hint to an arbitrary object
        # associated with this hint when this hint conditionally satisfies any
        # of various conditions.
        #
        # ................{ REDUCTION ~ pep 484               }................
        # If this is the PEP 484-compliant "None" singleton, reduce this hint
        # to the type of that singleton. While not explicitly defined by the
        # "typing" module, PEP 484 explicitly supports this singleton:
        #     When used in a type hint, the expression None is considered
        #     equivalent to type(None).
        if hint is None:
            hint = NoneType
        # If this is a PEP 484-compliant new type hint, reduce this hint to the
        # user-defined class aliased by this hint. Although this logic could
        # also be performed below, doing so here simplifies matters.
        elif is_hint_pep484_newtype(hint):
            hint = get_hint_pep484_newtype_class(hint)
        # ................{ REDUCTION ~ pep 544               }................
        # If this is a PEP 484-compliant IO generic base class *AND* the active
        # Python interpreter targets at least Python >= 3.8 and thus supports
        # PEP 544-compliant protocols, reduce this functionally useless hint to
        # the corresponding functionally useful beartype-specific PEP
        # 544-compliant protocol implementing this hint.
        #
        # Note that PEP 484-compliant IO generic base classes are technically
        # usable under Python < 3.8 (e.g., by explicitly subclassing those
        # classes from third-party classes). Ergo, we can neither safely emit
        # warnings nor raise exceptions on visiting these classes under *ANY*
        # Python version.
        elif is_hint_pep544_io_generic(hint):
            hint = get_hint_pep544_io_protocol_from_generic(hint)
        # ................{ REDUCTION ~ pep 593               }................
        # If this is a PEP 593-compliant type metahint, ignore all annotations
        # on this hint (i.e., "hint_curr.__metadata__" tuple) by reducing this
        # hint to its origin (e.g., "str" in "Annotated[str, 50, False]").
        elif is_hint_pep593(hint):
            hint = get_hint_pep593_hint(hint)
        # ................{ REDUCTION ~ end                   }................

        # If this hint is PEP-compliant...
        if is_hint_pep(hint):
            # Arbitrary object uniquely identifying this hint.
            self.hint_sign = get_hint_pep_sign(hint)

            # Tuple of either...
            self.hint_childs = (
                # If this hint is a generic, the one or more unerased
                # pseudo-superclasses originally subclassed by this hint.
                get_hint_pep_generic_bases_unerased(hint)
                if is_hint_pep_generic(hint) else
                # Else, the zero or more arguments subscripting this hint.
                get_hint_pep_args(hint))

        # Classify this hint *AFTER* all other assignments above.
        self._hint = hint
Exemple #6
0
def is_hint_pep_ignorable(hint: object) -> bool:
    '''
    ``True`` only if the passed object is a **deeply ignorable PEP-compliant
    type hint** (i.e., PEP-compliant type hint shown to be ignorable only after
    recursively inspecting the contents of this hint).

    This tester is intentionally *not* memoized (e.g., by the
    :func:`callable_cached` decorator), as this tester is only safely callable
    by the memoized parent
    :func:`beartype._util.hint.utilhinttest.is_hint_ignorable` tester.

    Parameters
    ----------
    hint : object
        Object to be inspected.

    Returns
    ----------
    bool
        ``True`` only if this object is a deeply ignorable PEP-compliant type
        hint.

    Warnings
    ----------
    BeartypeDecorHintPepIgnorableDeepWarning
        If this object is a deeply ignorable PEP-compliant type hint. Why?
        Because deeply ignorable PEP-compliant type hints convey *no*
        meaningful semantics but superficially appear to do so. Consider
        ``Union[str, List[int], NewType('MetaType', Annotated[object, 53])]``,
        for example; this PEP-compliant type hint effectively reduces to
        ``typing.Any`` and thus conveys *no* meaningful semantics despite
        superficially appearing to do so.
    '''

    # Avoid circular import dependencies.
    from beartype._util.hint.pep.utilhintpepget import get_hint_pep_sign

    #FIXME: Remove this *AFTER* properly supporting type variables. For
    #now, ignoring type variables is required ta at least shallowly support
    #generics parametrized by one or more type variables.

    # If this hint is a type variable, return true. Type variables require
    # non-trivial and currently unimplemented decorator support.
    if is_hint_pep_typevar(hint):
        return True

    # Sign uniquely identifying this hint.
    hint_sign = get_hint_pep_sign(hint)

    # For each PEP-specific function testing whether this hint is an ignorable
    # type hint fully compliant with that PEP...
    for is_hint_pep_ignorable_tester in _IS_HINT_PEP_IGNORABLE_TESTERS:
        # True only if this hint is a ignorable under this PEP, False only if
        # this hint is unignorable under this PEP, and None if this hint is
        # *NOT* compliant with this PEP.
        is_hint_pep_ignorable_or_none = is_hint_pep_ignorable_tester(
            hint, hint_sign)

        # If this hint is compliant with this PEP...
        # print(f'{is_hint_pep_ignorable_or_none} = {is_hint_pep_ignorable_tester}({hint}, {hint_sign})')
        if is_hint_pep_ignorable_or_none is not None:
            #FIXME: Uncomment *AFTER* we properly support type variables. Since
            #we currently ignore type variables, uncommenting this now would
            #raise spurious warnings for otherwise unignorable and absolutely
            #unsuspicious generics and protocols parametrized by type
            #variables, which would be worse than the existing situation.

            # # If this hint is ignorable under this PEP, warn the user this hint
            # # is deeply ignorable. (See the docstring for justification.)
            # if is_hint_pep_ignorable_or_none:
            #     warn(
            #         (
            #             f'Ignorable PEP type hint {repr(hint)} '
            #             f'typically not intended to be ignored.'
            #         ),
            #         BeartypeDecorHintPepIgnorableDeepWarning,
            #     )

            # Return this boolean.
            return is_hint_pep_ignorable_or_none
        # Else, this hint is *NOT* compliant with this PEP. In this case,
        # silently continue to the next such tester.

    # Else, this hint is *NOT* deeply ignorable. In this case, return false.
    return False