Skip to content

Commit

Permalink
Parametrized generic detection x 1.
Browse files Browse the repository at this point in the history
This commit is the first in a commit chain detecting parametrized
generics (i.e., user-defined generics subscripted by one or more type
variables) en-route to resolving issue #29, kindly submitted by
indefatigable test engineer and anthropomorphic Siberian Husky @eehusky.
Specifically, this commit adds a sample type hint exercising this edge
case to our test suite and documents a likely fix. (*Common dominator!*)
  • Loading branch information
leycec committed Feb 28, 2021
1 parent e25cde7 commit 6381caa
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 1 deletion.
4 changes: 3 additions & 1 deletion beartype/_util/hint/pep/utilhintpepget.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ class like :class:`list` or :class:`tuple` *or* an abstract base class
# superficially subclassing at least one non-class PEP-compliant object),
# return the "typing.Generic" abstract base class (ABC) generically -- get
# it? -- identifying PEP-compliant generics. Note that:
#
# * *ALL* PEP 484-compliant generics and PEP 544-compliant protocols are
# guaranteed by the "typing" module to subclass this ABC regardless of
# whether those generics originally did so explicitly. How? By type
Expand All @@ -438,7 +439,8 @@ class like :class:`list` or :class:`tuple` *or* an abstract base class
#
# Note that generics *CANNOT* be detected by the general-purpose logic
# performed below, as this ABC does *NOT* define a __repr__() dunder method
# returning a string prefixed by the "typing." substring.
# returning a string prefixed by the "typing." substring. Ergo, we
# necessarily detect generics with an explicit test instead.
elif is_hint_pep_generic(hint):
return Generic
# Else, this hint is *NOT* a generic.
Expand Down
15 changes: 15 additions & 0 deletions beartype/_util/hint/pep/utilhintpeptest.py
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,21 @@ def is_hint_pep_generic(hint: object) -> bool:

# Return true only if this hint is...
return (
#FIXME: Fascinating. Parametrized generics (e.g.,
#"Pep484GenericTypevaredSingle[S, T]") are *NOT* types, which means
#things now get complex. Refactor as follows:
#* Memoize this tester, which will no longer be trivial.
#* Detect parametrization by calling "get_hint_pep_typevars(hint)" and
# where the return tuple is non-empty:
# * Reduce this hint to "hint.__origin__". That *SHOULD* be safe, but
# that's why we have tests, right? *smh*
#
#I can confirm that *SHOULD* work: e.g.,
#>>> isinstance(Pep484GenericTypevaredSingle[S, T].__origin__, type)
#True
#FIXME: Note we'll need to also implement similar behaviour for
#subscripted generics with respect to "__args__".

# A class that is either...
isinstance(hint, type) and (
# A PEP 484-compliant generic. Note this test trivially reduces to
Expand Down
19 changes: 19 additions & 0 deletions beartype_test/a00_unit/data/hint/pep/proposal/data_hintpep484.py
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,25 @@ 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(
hint=Pep484GenericTypevaredSingle[S, T],
pep_sign=Generic,
is_typevared=True,
is_typing=False,
piths_satisfied_meta=(
# Subclass-specific generic.
PepHintPithSatisfiedMetadata(Pep484GenericTypevaredSingle()),
),
piths_unsatisfied_meta=(
# String constant.
PepHintPithUnsatisfiedMetadata(
'Token welfare’s malformed keening fare, keenly despaired'
),
),
),

# Generic subclassing multiple unparametrized "typing" types *AND* a
# non-"typing" abstract base class (ABC).
PepHintMetadata(
Expand Down

0 comments on commit 6381caa

Please sign in to comment.