Esempio n. 1
0
def get_cause_or_none_sequence_standard(
    sleuth: CauseSleuth) -> 'Optional[str]':
    '''
    Human-readable string describing the failure of the passed arbitrary object
    to satisfy the passed **PEP-compliant standard sequence type hint** (i.e.,
    PEP-compliant type hint accepting exactly one subscripted type hint
    argument constraining *all* items of this object, which necessarily
    satisfies the :class:`collections.abc.Sequence` protocol with guaranteed
    ``O(1)`` indexation across all sequence items) if this object actually
    fails to satisfy this hint *or* ``None`` otherwise (i.e., if this object
    satisfies this hint).

    Parameters
    ----------
    sleuth : CauseSleuth
        Type-checking error cause sleuth.
    '''
    assert isinstance(sleuth, CauseSleuth), f'{repr(sleuth)} not cause sleuth.'
    assert sleuth.hint_sign in HINT_PEP_SIGNS_SEQUENCE_STANDARD, (
        f'{repr(sleuth.hint)} not standard sequence.')

    # Assert this sequence was subscripted by exactly one argument. Note that
    # the "typing" module should have already guaranteed this on our behalf.
    assert len(sleuth.hint_childs) == 1, (
        f'Standard sequence {repr(sleuth.hint)} subscripted by '
        f'multiple arguments.')

    # Non-"typing" class originating this attribute (e.g., "list" for "List").
    hint_type_origin = get_hint_pep_type_origin(sleuth.hint)

    # If this pith is *NOT* an instance of this class, defer to the getter
    # function handling non-"typing" classes.
    if not isinstance(sleuth.pith, hint_type_origin):
        return get_cause_or_none_type(sleuth.permute(hint=hint_type_origin))

    # Else, this pith is an instance of this class and is thus a sequence.
    # Defer to the getter function supporting simple sequences.
    return _get_cause_or_none_sequence(sleuth)
Esempio n. 2
0
def get_cause_or_none_generic(sleuth: CauseSleuth) -> 'Optional[str]':
    '''
    Human-readable string describing the failure of the passed arbitrary object
    to satisfy the passed `PEP 484`_-compliant **generic** (i.e., type hint
    subclassing a combination of one or more of the :mod:`typing.Generic`
    superclass, the :mod:`typing.Protocol` superclass, and/or other
    :mod:`typing` non-class pseudo-superclasses) if this object actually fails
    to satisfy this hint *or* ``None`` otherwise (i.e., if this object
    satisfies this hint).

    Parameters
    ----------
    sleuth : CauseSleuth
        Type-checking error cause sleuth.
    '''
    assert isinstance(sleuth, CauseSleuth), f'{repr(sleuth)} not cause sleuth.'
    assert isinstance(sleuth.hint, type), f'{repr(sleuth.hint)} not class.'
    assert sleuth.hint_sign is Generic, (
        f'{repr(sleuth.hint_sign)} not generic.')

    # If this pith is *NOT* an instance of this generic, defer to the getter
    # function handling non-"typing" classes.
    if not isinstance(sleuth.pith, sleuth.hint):
        return get_cause_or_none_type(sleuth)
    # Else, this pith is an instance of this generic.

    # For each pseudo-superclass of this generic...
    for hint_base in sleuth.hint_childs:
        # If this pseudo-superclass is an actual superclass, this
        # pseudo-superclass is effectively ignorable. Why? Because the
        # isinstance() call above already type-checked this pith against the
        # generic subclassing this superclass and thus this superclass as well.
        # In this case, skip to the next pseudo-superclass.
        if isinstance(hint_base, type):
            continue
        # Else, this pseudo-superclass is *NOT* an actual class.
        #
        # If this pseudo-superclass is neither a PEP 585-compliant type hint
        # *NOR* a PEP-compliant type hint defined by the "typing" module,
        # reduce this pseudo-superclass to a real superclass originating this
        # pseudo-superclass. See commentary in the "_pephint" submodule.
        elif not (is_hint_pep585(hint_base) and is_hint_pep_typing(hint_base)):
            hint_base = get_hint_pep484_generic_base_erased_from_unerased(
                hint_base)
        # Else, this pseudo-superclass is defined by the "typing" module.

        # If this superclass is ignorable, do so.
        if is_hint_ignorable(hint_base):
            continue
        # Else, this superclass is unignorable.

        # Human-readable string describing the failure of this pith to satisfy
        # this pseudo-superclass if this pith actually fails to satisfy
        # this pseudo-superclass *or* "None" otherwise.
        # print(f'tuple pith: {pith_item}\ntuple hint child: {hint_child}')
        pith_base_cause = sleuth.permute(hint=hint_base).get_cause_or_none()

        # If this pseudo-superclass is the cause of this failure, return a
        # substring describing this failure by embedding this failure (itself
        # intended to be embedded in a longer string).
        if pith_base_cause is not None:
            # print(f'tuple pith: {sleuth_copy.pith}\ntuple hint child: {sleuth_copy.hint}\ncause: {pith_item_cause}')
            return f'generic base {repr(hint_base)} {pith_base_cause}'
        # Else, this pseudo-superclass is *NOT* the cause of this failure.
        # Silently continue to the next.

    # Return "None", as this pith satisfies both this generic itself *AND* all
    # pseudo-superclasses subclassed by this generic, implying this pith to
    # deeply satisfy this hint.
    return None
Esempio n. 3
0
def get_cause_or_none_tuple(sleuth: CauseSleuth) -> 'Optional[str]':
    '''
    Human-readable string describing the failure of the passed arbitrary object
    to satisfy the passed **PEP-compliant standard sequence type hint** (i.e.,
    PEP-compliant type hint accepting exactly one subscripted type hint
    argument constraining *all* items of this object, which necessarily
    satisfies the :class:`collections.abc.Sequence` protocol with guaranteed
    ``O(1)`` indexation across all sequence items) if this object actually
    fails to satisfy this hint *or* ``None`` otherwise (i.e., if this object
    satisfies this hint).

    Parameters
    ----------
    sleuth : CauseSleuth
        Type-checking error cause sleuth.
    '''
    assert isinstance(sleuth, CauseSleuth), f'{repr(sleuth)} not cause sleuth.'
    assert sleuth.hint_sign in HINT_PEP_SIGNS_TUPLE, (
        f'{repr(sleuth.hint_sign)} not tuple.')

    # If this pith is *NOT* an instance of this class, defer to the getter
    # function handling non-"typing" classes.
    if not isinstance(sleuth.pith, tuple):
        return get_cause_or_none_type(sleuth.permute(hint=tuple))

    # If this hint is...
    if (
        # This tuple is subscripted by exactly two child hints *AND*...
        len(sleuth.hint_childs) == 2 and
        # The second child hint is just an unquoted ellipsis...
        sleuth.hint_childs[1] is Ellipsis
    ):
    # Then this hint is of the variadic form "Tuple[{typename}, ...]", typing a
    # tuple accepting a variadic number of items all satisfying the
    # child hint "{typename}". Since this case semantically reduces to a simple
    # sequence, defer to the getter function supporting simple sequences.
        return _get_cause_or_none_sequence(sleuth)
    # Else, this hint is of the fixed-length form "Tuple[{typename1}, ...,
    # {typenameN}]", typing a tuple accepting a fixed number of items each
    # satisfying a unique child hint.
    #
    # If this hint is the empty fixed-length tuple, validate this pith to be
    # the empty tuple.
    elif is_hint_pep_tuple_empty(sleuth.hint):
        # If this pith is non-empty and thus fails to satisfy this hint...
        if sleuth.pith:
            # Truncated representation of this tuple.
            pith_repr = get_object_representation(sleuth.pith)

            # Return a substring describing this failure.
            return f'tuple {pith_repr} non-empty'
        # Else, this pith is the empty tuple and thus satisfies this hint.
    # Else, this hint is a standard fixed-length tuple. In this case...
    else:
        # If this pith and hint are of differing lengths, this tuple fails to
        # satisfy this hint. In this case...
        if len(sleuth.pith) != len(sleuth.hint_childs):
            # Truncated representation of this tuple.
            pith_repr = get_object_representation(sleuth.pith)

            # Return a substring describing this failure.
            return (
                f'tuple {pith_repr} length '
                f'{len(sleuth.pith)} not {len(sleuth.hint_childs)}'
            )
        # Else, this pith and hint are of the same length.

        # For each enumerated item of this tuple...
        for pith_item_index, pith_item in enumerate(sleuth.pith):
            # Child hint corresponding to this tuple item. Since this pith and
            # hint are of the same length, this child hint exists.
            hint_child = sleuth.hint_childs[pith_item_index]

            # If this child hint is ignorable, continue to the next.
            if is_hint_ignorable(hint_child):
                continue
            # Else, this child hint is unignorable.

            # Human-readable string describing the failure of this tuple item
            # to satisfy this child hint if this item actually fails to satisfy
            # this child hint *or* "None" otherwise.
            # print(f'tuple pith: {pith_item}\ntuple hint child: {hint_child}')
            # sleuth_copy = sleuth.permute(pith=pith_item, hint=hint_child)
            # pith_item_cause = sleuth_copy.get_cause_or_none()
            pith_item_cause = sleuth.permute(
                pith=pith_item, hint=hint_child).get_cause_or_none()

            # If this item is the cause of this failure, return a substring
            # describing this failure by embedding this failure (itself
            # intended to be embedded in a longer string).
            if pith_item_cause is not None:
                # print(f'tuple pith: {sleuth_copy.pith}\ntuple hint child: {sleuth_copy.hint}\ncause: {pith_item_cause}')
                return f'tuple item {pith_item_index} {pith_item_cause}'
            # Else, this item is *NOT* the cause of this failure. Silently
            # continue to the next.

    # Return "None", as all items of this fixed-length tuple are valid,
    # implying this pith to deeply satisfy this hint.
    return None