Skip to content

Commit

Permalink
Python 3.7 last-rites x 3.
Browse files Browse the repository at this point in the history
This commit is the last in a commit chain **last-riting** (i.e.,
permanently removing) support for Python 3.7, which recently hit its
official End-of-Life (EoL) date and thus constitutes a security risk.
Since Python 3.7 substantially differed from Python 3.8 with respect to
type hints in general and the standard `typing` module in specific, this
commit chain is likely to take longer than anyone wants or expects. This
is why @leycec does @beartype: so you don't have to.
(*Dramatic dragons on airy auto-pilot ate Gary!*)
  • Loading branch information
leycec committed Sep 8, 2023
1 parent d4e0aaa commit cca21f8
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 114 deletions.
12 changes: 0 additions & 12 deletions beartype/_util/py/utilpyversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,18 +96,6 @@
'''


#FIXME: After dropping Python 3.7 support:
#* Refactor all code conditionally testing this global to be unconditional.
#* Remove this global.
#* Remove all decorators resembling:
# @skip_if_python_version_less_than('3.8.0')
IS_PYTHON_AT_LEAST_3_8 = IS_PYTHON_AT_LEAST_3_9 or version_info >= (3, 8)
'''
:data:`True` only if the active Python interpreter targets at least Python
3.8.0.
'''


#FIXME: After dropping Python 3.8 support:
#* Refactor all code conditionally testing this global to be unconditional.
#* Remove this global.
Expand Down
140 changes: 74 additions & 66 deletions beartype_test/a00_unit/a40_api/door/_doorfixture.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,20 @@ def door_cases_subhint() -> 'Iterable[Tuple[object, object, bool]]':
from beartype._util.hint.pep.utilpepget import get_hint_pep_typevars
from beartype._util.py.utilpyversion import (
IS_PYTHON_AT_LEAST_3_9,
IS_PYTHON_AT_LEAST_3_8,
)
from collections.abc import (
Collection as CollectionABC,
Sequence as SequenceABC,
)

# Intentionally import from "beartype.typing" rather than "typing" to
# guarantee PEP 544-compliant caching protocol type hints.
from beartype.typing import (
Literal,
Protocol,
TypedDict,
)

# Intentionally import from "typing" rather than "beartype.typing" to
# guarantee PEP 484-compliant type hints.
from typing import (
Expand Down Expand Up @@ -211,7 +218,29 @@ def __len__(self) -> int:
pass


class MuhDict(TypedDict):
'''
Arbitrary typed dictionary.
'''

thing_one: str
thing_two: int


class MuhThingP(Protocol):
'''
Arbitrary caching @beartype protocol.
'''

def muh_method(self):
...


class MuhTuple(NamedTuple):
'''
Arbitrary named tuple.
'''

thing_one: str
thing_two: int

Expand All @@ -232,6 +261,7 @@ class MuhGenericTwo(Generic[S, T]):
pass


#FIXME: Actually reference this class below, please. *sigh*
class MuhGenericTwoIntInt(MuhGenericTwo[int, int]):
'''
Arbitrary concrete generic subclass inheriting the
Expand All @@ -243,7 +273,7 @@ class MuhGenericTwoIntInt(MuhGenericTwo[int, int]):

# ..................{ LISTS }..................
HINT_SUBHINT_CASES = [
# ..................{ HINTS ~ argless : any }..................
# ..................{ PEP 484 ~ argless : any }..................
# PEP 484-compliant catch-all type hint.
(MuhThing, Any, True),
(Tuple[object, ...], Any, True),
Expand All @@ -254,7 +284,7 @@ class MuhGenericTwoIntInt(MuhGenericTwo[int, int]):
(Any, Any, True),
(Any, object, False),

# ..................{ HINTS ~ argless : bare }..................
# ..................{ PEP 484 ~ argless : bare }..................
# PEP 484-compliant unsubscripted type hints, which are necessarily
# subhints of themselves.
(list, list, True),
Expand All @@ -268,7 +298,7 @@ class MuhGenericTwoIntInt(MuhGenericTwo[int, int]):
(list, SequenceABC, True),
(list, CollectionABC, True),

# ..................{ HINTS ~ argless : type }..................
# ..................{ PEP 484 ~ argless : type }..................
# PEP 484-compliant argumentless abstract base classes (ABCs).
(bytes, ByteString, True),
(str, Hashable, True),
Expand All @@ -282,7 +312,7 @@ class MuhGenericTwoIntInt(MuhGenericTwo[int, int]):
(int, NewStr, False),
(str, NewStr, False), # NewType act like subtypes

# ..................{ HINTS ~ argless : typevar }..................
# ..................{ PEP 484 ~ argless : typevar }..................
# PEP 484-compliant type variables.
(list, SeqBoundTypeVar, True),
(SeqBoundTypeVar, list, False),
Expand All @@ -297,7 +327,7 @@ class MuhGenericTwoIntInt(MuhGenericTwo[int, int]):
(Any, T, True), # Any is compatible with an unconstrained TypeVar
(Any, SeqBoundTypeVar, False), # but not vice versa

# ..................{ HINTS ~ argless : number }..................
# ..................{ PEP 484 ~ argless : number }..................
# Blame Guido.
(bool, int, True),

Expand All @@ -311,7 +341,7 @@ class MuhGenericTwoIntInt(MuhGenericTwo[int, int]):
(int, float, False),
(float, complex, False),

# ..................{ HINTS ~ arg : callable }..................
# ..................{ PEP 484 ~ arg : callable }..................
# PEP 484-compliant callable type hints.
(Callable, Callable[..., Any], True),
(Callable[[], int], Callable[..., Any], True),
Expand All @@ -330,7 +360,7 @@ class MuhGenericTwoIntInt(MuhGenericTwo[int, int]):
(Callable[[int, str], int], Callable[[int, str], Any], True),
# (types.FunctionType, Callable, True), # FIXME

# ..................{ HINTS ~ arg : generic }..................
# ..................{ PEP 484 ~ arg : generic }..................
# PEP 484-compliant generics parametrized by one type variable.
(MuhGeneric, MuhGeneric, True),
(MuhGeneric, MuhGeneric[int], False),
Expand All @@ -347,7 +377,7 @@ class MuhGenericTwoIntInt(MuhGenericTwo[int, int]):
# PEP 484-compliant generics parametrized by two type variables.
# (MuhGenericTwoIntInt, MuhGenericTwo[int, int], True),

# ..................{ HINTS ~ arg : mapping }..................
# ..................{ PEP 484 ~ arg : mapping }..................
# PEP 484-compliant mapping type hints.
(dict, Dict, True),
(Dict[str, int], Dict, True),
Expand All @@ -358,7 +388,7 @@ class MuhGenericTwoIntInt(MuhGenericTwo[int, int]):
True,
),

# ..................{ HINTS ~ arg : sequence }..................
# ..................{ PEP 484 ~ arg : sequence }..................
# PEP 484-compliant sequence type hints.
(List[int], List[int], True),
(List[int], Sequence[int], True),
Expand Down Expand Up @@ -391,15 +421,15 @@ class MuhGenericTwoIntInt(MuhGenericTwo[int, int]):
# PEP 484-compliant nested sequence type hints.
(List[int], Union[str, List[Union[int, str]]], True),

# ..................{ HINTS ~ arg : subclass }..................
# ..................{ PEP 484 ~ arg : subclass }..................
# PEP 484-compliant subclass type hints.
(Type[int], Type[int], True),
(Type[int], Type[str], False),
(Type[MuhSubThing], Type[MuhThing], True),
(Type[MuhThing], Type[MuhSubThing], False),
(MuhThing, Type[MuhThing], False),

# ..................{ HINTS ~ arg : union }..................
# ..................{ PEP 484 ~ arg : union }..................
# PEP 484-compliant unions.
(int, Union[int, str], True),
(Union[int, str], Union[list, int, str], True),
Expand All @@ -410,6 +440,23 @@ class MuhGenericTwoIntInt(MuhGenericTwo[int, int]):
(int, Optional[int], True),
(Optional[int], int, False),
(list, Optional[Sequence], True),

# ..................{ PEP 544 }..................
# PEP 544-compliant type hints.
(MuhThing, MuhThingP, True),
(MuhNutherThing, MuhThingP, False),
(MuhThingP, MuhThing, False),

# PEP 586-compliant type hints.
(Literal[7], int, True),
(Literal["a"], str, True),
(Literal[7, 8, "3"], Union[int, str], True),
(Literal[7, 8, "3"], Union[list, int], False),
(int, Literal[7], False),
(Literal[7, 8], Literal[7, 8, 9], True),

# PEP 589-compliant type hints.
(MuhDict, dict, True),
]

# ..................{ HINTS ~ abcs }..................
Expand Down Expand Up @@ -438,64 +485,25 @@ class MuhGenericTwoIntInt(MuhGenericTwo[int, int]):
HINT_SUBHINT_CASES.append((sub, sup, True))

# ..................{ HINTS ~ version }..................
# If the active Python interpreter targets Python >= 3.8 and thus supports
# PEP 544 and 586...
if IS_PYTHON_AT_LEAST_3_8:
# Intentionally import from "beartype.typing" rather than "typing" to
# guarantee PEP 544-compliant caching protocol type hints.
from beartype.typing import (
Literal,
Protocol,
TypedDict,
)

# Arbitrary caching @beartype protocol.
class MuhThingP(Protocol):
def muh_method(self):
...

class MuhDict(TypedDict):
thing_one: str
thing_two: int
# If the active Python interpreter targets Python >= 3.9 and thus
# supports PEP 585 and 593...
if IS_PYTHON_AT_LEAST_3_9:
from beartype.typing import Annotated

# Append cases exercising version-specific relations.
HINT_SUBHINT_CASES.extend((
# PEP 544-compliant type hints.
(MuhThing, MuhThingP, True),
(MuhNutherThing, MuhThingP, False),
(MuhThingP, MuhThing, False),

# PEP 586-compliant type hints.
(Literal[7], int, True),
(Literal["a"], str, True),
(Literal[7, 8, "3"], Union[int, str], True),
(Literal[7, 8, "3"], Union[list, int], False),
(int, Literal[7], False),
(Literal[7, 8], Literal[7, 8, 9], True),

# PEP 589-compliant type hints.
(MuhDict, dict, True),
))
# PEP 585-compliant type hints.
(tuple, Tuple, True),
(tuple[()], Tuple[()], True),

# If the active Python interpreter targets Python >= 3.9 and thus
# supports PEP 585 and 593...
if IS_PYTHON_AT_LEAST_3_9:
from beartype.typing import Annotated

# Append cases exercising version-specific relations.
HINT_SUBHINT_CASES.extend((
# PEP 585-compliant type hints.
(tuple, Tuple, True),
(tuple[()], Tuple[()], True),

# PEP 593-compliant type hints.
(Annotated[int, "a note"], int, True), # annotated is subtype of unannotated
(int, Annotated[int, "a note"], False), # but not vice versa
(Annotated[list, True], Annotated[Sequence, True], True),
(Annotated[list, False], Annotated[Sequence, True], False),
(Annotated[list, 0, 0], Annotated[list, 0], False), # must have same num args
(Annotated[List[int], "metadata"], List[int], True),
))
# PEP 593-compliant type hints.
(Annotated[int, "a note"], int, True), # annotated is subtype of unannotated
(int, Annotated[int, "a note"], False), # but not vice versa
(Annotated[list, True], Annotated[Sequence, True], True),
(Annotated[list, False], Annotated[Sequence, True], False),
(Annotated[list, 0, 0], Annotated[list, 0], False), # must have same num args
(Annotated[List[int], "metadata"], List[int], True),
))

# Return this mutable list coerced into an immutable tuple for safety.
return tuple(HINT_SUBHINT_CASES)
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# See "LICENSE" for further details.

'''
**Beartype** :pep:`3119` **unit tests.**
Beartype :pep:`3119` **unit tests.
This submodule unit tests :pep:`3119` support implemented in the
:func:`beartype.beartype` decorator.
Expand All @@ -31,10 +31,16 @@ def test_decor_pep3119() -> None:
# Defer test-specific imports.
from beartype import beartype
from beartype.roar import BeartypeDecorHintPep3119Exception
from beartype.typing import Tuple, Type
from beartype._util.py.utilpyversion import IS_PYTHON_AT_LEAST_3_8
from beartype.typing import (
Tuple,
Type,
)
from pytest import raises

# Intentionally import from the "typing" module to allow the definition of
# an uncheckable protocol below.
from typing import Protocol

# ....................{ CLASSES }....................
class Pep3119Metaclass(type):
'''
Expand All @@ -57,6 +63,18 @@ class Pep3119Class(metaclass=Pep3119Metaclass):
guaranteed to be only partially initialized.
'''


class NonisinstanceableProtocol(Protocol):
'''
Arbitrary **non-isinstanceable protocol** (i.e., protocol *not*
decorated by the :func:`typing.runtime_checkable` decorator and
hence *not* checkable at runtime, as doing so raises a
:exc:`TypeError` from the ``__instancecheck__`` dunder method of the
metaclass of this protocol).
'''

pass

# ....................{ FUNCTIONS }....................
# Implicitly assert that decorating a function annotated by a partially
# initialized PEP 3119-compliant metaclass does *NOT* raise an exception.
Expand Down Expand Up @@ -97,38 +115,19 @@ def each_like_a_corpse_within_its_grave() -> bool:
thine_azure_sister_of_the_Spring_shall_blow, Pep3119Class)

# ....................{ FAIL }....................
# If the active Python interpreter targets Python >= 3.8 and thus supports
# the PEP 544-compliant "typing.Protocol" superclass...
if IS_PYTHON_AT_LEAST_3_8:
# Intentionally import from the "typing" module to allow the definition
# of an uncheckable protocol below.
from typing import Protocol

class NonisinstanceableProtocol(Protocol):
'''
Arbitrary **non-isinstanceable protocol** (i.e., protocol *not*
decorated by the :func:`typing.runtime_checkable` decorator and
hence *not* checkable at runtime, as doing so raises a
:exc:`TypeError` from the ``__instancecheck__`` dunder method of the
metaclass of this protocol).
'''

# Assert that decorating a function annotated as accepting a parameter whose
# value is an instance of a non-isinstanceable class raises the expected
# exception.
with raises(BeartypeDecorHintPep3119Exception):
@beartype
def her_clarion_over_the_dreaming_earth(
and_lie: NonisinstanceableProtocol) -> None:
pass

# Assert that decorating a function annotated as accepting a parameter
# whose value is an instance of a non-isinstanceable class raises the
# expected exception.
with raises(BeartypeDecorHintPep3119Exception):
@beartype
def her_clarion_over_the_dreaming_earth(
and_lie: NonisinstanceableProtocol) -> None:
pass

# Assert that decorating a function annotated as accepting a parameter
# whose value is a non-issubclassable class raises the expected
# exception.
with raises(BeartypeDecorHintPep3119Exception):
@beartype
def with_living_hues_and_odours_plain(
and_hill: Type[NonisinstanceableProtocol]) -> None:
pass
# Assert that decorating a function annotated as accepting a parameter whose
# value is a non-issubclassable class raises the expected exception.
with raises(BeartypeDecorHintPep3119Exception):
@beartype
def with_living_hues_and_odours_plain(
and_hill: Type[NonisinstanceableProtocol]) -> None:
pass

0 comments on commit cca21f8

Please sign in to comment.