Skip to content

Commit

Permalink
Dropped Python 3.7 support
Browse files Browse the repository at this point in the history
  • Loading branch information
agronholm committed Aug 18, 2023
1 parent eb65830 commit c637720
Show file tree
Hide file tree
Showing 9 changed files with 34 additions and 79 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", pypy-3.10]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", pypy-3.10]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
Expand Down
4 changes: 4 additions & 0 deletions docs/versionhistory.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ Version history

This library adheres to `Semantic Versioning 2.0 <https://semver.org/#semantic-versioning-200>`_.

**UNRELEASED**

- Dropped Python 3.7 support

**4.1.2** (2023-08-18)

- Fixed ``Any`` being removed from a subscript that still contains other elements
Expand Down
7 changes: 2 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,13 @@ classifiers = [
"License :: OSI Approved :: MIT License",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
requires-python = ">= 3.7.4"
requires-python = ">= 3.8"
dependencies = [
"importlib_metadata >= 3.6; python_version < '3.10'",
"typing_extensions >= 4.7.0; python_version < '3.12'",
Expand Down Expand Up @@ -75,7 +74,6 @@ exclude_lines = [
]

[tool.ruff]
line-length = 88
select = [
"E", "F", "W", # default flake-8
"I", # isort
Expand All @@ -87,7 +85,6 @@ ignore = [
"PGH001",
"B008",
]
target-version = "py37"
src = ["src"]

[tool.mypy]
Expand All @@ -98,7 +95,7 @@ pretty = true
[tool.tox]
legacy_tox_ini = """
[tox]
envlist = pypy3, py37, py38, py39, py310, py311, py312
envlist = pypy3, py38, py39, py310, py311, py312
skip_missing_interpreters = true
minversion = 4.0
Expand Down
26 changes: 6 additions & 20 deletions src/typeguard/_checkers.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,11 +339,7 @@ def check_tuple(
memo: TypeCheckMemo,
) -> None:
# Specialized check for NamedTuples
field_types = getattr(origin_type, "__annotations__", None)
if field_types is None and sys.version_info < (3, 8):
field_types = getattr(origin_type, "_field_types", None)

if field_types:
if field_types := getattr(origin_type, "__annotations__", None):
if not isinstance(value, origin_type):
raise TypeCheckError(
f"is not a named tuple of type {qualified_name(origin_type)}"
Expand All @@ -361,7 +357,6 @@ def check_tuple(
raise TypeCheckError("is not a tuple")

if args:
# Python 3.6+
use_ellipsis = args[-1] is Ellipsis
tuple_params = args[: -1 if use_ellipsis else None]
else:
Expand Down Expand Up @@ -444,7 +439,6 @@ def check_class(
if not isclass(value):
raise TypeCheckError("is not a class")

# Needed on Python 3.7+
if not args:
return

Expand Down Expand Up @@ -531,21 +525,15 @@ def check_typevar(
)


if sys.version_info >= (3, 8):
if typing_extensions is None:

def _is_literal_type(typ: object) -> bool:
return typ is typing.Literal
if typing_extensions is None:

else:

def _is_literal_type(typ: object) -> bool:
return typ is typing.Literal or typ is typing_extensions.Literal
def _is_literal_type(typ: object) -> bool:
return typ is typing.Literal

else:

def _is_literal_type(typ: object) -> bool:
return typ is typing_extensions.Literal
return typ is typing.Literal or typ is typing_extensions.Literal


def check_literal(
Expand All @@ -558,7 +546,6 @@ def get_literal_args(literal_args: tuple[Any, ...]) -> tuple[Any, ...]:
retval: list[Any] = []
for arg in literal_args:
if _is_literal_type(get_origin(arg)):
# The first check works on py3.6 and lower, the second one on py3.7+
retval.extend(get_literal_args(arg.__args__))
elif arg is None or isinstance(arg, (int, str, bytes, bool, Enum)):
retval.append(arg)
Expand Down Expand Up @@ -801,6 +788,7 @@ def check_type_internal(
IO: check_io,
list: check_list,
List: check_list,
typing.Literal: check_literal,
Mapping: check_mapping,
MutableMapping: check_mapping,
None: check_none,
Expand All @@ -818,8 +806,6 @@ def check_type_internal(
Type: check_class,
Union: check_union,
}
if sys.version_info >= (3, 8):
origin_type_checkers[typing.Literal] = check_literal
if sys.version_info >= (3, 10):
origin_type_checkers[types.UnionType] = check_uniontype
origin_type_checkers[typing.TypeGuard] = check_typeguard
Expand Down
35 changes: 10 additions & 25 deletions src/typeguard/_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
Module,
Mult,
Name,
NamedExpr,
NodeTransformer,
NodeVisitor,
Pass,
Expand All @@ -45,7 +46,6 @@
RShift,
Starred,
Store,
Str,
Sub,
Subscript,
Tuple,
Expand All @@ -65,9 +65,6 @@
from dataclasses import dataclass, field
from typing import Any, ClassVar, cast, overload

if sys.version_info >= (3, 8):
from ast import NamedExpr

generator_names = (
"typing.Generator",
"collections.abc.Generator",
Expand Down Expand Up @@ -159,13 +156,12 @@ def __post_init__(self) -> None:
if isinstance(child, ImportFrom) and child.module == "__future__":
# (module only) __future__ imports must come first
continue
elif isinstance(child, Expr):
if isinstance(child.value, Constant) and isinstance(
child.value.value, str
):
continue # docstring
elif sys.version_info < (3, 8) and isinstance(child.value, Str):
continue # docstring
elif (
isinstance(child, Expr)
and isinstance(child.value, Constant)
and isinstance(child.value.value, str)
):
continue # docstring

self.code_inject_index = index
break
Expand Down Expand Up @@ -411,7 +407,7 @@ def visit_Subscript(self, node: Subscript) -> Any:
# don't try to evaluate it as code
if node.slice:
if isinstance(node.slice, Index):
# Python 3.7 and 3.8
# Python 3.8
slice_value = node.slice.value # type: ignore[attr-defined]
else:
slice_value = node.slice
Expand Down Expand Up @@ -491,15 +487,6 @@ def visit_Constant(self, node: Constant) -> Any:

return node

def visit_Str(self, node: Str) -> Any:
# Only used on Python 3.7
expression = ast.parse(node.s, mode="eval")
new_node = self.visit(expression)
if new_node:
return copy_location(new_node.body, node)
else:
return None


class TypeguardTransformer(NodeTransformer):
def __init__(
Expand Down Expand Up @@ -705,14 +692,12 @@ def visit_FunctionDef(
if self.target_lineno == first_lineno:
assert self.target_node is None
self.target_node = node
if node.decorator_list and sys.version_info >= (3, 8):
if node.decorator_list:
self.target_lineno = node.decorator_list[0].lineno
else:
self.target_lineno = node.lineno

all_args = node.args.args + node.args.kwonlyargs
if sys.version_info >= (3, 8):
all_args.extend(node.args.posonlyargs)
all_args = node.args.args + node.args.kwonlyargs + node.args.posonlyargs

# Ensure that any type shadowed by the positional or keyword-only
# argument names are ignored in this function
Expand Down
8 changes: 1 addition & 7 deletions src/typeguard/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from importlib import import_module
from inspect import currentframe
from types import CodeType, FrameType, FunctionType
from typing import TYPE_CHECKING, Any, Callable, ForwardRef, Union, cast
from typing import TYPE_CHECKING, Any, Callable, ForwardRef, Union, cast, final
from weakref import WeakValueDictionary

if TYPE_CHECKING:
Expand Down Expand Up @@ -47,12 +47,6 @@ def evaluate_forwardref(forwardref: ForwardRef, memo: TypeCheckMemo) -> Any:
raise


if sys.version_info >= (3, 8):
from typing import final
else:
from typing_extensions import final


_functions_map: WeakValueDictionary[CodeType, FunctionType] = WeakValueDictionary()


Expand Down
8 changes: 2 additions & 6 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import sys
from typing import (
AbstractSet,
Collection,
Expand All @@ -7,15 +6,12 @@
List,
NamedTuple,
NewType,
Protocol,
TypeVar,
Union,
runtime_checkable,
)

if sys.version_info >= (3, 8):
from typing import Protocol, runtime_checkable
else:
from typing_extensions import Protocol, runtime_checkable

T_Foo = TypeVar("T_Foo")

TBound = TypeVar("TBound", bound="Parent")
Expand Down
16 changes: 6 additions & 10 deletions tests/dummymodule.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
Dict,
Generator,
List,
Literal,
Sequence,
Tuple,
Type,
Expand All @@ -19,23 +20,18 @@
overload,
)

if sys.version_info >= (3, 10):
from typing import ParamSpec
else:
from typing_extensions import ParamSpec

if sys.version_info >= (3, 8):
from typing import Literal
else:
from typing_extensions import Literal

from typeguard import (
CollectionCheckStrategy,
ForwardRefPolicy,
typechecked,
typeguard_ignore,
)

if sys.version_info >= (3, 10):
from typing import ParamSpec
else:
from typing_extensions import ParamSpec

if TYPE_CHECKING:
from nonexistent import Imaginary

Expand Down
7 changes: 2 additions & 5 deletions tests/test_checkers.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
FrozenSet,
Iterator,
List,
Literal,
Mapping,
MutableMapping,
Optional,
Expand All @@ -26,6 +27,7 @@
TextIO,
Tuple,
Type,
TypedDict,
TypeVar,
Union,
)
Expand Down Expand Up @@ -77,11 +79,6 @@
else:
from typing_extensions import Annotated

if sys.version_info >= (3, 8):
from typing import Literal, TypedDict
else:
from typing_extensions import Literal, TypedDict

P = ParamSpec("P")


Expand Down

0 comments on commit c637720

Please sign in to comment.