Skip to content

typing-extensions: Drop Python 3.6 #1104

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
# Python version, because typing sometimes changed between bugfix releases.
# For available versions, see:
# https://github.com/raw/actions/python-versions/main/versions-manifest.json
python-version: ["3.6", "3.6.7", "3.7", "3.7.1", "3.8", "3.8.0", "3.9", "3.9.0", "3.10", "3.10.0", "3.11-dev"]
python-version: ["3.7", "3.7.1", "3.8", "3.8.0", "3.9", "3.9.0", "3.10", "3.10.0", "3.11-dev"]

runs-on: ubuntu-latest

Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ standard library, so that users can experiment with them before they are added t
standard library. Such features should ideally already be specified in a PEP or draft
PEP.

`typing_extensions` supports Python versions 3.6 and up.
`typing_extensions` supports Python versions 3.7 and up.

# Versioning scheme

Expand Down
4 changes: 4 additions & 0 deletions typing_extensions/CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Unreleased

- Drop support for Python 3.6. Original patch by Adam Turner (@AA-Turner).

# Release 4.1.1 (February 13, 2022)

- Fix importing `typing_extensions` on Python 3.7.0 and 3.7.1. Original
Expand Down
9 changes: 5 additions & 4 deletions typing_extensions/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ Therefore, it's safe to depend
on ``typing_extensions`` like this: ``typing_extensions >=x.y, <(x+1)``,
where ``x.y`` is the first version that includes all features you need.

``typing_extensions`` supports Python versions 3.7 and higher. In the future,
support for older Python versions will be dropped some time after that version
reaches end of life.

Included items
==============

Expand Down Expand Up @@ -101,7 +105,7 @@ This module currently contains the following:
- ``Text``
- ``Type``
- ``TYPE_CHECKING``
- ``get_type_hints`` (``typing_extensions`` provides this function only in Python 3.7+)
- ``get_type_hints``

Other Notes and Limitations
===========================
Expand Down Expand Up @@ -131,9 +135,6 @@ versions of the typing module:
- ``ParamSpec`` and ``Concatenate`` will not work with ``get_args`` and
``get_origin``. Certain PEP 612 special cases in user-defined
``Generic``\ s are also not available.
- ``Unpack`` from PEP 646 does not work properly with user-defined
``Generic``\ s in Python 3.6: ``class X(Generic[Unpack[Ts]]):`` does
not work.

These types are only guaranteed to work for static type checking.

Expand Down
2 changes: 1 addition & 1 deletion typing_extensions/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ name = "typing_extensions"
version = "4.1.1"
description = "Backported and Experimental Type Hints for Python 3.6+"
readme = "README.rst"
requires-python = ">=3.6"
requires-python = ">=3.7"
urls.Home = "https://github.com/python/typing/blob/master/typing_extensions/README.rst"
license.file = "LICENSE"
keywords = [
Expand Down
90 changes: 17 additions & 73 deletions typing_extensions/src/test_typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,32 +23,14 @@
from typing_extensions import Awaitable, AsyncIterator, AsyncContextManager, Required, NotRequired
from typing_extensions import Protocol, runtime, runtime_checkable, Annotated, overload, final, is_typeddict
from typing_extensions import TypeVarTuple, Unpack, dataclass_transform, reveal_type, Never, assert_never, LiteralString
try:
from typing_extensions import get_type_hints
except ImportError:
from typing import get_type_hints

PEP_560 = sys.version_info[:3] >= (3, 7, 0)

OLD_GENERICS = False
try:
from typing import _type_vars, _next_in_mro, _type_check # noqa
except ImportError:
OLD_GENERICS = True
from typing_extensions import get_type_hints, get_origin, get_args

# Flags used to mark tests that only apply after a specific
# version of the typing module.
TYPING_3_6_1 = sys.version_info[:3] >= (3, 6, 1)
TYPING_3_8_0 = sys.version_info[:3] >= (3, 8, 0)
TYPING_3_10_0 = sys.version_info[:3] >= (3, 10, 0)
TYPING_3_11_0 = sys.version_info[:3] >= (3, 11, 0)

# For typing versions where instantiating collection
# types are allowed.
#
# See https://github.com/python/typing/issues/367
CAN_INSTANTIATE_COLLECTIONS = TYPING_3_6_1


class BaseTestCase(TestCase):
def assertIsSubclass(self, cls, class_or_tuple, msg=None):
Expand Down Expand Up @@ -78,9 +60,7 @@ def test_equality(self):
self.assertIs(self.bottom_type, self.bottom_type)
self.assertNotEqual(self.bottom_type, None)

@skipUnless(PEP_560, "Python 3.7+ required")
def test_get_origin(self):
from typing_extensions import get_origin
self.assertIs(get_origin(self.bottom_type), None)

def test_instance_type_error(self):
Expand Down Expand Up @@ -621,11 +601,8 @@ def test_final_forward_ref(self):
self.assertNotEqual(gth(Loop, globals())['attr'], Final)


@skipUnless(PEP_560, "Python 3.7+ required")
class GetUtilitiesTestCase(TestCase):
def test_get_origin(self):
from typing_extensions import get_origin

T = TypeVar('T')
P = ParamSpec('P')
Ts = TypeVarTuple('Ts')
Expand Down Expand Up @@ -655,8 +632,6 @@ class C(Generic[T]): pass
self.assertIs(get_origin(Unpack), None)

def test_get_args(self):
from typing_extensions import get_args

T = TypeVar('T')
Ts = TypeVarTuple('Ts')
class C(Generic[T]): pass
Expand Down Expand Up @@ -767,7 +742,6 @@ class MyDeque(typing_extensions.Deque[int]): ...
def test_counter(self):
self.assertIsSubclass(collections.Counter, typing_extensions.Counter)

@skipUnless(CAN_INSTANTIATE_COLLECTIONS, "Behavior added in typing 3.6.1")
def test_defaultdict_instantiation(self):
self.assertIs(
type(typing_extensions.DefaultDict()),
Expand All @@ -790,7 +764,6 @@ class MyDefDict(typing_extensions.DefaultDict[str, int]):
self.assertIsSubclass(MyDefDict, collections.defaultdict)
self.assertNotIsSubclass(collections.defaultdict, MyDefDict)

@skipUnless(CAN_INSTANTIATE_COLLECTIONS, "Behavior added in typing 3.6.1")
def test_ordereddict_instantiation(self):
self.assertIs(
type(typing_extensions.OrderedDict()),
Expand Down Expand Up @@ -844,10 +817,7 @@ def test_counter_instantiation(self):
self.assertIs(type(typing_extensions.Counter[int]()), collections.Counter)
class C(typing_extensions.Counter[T]): ...
self.assertIs(type(C[int]()), C)
if not PEP_560:
self.assertEqual(C.__bases__, (typing_extensions.Counter,))
else:
self.assertEqual(C.__bases__, (collections.Counter, typing.Generic))
self.assertEqual(C.__bases__, (collections.Counter, typing.Generic))

def test_counter_subclass_instantiation(self):

Expand Down Expand Up @@ -922,9 +892,8 @@ def manager():
cm = manager()
self.assertNotIsInstance(cm, typing_extensions.AsyncContextManager)
self.assertEqual(typing_extensions.AsyncContextManager[int].__args__, (int,))
if TYPING_3_6_1:
with self.assertRaises(TypeError):
isinstance(42, typing_extensions.AsyncContextManager[int])
with self.assertRaises(TypeError):
isinstance(42, typing_extensions.AsyncContextManager[int])
with self.assertRaises(TypeError):
typing_extensions.AsyncContextManager[int, str]

Expand Down Expand Up @@ -1189,10 +1158,6 @@ def x(self): ...
self.assertIsSubclass(C, P)
self.assertIsSubclass(C, PG)
self.assertIsSubclass(BadP, PG)
if not PEP_560:
self.assertIsSubclass(PG[int], PG)
self.assertIsSubclass(BadPG[int], P)
self.assertIsSubclass(BadPG[T], PG)
with self.assertRaises(TypeError):
issubclass(C, PG[T])
with self.assertRaises(TypeError):
Expand Down Expand Up @@ -1383,7 +1348,6 @@ class C: pass
with self.assertRaises(TypeError):
issubclass(C(), P)

@skipUnless(not OLD_GENERICS, "New style generics required")
def test_defining_generic_protocols(self):
T = TypeVar('T')
S = TypeVar('S')
Expand All @@ -1392,16 +1356,19 @@ class PR(Protocol[T, S]):
def meth(self): pass
class P(PR[int, T], Protocol[T]):
y = 1
self.assertIsSubclass(PR[int, T], PR)
self.assertIsSubclass(P[str], PR)
with self.assertRaises(TypeError):
PR[int]
issubclass(PR[int, T], PR)
with self.assertRaises(TypeError):
P[int, str]
issubclass(P[str], PR)
with self.assertRaises(TypeError):
PR[int, 1]
PR[int]
with self.assertRaises(TypeError):
PR[int, ClassVar]
P[int, str]
if not TYPING_3_10_0:
with self.assertRaises(TypeError):
PR[int, 1]
with self.assertRaises(TypeError):
PR[int, ClassVar]
class C(PR[int, T]): pass
self.assertIsInstance(C[str](), C)

Expand All @@ -1413,11 +1380,8 @@ class PR(Protocol, Generic[T, S]):
def meth(self): pass
class P(PR[int, str], Protocol):
y = 1
if not PEP_560:
with self.assertRaises(TypeError):
self.assertIsSubclass(PR[int, str], PR)
else:
with self.assertRaises(TypeError):
self.assertIsSubclass(PR[int, str], PR)
self.assertIsSubclass(P, PR)
with self.assertRaises(TypeError):
PR[int]
Expand Down Expand Up @@ -1448,7 +1412,6 @@ def __init__(self):
self.test = 'OK'
self.assertEqual(C[int]().test, 'OK')

@skipUnless(not OLD_GENERICS, "New style generics required")
def test_protocols_bad_subscripts(self):
T = TypeVar('T')
S = TypeVar('S')
Expand All @@ -1465,9 +1428,6 @@ def test_generic_protocols_repr(self):
T = TypeVar('T')
S = TypeVar('S')
class P(Protocol[T, S]): pass
# After PEP 560 unsubscripted generics have a standard repr.
if not PEP_560:
self.assertTrue(repr(P).endswith('P'))
self.assertTrue(repr(P[T, S]).endswith('P[~T, ~S]'))
self.assertTrue(repr(P[int, str]).endswith('P[int, str]'))

Expand All @@ -1480,13 +1440,10 @@ class P(Protocol[T, S]): pass
self.assertEqual(P[T, T][Tuple[T, S]][int, str],
P[Tuple[int, str], Tuple[int, str]])

@skipUnless(not OLD_GENERICS, "New style generics required")
def test_generic_protocols_special_from_generic(self):
T = TypeVar('T')
class P(Protocol[T]): pass
self.assertEqual(P.__parameters__, (T,))
self.assertIs(P.__args__, None)
self.assertIs(P.__origin__, None)
self.assertEqual(P[int].__parameters__, ())
self.assertEqual(P[int].__args__, (int,))
self.assertIs(P[int].__origin__, P)
Expand Down Expand Up @@ -1517,9 +1474,6 @@ def meth(self):
self.assertEqual(typing_extensions._get_protocol_attrs(PR), {'x'})
self.assertEqual(frozenset(typing_extensions._get_protocol_attrs(PG)),
frozenset({'x', 'meth'}))
if not PEP_560:
self.assertEqual(frozenset(typing_extensions._get_protocol_attrs(PG[int])),
frozenset({'x', 'meth'}))

def test_no_runtime_deco_on_nominal(self):
with self.assertRaises(TypeError):
Expand Down Expand Up @@ -1747,7 +1701,6 @@ def test_optional_keys(self):
assert Point2Dor3D.__required_keys__ == frozenset(['x', 'y'])
assert Point2Dor3D.__optional_keys__ == frozenset(['z'])

@skipUnless(PEP_560, "runtime support for Required and NotRequired requires PEP 560")
def test_required_notrequired_keys(self):
assert NontotalMovie.__required_keys__ == frozenset({'title'})
assert NontotalMovie.__optional_keys__ == frozenset({'year'})
Expand Down Expand Up @@ -1821,16 +1774,14 @@ def test_flatten(self):
A = Annotated[Annotated[int, 4], 5]
self.assertEqual(A, Annotated[int, 4, 5])
self.assertEqual(A.__metadata__, (4, 5))
if PEP_560:
self.assertEqual(A.__origin__, int)
self.assertEqual(A.__origin__, int)

def test_specialize(self):
L = Annotated[List[T], "my decoration"]
LI = Annotated[List[int], "my decoration"]
self.assertEqual(L[int], Annotated[List[int], "my decoration"])
self.assertEqual(L[int].__metadata__, ("my decoration",))
if PEP_560:
self.assertEqual(L[int].__origin__, List[int])
self.assertEqual(L[int].__origin__, List[int])
with self.assertRaises(TypeError):
LI[int]
with self.assertRaises(TypeError):
Expand Down Expand Up @@ -1934,7 +1885,6 @@ def test_cannot_check_subclass(self):
with self.assertRaises(TypeError):
issubclass(int, Annotated[int, "positive"])

@skipUnless(PEP_560, "pickle support was added with PEP 560")
def test_pickle(self):
samples = [typing.Any, typing.Union[int, str],
typing.Optional[str], Tuple[int, ...],
Expand Down Expand Up @@ -2000,7 +1950,6 @@ def test_annotated_in_other_types(self):
self.assertEqual(X[int], List[Annotated[int, 5]])


@skipUnless(PEP_560, "Python 3.7 required")
class GetTypeHintsTests(BaseTestCase):
def test_get_type_hints(self):
def foobar(x: List['X']): ...
Expand Down Expand Up @@ -2355,9 +2304,7 @@ def baz(self) -> "LiteralString": ...
self.assertEqual(gth(Foo.bar), {'return': LiteralString})
self.assertEqual(gth(Foo.baz), {'return': LiteralString})

@skipUnless(PEP_560, "Python 3.7+ required")
def test_get_origin(self):
from typing_extensions import get_origin
self.assertIsNone(get_origin(LiteralString))

def test_repr(self):
Expand Down Expand Up @@ -2510,7 +2457,6 @@ def test_union(self):
Union
)

@skipUnless(PEP_560, "Unimplemented for 3.6")
def test_concatenation(self):
Xs = TypeVarTuple('Xs')
self.assertEqual(Tuple[int, Unpack[Xs]].__args__, (int, Unpack[Xs]))
Expand All @@ -2523,7 +2469,6 @@ class C(Generic[Unpack[Xs]]): pass
self.assertEqual(C[int, Unpack[Xs], str].__args__,
(int, Unpack[Xs], str))

@skipUnless(PEP_560, "Unimplemented for 3.6")
def test_class(self):
Ts = TypeVarTuple('Ts')

Expand Down Expand Up @@ -2766,8 +2711,7 @@ def test_typing_extensions_includes_standard(self):
self.assertIn("Concatenate", a)

self.assertIn('Annotated', a)
if PEP_560:
self.assertIn('get_type_hints', a)
self.assertIn('get_type_hints', a)

self.assertIn('Awaitable', a)
self.assertIn('AsyncIterator', a)
Expand Down
Loading