From 99f99513ce19968ea0e5da15ba2084c32e6d0a2d Mon Sep 17 00:00:00 2001 From: Michel Alexandre Salim Date: Tue, 12 May 2020 11:14:08 -0700 Subject: [PATCH] Add support for Python 3.9's NamedTuple changes In Python 3.9, NamedTuples no longer have a `_field_type` attribute; it has been deprecated since Python 3.8 and the `__annotations__` attribute has identical information: ``` >>> from typing import NamedTuple >>> class MyNT(NamedTuple): ... x: int ... y: int = 1 ... ... >>> issubclass(P, tuple) True >>> issubclass(P, NamedTuple) False >>> hasattr(MyNT, "_fields") True >>> hasattr(MyNT, "_field_types") True >>> hasattr(MyNT, "__annotations__") True >>> MyNT._field_types {'x': , 'y': } >>> MyNT.__annotations__ {'x': , 'y': } ``` see https://bugs.python.org/issue40182 Testing for the existence of *either* `_field_types` or `__annotations__` fixes the four failing tests on Python 3.9: ``` /builddir/build/BUILD/hypothesis-hypothesis-python-5.12.0/hypothesis-python sh-5.0# PYTHONPATH=/builddir/build/BUILDROOT/python-hypothesis-5.12.0-1.fc33.x86_64/usr/lib/python3.9/site-packages pytest tests/cover/test_lookup.py::test_infers_args_f or_namedtuple_builds ==================== test session starts ==================== platform linux -- Python 3.9.0a6, pytest-4.6.10, py-1.8.0, pluggy-0.13.0 rootdir: /builddir/build/BUILD/hypothesis-hypothesis-python-5.12.0, inifile: pytest.ini plugins: hypothesis-5.12.0, xdist-1.32.0, forked-1.1.1 collected 1 item tests/cover/test_lookup.py . [100%] ================= slowest 20 test durations ================= 0.23s call hypothesis-python/tests/cover/test_lookup.py::test_infers_args_for_namedtuple_builds 0.02s setup hypothesis-python/tests/cover/test_lookup.py::test_infers_args_for_namedtuple_builds (0.00 durations hidden. Use -vv to show these durations.) ================= 1 passed in 0.45 seconds ================== sh-5.0# grep -rl test_infers_args_for_namedtuple_builds * tests/cover/__pycache__/test_lookup.cpython-39-PYTEST.pyc tests/cover/test_lookup.py sh-5.0# PYTHONPATH=/builddir/build/BUILDROOT/python-hypothesis-5.12.0-1.fc33.x86_64/usr/lib/python3.9/site-packages pytest tests/cover/test_lookup.py::test_infers_args_for_namedtuple_from_type ==================== test session starts ==================== platform linux -- Python 3.9.0a6, pytest-4.6.10, py-1.8.0, pluggy-0.13.0 rootdir: /builddir/build/BUILD/hypothesis-hypothesis-python-5.12.0, inifile: pytest.ini plugins: hypothesis-5.12.0, xdist-1.32.0, forked-1.1.1 collected 1 item tests/cover/test_lookup.py . [100%] ================= slowest 20 test durations ================= 0.20s call hypothesis-python/tests/cover/test_lookup.py::test_infers_args_for_namedtuple_from_type 0.02s setup hypothesis-python/tests/cover/test_lookup.py::test_infers_args_for_namedtuple_from_type (0.00 durations hidden. Use -vv to show these durations.) ================= 1 passed in 0.42 seconds ================== sh-5.0# grep -rl test_resolves_forward_references_outside_annotations * tests/cover/test_lookup_py36.py tests/cover/__pycache__/test_lookup_py36.cpython-39-PYTEST.pyc sh-5.0# PYTHONPATH=/builddir/build/BUILDROOT/python-hypothesis-5.12.0-1.fc33.x86_64/usr/lib/python3.9/site-packages pytest tests/cover/test_lookup_py36.py::test_resolves _forward_references_outside_annotations ==================== test session starts ==================== platform linux -- Python 3.9.0a6, pytest-4.6.10, py-1.8.0, pluggy-0.13.0 rootdir: /builddir/build/BUILD/hypothesis-hypothesis-python-5.12.0, inifile: pytest.ini plugins: hypothesis-5.12.0, xdist-1.32.0, forked-1.1.1 collected 1 item tests/cover/test_lookup_py36.py . [100%] ================= slowest 20 test durations ================= 0.93s call hypothesis-python/tests/cover/test_lookup_py36.py::test_resolves_forward_references_outside_annotations 0.01s setup hypothesis-python/tests/cover/test_lookup_py36.py::test_resolves_forward_references_outside_annotations (0.00 durations hidden. Use -vv to show these durations.) ================= 1 passed in 0.95 seconds ================== sh-5.0# PYTHONPATH=/builddir/build/BUILDROOT/python-hypothesis-5.12.0-1.fc33.x86_64/usr/lib/python3.9/site-packages pytest tests/cover/test_lookup.py::test_infers_args_for_namedtuple_from_type^C sh-5.0# grep -rl test_specialised_collection_types * tests/cover/__pycache__/test_lookup.cpython-39-PYTEST.pyc tests/cover/test_lookup.py sh-5.0# PYTHONPATH=/builddir/build/BUILDROOT/python-hypothesis-5.12.0-1.fc33.x86_64/usr/lib/python3.9/site-packages pytest tests/cover/test_lookup.py::test_specialised_collection_types ==================== test session starts ==================== platform linux -- Python 3.9.0a6, pytest-4.6.10, py-1.8.0, pluggy-0.13.0 rootdir: /builddir/build/BUILD/hypothesis-hypothesis-python-5.12.0, inifile: pytest.ini plugins: hypothesis-5.12.0, xdist-1.32.0, forked-1.1.1 collected 14 items tests/cover/test_lookup.py .............. [100%] ================= slowest 20 test durations ================= 0.44s call hypothesis-python/tests/cover/test_lookup.py::test_specialised_collection_types[typ12-coll_type12-int] 0.34s call hypothesis-python/tests/cover/test_lookup.py::test_specialised_collection_types[typ2-dict-int] 0.34s call hypothesis-python/tests/cover/test_lookup.py::test_specialised_collection_types[typ4-dict_values-int] 0.34s call hypothesis-python/tests/cover/test_lookup.py::test_specialised_collection_types[typ11-coll_type11-int] 0.31s call hypothesis-python/tests/cover/test_lookup.py::test_specialised_collection_types[typ3-dict_keys-int] 0.30s call hypothesis-python/tests/cover/test_lookup.py::test_specialised_collection_types[typ10-coll_type10-int] 0.28s call hypothesis-python/tests/cover/test_lookup.py::test_specialised_collection_types[typ0-set-int] 0.26s call hypothesis-python/tests/cover/test_lookup.py::test_specialised_collection_types[typ1-frozenset-int] 0.23s call hypothesis-python/tests/cover/test_lookup.py::test_specialised_collection_types[typ9-coll_type9-int] 0.21s call hypothesis-python/tests/cover/test_lookup.py::test_specialised_collection_types[typ7-tuple-int] 0.20s call hypothesis-python/tests/cover/test_lookup.py::test_specialised_collection_types[typ5-list-int] 0.19s call hypothesis-python/tests/cover/test_lookup.py::test_specialised_collection_types[typ8-coll_type8-int] 0.13s call hypothesis-python/tests/cover/test_lookup.py::test_specialised_collection_types[A_NamedTuple-tuple-int] 0.10s call hypothesis-python/tests/cover/test_lookup.py::test_specialised_collection_types[typ6-tuple-int] 0.02s setup hypothesis-python/tests/cover/test_lookup.py::test_specialised_collection_types[typ2-dict-int] 0.02s setup hypothesis-python/tests/cover/test_lookup.py::test_specialised_collection_types[typ0-set-int] 0.02s setup hypothesis-python/tests/cover/test_lookup.py::test_specialised_collection_types[A_NamedTuple-tuple-int] 0.02s setup hypothesis-python/tests/cover/test_lookup.py::test_specialised_collection_types[typ10-coll_type10-int] 0.02s setup hypothesis-python/tests/cover/test_lookup.py::test_specialised_collection_types[typ11-coll_type11-int] 0.02s setup hypothesis-python/tests/cover/test_lookup.py::test_specialised_collection_types[typ4-dict_values-int] ================= 14 passed in 4.11 seconds ================= ``` This addresses issue #2427 . --- hypothesis-python/src/hypothesis/internal/compat.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hypothesis-python/src/hypothesis/internal/compat.py b/hypothesis-python/src/hypothesis/internal/compat.py index 08853a6b80..fd045a3464 100644 --- a/hypothesis-python/src/hypothesis/internal/compat.py +++ b/hypothesis-python/src/hypothesis/internal/compat.py @@ -92,7 +92,9 @@ def is_typed_named_tuple(cls): return ( issubclass(cls, tuple) and hasattr(cls, "_fields") - and hasattr(cls, "_field_types") + and (hasattr(cls, "_field_types") + or hasattr(cls, "__annotations__") + ) )