Skip to content

Commit

Permalink
Python 3.7 last-rites x 1.
Browse files Browse the repository at this point in the history
This commit is the first 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. (*Broad roads, bro!*)
  • Loading branch information
leycec committed Sep 7, 2023
1 parent 1ed7c90 commit ddf6233
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 92 deletions.
58 changes: 10 additions & 48 deletions beartype/_cave/_cavefast.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
from beartype._cave._caveabc import BoolType
from beartype._util.py.utilpyversion import (
IS_PYTHON_AT_LEAST_3_9,
IS_PYTHON_AT_LEAST_3_8,
)
from collections import deque as _deque
from collections.abc import (
Expand Down Expand Up @@ -86,6 +85,7 @@
from types import (
AsyncGeneratorType as _AsyncGeneratorType,
BuiltinFunctionType as _BuiltinFunctionType,
CellType as _CellType,
CoroutineType as _CoroutineType,
FrameType as _FrameType,
FunctionType as _FunctionType,
Expand Down Expand Up @@ -219,53 +219,15 @@ class by that name. To circumvents this obvious oversight, this global globally
'''


# If the active Python interpreter targets Python >= 3.8...
if IS_PYTHON_AT_LEAST_3_8:
# Defer version-specific imports.
from types import CellType as _CellType # type: ignore[attr-defined]

# Alias this type to this standard type.
#
# Note that this is explicitly required for "nuitka" support, which supports
# this standard type but *NOT* the non-standard approach used to deduce this
# type under Python 3.7 leveraged below.
ClosureVarCellType = _CellType
'''
Type of all **pure-Python closure cell variables.**
'''
# Else, the active Python interpreter only targets Python 3.7. In this case...
else:
def _closure_varcell_factory():
'''
Arbitrary function returning a closure-specific cell variable exposed by
an arbitrary closure isolated to this function.
'''

# Arbitrary outer local variable.
cell_variable = 42

def closure():
'''
Arbitrary closure exposing a closure-specific cell variable.
'''

nonlocal cell_variable

return closure.__closure__[0] # pyright: ignore[reportOptionalSubscript]


# Although Python >= 3.7 now exposes an explicit closure cell variable type
# via the standard "types.CellType" object, this is of no benefit to older
# versions of Python. Ergo, the type of an arbitrary method wrapper
# guaranteed to *ALWAYS* exist is obtained instead.
ClosureVarCellType = type(_closure_varcell_factory()) # type: ignore[misc]
'''
Type of all **pure-Python closure cell variables.**
'''


# Delete this factory function for safety.
del _closure_varcell_factory
# Alias this type to this standard type.
#
# Note that this is explicitly required for "nuitka" support, which supports
# this standard type but *NOT* the non-standard approach used to deduce this
# type under Python 3.7 leveraged below.
ClosureVarCellType = _CellType
'''
Type of all **pure-Python closure cell variables.**
'''

# ....................{ TYPES ~ call : exception }....................
ExceptionTracebackType = _TracebackType
Expand Down
65 changes: 33 additions & 32 deletions beartype/_util/py/utilpyversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,20 @@
# ....................{ CONSTANTS ~ at least }....................
IS_PYTHON_AT_LEAST_4_0 = version_info >= (4, 0)
'''
``True`` only if the active Python interpreter targets at least Python 4.0.0.
:data:`True` only if the active Python interpreter targets at least Python
4.0.0.
'''


#FIXME: After dropping Python 3.12 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.12.0')
IS_PYTHON_AT_LEAST_3_12 = IS_PYTHON_AT_LEAST_4_0 or version_info >= (3, 12)
'''
:data:`True` only if the active Python interpreter targets at least Python
3.12.0.
'''


Expand All @@ -24,17 +37,20 @@
#* Remove this global.
#* Remove all decorators resembling:
# @skip_if_python_version_less_than('3.11.0')
IS_PYTHON_AT_LEAST_3_11 = IS_PYTHON_AT_LEAST_4_0 or version_info >= (3, 11)
IS_PYTHON_AT_LEAST_3_11 = IS_PYTHON_AT_LEAST_3_12 or version_info >= (3, 11)
'''
``True`` only if the active Python interpreter targets at least Python 3.11.0.
:data:`True` only if the active Python interpreter targets at least Python
3.11.0.
'''


#FIXME: After dropping Python 3.9 support:
#* Refactor all code conditionally testing this global to be unconditional.
#* Remove this global.
IS_PYTHON_AT_MOST_3_10 = not IS_PYTHON_AT_LEAST_3_11
'''
``True`` only if the active Python interpreter targets at most Python 3.10.x.
:data:`True` only if the active Python interpreter targets at most Python
3.10.x.
'''


Expand All @@ -45,15 +61,17 @@
# @skip_if_python_version_less_than('3.10.0')
IS_PYTHON_AT_LEAST_3_10 = IS_PYTHON_AT_LEAST_3_11 or version_info >= (3, 10)
'''
``True`` only if the active Python interpreter targets at least Python 3.10.0.
:data:`True` only if the active Python interpreter targets at least Python
3.10.0.
'''


#FIXME: After dropping Python 3.9 support:
#* Refactor all code conditionally testing this global to be unconditional.
#* 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.
:data:`True` only if the active Python interpreter targets at most Python 3.9.x.
'''


Expand All @@ -64,15 +82,17 @@
# @skip_if_python_version_less_than('3.9.0')
IS_PYTHON_AT_LEAST_3_9 = IS_PYTHON_AT_LEAST_3_10 or version_info >= (3, 9)
'''
``True`` only if the active Python interpreter targets at least Python 3.9.0.
:data:`True` only if the active Python interpreter targets at least Python
3.9.0.
'''


#FIXME: After dropping Python 3.8 support:
#* Refactor all code conditionally testing this global to be unconditional.
#* Remove this global.
IS_PYTHON_AT_MOST_3_8 = not IS_PYTHON_AT_LEAST_3_9
'''
``True`` only if the active Python interpreter targets at most Python 3.8.x.
:data:`True` only if the active Python interpreter targets at most Python 3.8.x.
'''


Expand All @@ -83,36 +103,17 @@
# @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)
'''
``True`` only if the active Python interpreter targets at least Python 3.8.0.
:data:`True` only if the active Python interpreter targets at least Python
3.8.0.
'''


#FIXME: After dropping Python 3.8 support, *REMOVE* all code conditionally
#testing this global.
IS_PYTHON_3_8 = version_info[:2] == (3, 8)
'''
``True`` only if the active Python interpreter targets exactly Python 3.8.
'''


#FIXME: After dropping Python 3.7 support:
#FIXME: After dropping Python 3.8 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.7.2')
IS_PYTHON_AT_LEAST_3_7_2 = IS_PYTHON_AT_LEAST_3_8 or version_info >= (3, 7, 2)
'''
``True`` only if the active Python interpreter targets at least Python 3.7.2,
which introduced several new public attributes to the :mod:`typing` module
(e.g., :attr:`typing.OrderedDict`).
'''


#FIXME: After dropping Python 3.7 support, *REMOVE* all code conditionally
#testing this global.
IS_PYTHON_3_7 = version_info[:2] == (3, 7)
IS_PYTHON_3_8 = version_info[:2] == (3, 8)
'''
``True`` only if the active Python interpreter targets exactly Python 3.7.
:data:`True` only if the active Python interpreter targets exactly Python 3.8.x.
'''

# ....................{ GETTERS }....................
Expand Down
10 changes: 1 addition & 9 deletions beartype/typing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@
IS_PYTHON_AT_LEAST_3_10 as _IS_PYTHON_AT_LEAST_3_10,
IS_PYTHON_AT_LEAST_3_9 as _IS_PYTHON_AT_LEAST_3_9,
IS_PYTHON_AT_LEAST_3_8 as _IS_PYTHON_AT_LEAST_3_8,
IS_PYTHON_AT_LEAST_3_7_2 as _IS_PYTHON_AT_LEAST_3_7_2,
)

# ....................{ IMPORTS ~ all }....................
Expand Down Expand Up @@ -335,6 +334,7 @@
MutableMapping as MutableMapping,
MutableSequence as MutableSequence,
MutableSet as MutableSet,
OrderedDict as OrderedDict,
Pattern as Pattern,
Reversible as Reversible,
Set as Set,
Expand All @@ -343,14 +343,6 @@
Sequence as Sequence,
ValuesView as ValuesView,
)

# If the active Python interpreter targets Python >= 3.7.2, import *ALL*
# public attributes of the "typing" module introduced by Python 3.7.2
# deprecated by PEP 585 as their original values.
if _IS_PYTHON_AT_LEAST_3_7_2:
from typing import ( # type: ignore[attr-defined]
OrderedDict as OrderedDict,
)
# If the active Python interpreter targets Python >= 3.9 and thus supports PEP
# 585, alias *ALL* public attributes of the "typing" module deprecated by PEP
# 585 to their equivalent values elsewhere in the standard library.
Expand Down
3 changes: 0 additions & 3 deletions beartype/typing/_typingpep544.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@
# but couldn't find it after a brief search.
if IS_PYTHON_AT_LEAST_3_8:
# ..................{ IMPORTS }..................
#FIXME: The ignore[attr-defined] is for Python 3.7 because Mypy doesn't
#understand IS_PYTHON_AT_LEAST_3_8. That ignore should be removable
#when retiring PYTHON 3.7.
# Defer Python version-specific imports, including non-caching
# protocols to be overridden by caching equivalents below (and other
# requirements from various sources, depending on runtime environment).
Expand Down

0 comments on commit ddf6233

Please sign in to comment.