Skip to content

Conversation

@carljm
Copy link
Contributor

@carljm carljm commented Jun 6, 2025

Summary

Fixes astral-sh/ty#557

Test Plan

Stable property tests succeed with a million iterations. Added mdtests.

@carljm carljm added the ty Multi-file analysis & type inference label Jun 6, 2025
Copy link
Member

@AlexWaygood AlexWaygood left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This special form is callable:

@github-actions
Copy link
Contributor

github-actions bot commented Jun 6, 2025

mypy_primer results

No ecosystem changes detected ✅

@carljm carljm marked this pull request as draft June 6, 2025 16:53
@carljm
Copy link
Contributor Author

carljm commented Jun 6, 2025

@AlexWaygood It looks like ChainMap, DefaultDict, Counter, Deque, and OrderedDict (the collections proxies) are callable at runtime, but in typeshed are annotated as _Alias, which has no __call__. Do you think we should model their runtime callability as a special case, or trust typeshed here?

@AlexWaygood
Copy link
Member

AlexWaygood commented Jun 6, 2025

The collections proxies are callable, but the builtins proxies aren't?! That's... Odd. I had in my head that all the legacy stdlib aliases were not callable!

We should probably treat them as callable if they are at runtime — can we just delegate to the constructor signatures of the classes they're aliasing? I don't love the idea of hardcoding their constructors' signatures somewhere in our model — some of those classes have reasonably complicated constructor methods IIRC

@carljm
Copy link
Contributor Author

carljm commented Jun 7, 2025

can we just delegate to the constructor signatures of the classes they're aliasing

Yeah, that should be possible. It's beyond the scope of what I was looking to do in this PR, but I added a TODO for it.

@carljm carljm marked this pull request as ready for review June 7, 2025 02:08
@carljm
Copy link
Contributor Author

carljm commented Jun 7, 2025

can we just delegate to the constructor signatures of the classes they're aliasing

In that case, is there any reason not to just treat them as direct aliases? That is, remove their SpecialForm type entirely and act as if typing.pyi imported them directly from collections?

@AlexWaygood
Copy link
Member

AlexWaygood commented Jun 7, 2025

PEP 585 states:

Importing [the legacy aliases] from typing is deprecated. Due to PEP 563 and the intention to minimize the runtime impact of typing, this deprecation will not generate DeprecationWarnings. Instead, type checkers may warn about such deprecated usage when the target version of the checked program is signalled to be Python 3.9 or newer. It’s recommended to allow for those warnings to be silenced on a project-wide basis.

I think pyright implements this deprecation, and it would be nice if we could eventually as well. If we didn't distinguish between typing.ChainMap and collections.ChainMap at all in our model, that might be tricky?

Edit: Hmm, come to think of it, though, we'll have the same "tricky" issue when it comes to implementing deprecation warnings for typing.Iterator, typing.Iterable, typing.Mapping, etc. According to typeshed, these aren't just aliases for stdlib classes in collections.abc -- according to typeshed, these are actually defined in typing (and re-exported in collections.abc). There have been numerous attempts to move the class definitions to the collections.abc stub, but it's tough because existing type checkers hardcode assumptions that these class definitions will be found in typing.

Given that we'll maybe have to solve that issue for the collections.abc aliases anyway, maybe it doesn't actually help us at all to represent typing.ChainMap as a different type internally to collections.ChainMap.

@AlexWaygood
Copy link
Member

The collections proxies are callable, but the builtins proxies aren't?! That's... Odd. I had in my head that all the legacy stdlib aliases were not callable!

It looks like the builtin aliases are specifically excluded from being callable at runtime by passing the inst=False keyword argument: https://github.com/python/cpython/blob/24069fbca861a5904ee7718469919e84828f22e7/Lib/typing.py#L2701-L2785. Why the decision was made to treat these aliases differently to other stdlib aliases, I'm not sure.

@carljm carljm force-pushed the cjm/callable-vs-specialform branch from 88465fd to f7a8479 Compare June 10, 2025 15:48
@carljm carljm enabled auto-merge (squash) June 10, 2025 15:49
@carljm carljm merged commit a2de81c into main Jun 10, 2025
35 checks passed
@carljm carljm deleted the cjm/callable-vs-specialform branch June 10, 2025 20:25
dcreager added a commit that referenced this pull request Jun 10, 2025
* main:
  [ty] implement disjointness of Callable vs SpecialForm (#18503)
  [ty] more simplification of infer_parameterized_legacy_typing_alias (#18526)
  [`refurb`] Add a note about float literal handling (`FURB157`) (#18615)
@dhruvmanila dhruvmanila added the bug Something isn't working label Jun 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Daily property test run failed on Sat May 31 2025

4 participants