typing_extensions icon indicating copy to clipboard operation
typing_extensions copied to clipboard

Run the CPython typing test suite against typing-extensions

Open JelleZijlstra opened this issue 7 months ago • 14 comments

I propose to add a workflow that:

  • Grabs test_typing.py from CPython
  • Replaces all typing imports with typing_extensions through some hackery (either sed or some cleverness with importlib)
  • Runs the test suite

This would help us catch issues where something got changed in CPython and we didn't reapply it to our variant. For example, this would allow us to catch issues in our TypedDict implementation, which is currently different from CPython's on all versions.

We'd have to run this only on matching versions (e.g., run the CPython tests from the 3.14 branch on Python 3.14) because the CPython tests may rely on new syntax or standard library features.

Feel free to pick this up if you see this and are interested! I may do it eventually when I find time.

JelleZijlstra avatar May 25 '25 15:05 JelleZijlstra

Note that we already run test_typing after typing_extensions has been installed, but that's not as comprehensive as what's being proposed here: https://github.com/python/typing_extensions/blob/44de568f73a93f29e52c2fc2d5f149305a4a3bae/.github/workflows/ci.yml#L74-L81

AlexWaygood avatar May 25 '25 15:05 AlexWaygood

Right, that just tests that the monkeypatches we do to typing don't break things.

JelleZijlstra avatar May 25 '25 15:05 JelleZijlstra

I decided to take this issue, and here are the errors that were received as a result of running it (some of them are related to the fact that there are __module__ checks): https://github.com/donBarbos/typing_extensions/actions/runs/16691671387/ https://github.com/donBarbos/typing_extensions/actions/runs/16691671387/job/47250262252 (example job on 3.14)

donbarbos avatar Aug 02 '25 11:08 donbarbos

Thanks, looks like a bunch of them are the result of typing tests testing some private objects in the module. Possibly fixable in the test by monkeypatching typing_extensions to have a module __getattr__ method that delegates to typing.

JelleZijlstra avatar Aug 02 '25 14:08 JelleZijlstra

Thanks, I've added the patch you suggested. Regarding the __module__ issues, I wanted to fix them like this:

_mod = sys.modules[__name__]
for _name in dir(_mod):
    _obj = getattr(_mod, _name)
    if hasattr(_obj, "__module__") and _obj.__module__ == __name__:
        try:
            _obj.__module__ = "typing"
        except (AttributeError, TypeError):
            pass

But then there are errors with pickle serialization. for example, in test_pickling_then_unpickling_tuple_with_typevartuple_equality

This is what output looks like only with __getattr__ patch now: https://github.com/donBarbos/typing_extensions/actions/runs/16731514906/job/47360435347

donbarbos avatar Aug 04 '25 18:08 donbarbos

I am trying to have a look at the errors that come up with @donBarbos currenty test runs over the CPython test (in 3.14 for now) but running into the problem where the typing_extensions tests explicitely expect the opposite.

test_typing.py:

    def test_cannot_make_mutable_key_readonly(self):
        class Base(TypedDict):
            a: int

        with self.assertRaises(TypeError):
            class Child(Base):
                a: ReadOnly[int]

test_typing_extensions.py:

    def test_make_mutable_key_readonly(self):
        class Base(TypedDict):
            a: int

        self.assertEqual(Base.__readonly_keys__, frozenset())
        self.assertEqual(Base.__mutable_keys__, frozenset({'a'}))

        class Child(Base):
            a: ReadOnly[int]  # type checker error, but allowed at runtime

        self.assertEqual(Child.__readonly_keys__, frozenset({'a'}))
        self.assertEqual(Child.__mutable_keys__, frozenset())

Very simple example showing this:

from typing_extensions import TypedDict as TypedDictExt
from typing_extensions import ReadOnly as ReadOnlyExt
class Base(TypedDictExt):
        a: int

class Child(Base):
    a: ReadOnlyExt[int]  # type checker error, but allowed at runtime

from typing import TypedDict, ReadOnly
class Base(TypedDict):
        a: int

class Child(Base):
    a: ReadOnly[int]  # type checker error, but allowed at runtime


$ uv run --python 3.14 my_test.py
Traceback (most recent call last):
  File "D:\Programming\Projects\typing_extensions\src\my_test.py", line 14, in <module>
    class Child(Base):
    ^^^^^^^^^^^^^^^^^^
        a: ReadOnly[int]  # type checker error, but allowed at runtime
        ^^^^^^^^^^^^^^^^
  File "C:\Users\Jan-Eric\AppData\Roaming\uv\python\cpython-3.14.0a4-windows-x86_64-none\Lib\typing.py", line 3206, in __new__
    raise TypeError(
    ...<2 lines>...
    )
TypeError: Cannot override mutable key 'a' with read-only key


I couldnt find anything about the TypeError in the docs, but it has been present in the code since the introduction: https://github.com/python/cpython/blob/3.13/Lib/typing.py#L3208

If i change this to match CPythons i get 3.14 and 3.13 tests to pass:

https://github.com/JanEricNitschke/typing_extensions/actions/runs/17633620675 https://github.com/python/typing_extensions/compare/main...JanEricNitschke:typing_extensions:run-patched-test_typing

I found this commit: https://github.com/python/typing_extensions/commit/d6c50f585c386490d38ad6b8ce5543aed6e633a2 which linked to this dicussion: https://discuss.python.org/t/pep-705-read-only-typeddict-items/37867/40 but the point was not raised on the PR into CPython which included the TypeError: https://github.com/python/cpython/pull/116350

JanEricNitschke avatar Sep 11 '25 04:09 JanEricNitschke

We should making typing-extensions match the typing behavior. (I might have preferred not erroring at runtime but it's too late now.) Finding this kind of discrepancy is exactly why I opened this issue.

JelleZijlstra avatar Sep 11 '25 14:09 JelleZijlstra

Nice, ok. Actually currently going through these to try to address them. Probably create individual PRs for the first two issues tomorrow.

JanEricNitschke avatar Sep 11 '25 14:09 JanEricNitschke

@JelleZijlstra sorry, but I want to clarify that we did decide to revert typing_extensions behavior related to the runtime error in PEP 705 that I mentioned here?

It seems that, although changing the typing does break backward compatibility, this option is still preferable. The drop of runtime error was intentional, and it looks like the same behavior was meant to be introduced into typing as well. Maybe it would be worth opening a separate issue to discuss the implications of this backward incompatibility (or perhaps this isn’t really something that needs discussion, I'm not sure :).

donbarbos avatar Sep 11 '25 16:09 donbarbos

I don't feel too strongly about which of typing and typing-extensions should change, but it's easier to change typing-extensions. Probably best to open a new issue to lay out the options and discuss.

JelleZijlstra avatar Sep 11 '25 17:09 JelleZijlstra

Is that something fro here, the CPython repo or discuss?

JanEricNitschke avatar Sep 12 '25 04:09 JanEricNitschke

Let's start with here.

JelleZijlstra avatar Sep 12 '25 15:09 JelleZijlstra

From what i understand the TypeError in CPython for this behaviour is pretty unique, as i dont think any other such attribute is runtime enforced.

typing.Final and typing.ReadOnly even specify "There is no runtime checking for this property." in the docs. (Although that obviously refers them only being read, but still).

Additionally the typing docs in general state "The Python runtime does not enforce function and variable type annotations. They can be used by third party tools such as type checkers, IDEs, linters, etc.". Lastly, neither the docs nor the PEP say anything about such a type error existing.

So i definitely feel like the optimal thing here would be to not have a runtime error.

On top of that i feel like removing a TypeError is probably a less intrusive change than adding one, even though changes in CPython are obviously more impactful than ones in typing_extensions.

JanEricNitschke avatar Sep 12 '25 17:09 JanEricNitschke

I tried to see what tests exactly are failing when we run the CPython tests:

Some i think are actual potential issues and i have created PRs for most of those. Although some are just error messages. The only one that doesnt have a PR yet is this one.

Otherwise there are some tests that explicitly assert behavior we are changing with backports. These can be silenced/fixed with some seds that fix the line or skip the test.

And there are a lot of tests that check reprs, assert identity or do pickling. For these i dont have the experience yet to know which are actual issues we want/need to address and which tests we should just fix/skip.

And the last set are where __module__ gets set wrong on exec and becomes "main" instead of None.

I tried to collect some information in this issue: https://github.com/JanEricNitschke/typing_extensions/issues/1

Here are the changes i have made so far: https://github.com/python/typing_extensions/compare/main...JanEricNitschke:typing_extensions:run-patched-test_typing

And here is the latest run: https://github.com/JanEricNitschke/typing_extensions/actions/runs/17707749820/job/50322109340

JanEricNitschke avatar Sep 14 '25 07:09 JanEricNitschke