-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Make Any
a proper class instead of an alias to object()
#13520
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
Conversation
This comment has been minimized.
This comment has been minimized.
This is technically more accurate but as you can see it interacts poorly with the existing special casing in some type checkers. Any is a type system primitive and needs to be special-cased in type checkers anyway; it might make more practical sense to stick with the existing definition. |
From a mypy perspective, based on primer this is actually probably an improvement (at least on 3.11+, and maybe depending on what you think TypeForm semantics should be). pyright seems pretty unhappy though. |
This comment has been minimized.
This comment has been minimized.
Stubtest errors are unrelated. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Pyright has been updated and now it's OK with the change. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems good to me.
For red-knot, I'd honestly prefer for us to be forced to special-case We've already implemented the necessary special-casing in red-knot that allows users to inherit from |
(Disclaimer: I'm obviously just one developer working on red-knot; I can't claim to speak for everybody on the red-knot team!) |
I prefer annotations to follow implementation if possible. Since this needs to be special-cased by type checkers anyway, this shouldn't be a problem for them. But nowadays type annotations are not only used by type checkers. |
This comment has been minimized.
This comment has been minimized.
Note that this is only true on Python 3.11+, but typeshed supports Python 3.8+. It was changed in python/cpython@5a4973e. I'm still not enthusiastic about this PR, as I don't see it as bringing significant practical benefits to users. But having discussed it with other people on the red-knot team, I no longer want to block it. We'll be able to work around it without too much difficulty in red-knot, and it's true that it is more accurate to the runtime implementation on Python 3.11+. I'm happy for other maintainers to land it. |
Diff from mypy_primer, showing the effect of this PR on open source code: sphinx (https://github.com/sphinx-doc/sphinx)
+ sphinx/config.py:631: error: Unused "type: ignore" comment [unused-ignore]
ibis (https://github.com/ibis-project/ibis)
- ibis/expr/operations/__init__.py:13: error: Incompatible import of "Any" (imported name has type "type[ibis.expr.operations.reductions.Any]", local name has type "<typing special form>") [assignment]
+ ibis/expr/operations/__init__.py:13: error: Incompatible import of "Any" (imported name has type "type[ibis.expr.operations.reductions.Any]", local name has type "type[typing.Any]") [assignment]
- ibis/expr/types/logical.py:333: error: "<typing special form>" not callable [operator]
+ ibis/expr/types/logical.py:333: error: Too many arguments for "Any" [call-arg]
+ ibis/expr/types/logical.py:333: error: Unexpected keyword argument "where" for "Any" [call-arg]
+ note: "Any" defined here
+ ibis/expr/types/logical.py:340: error: "Any" has no attribute "to_expr" [attr-defined]
- ibis/backends/polars/compiler.py:745: error: No overload variant of "register" of "_SingleDispatchCallable" matches argument types "object", "Callable[[Any, KwArg(Any)], Any]" [call-overload]
- ibis/backends/polars/compiler.py:745: note: Possible overload variants:
- ibis/backends/polars/compiler.py:745: note: def register(self, cls: type[Any] | UnionType, func: None = ...) -> Callable[[Callable[..., Any]], Callable[..., Any]]
- ibis/backends/polars/compiler.py:745: note: def register(self, cls: Callable[..., Any], func: None = ...) -> Callable[..., Any]
- ibis/backends/polars/compiler.py:745: note: def register(self, cls: type[Any] | UnionType, func: Callable[..., Any]) -> Callable[..., Any]
prefect (https://github.com/PrefectHQ/prefect)
- src/prefect/server/events/actions.py:101: error: Unsupported left operand type for | ("<typing special form>") [operator]
+ src/prefect/server/events/actions.py:101: error: Unsupported left operand type for | ("type[Any]") [operator]
artigraph (https://github.com/artigraph/artigraph)
- src/arti/internal/type_hints.py:54: error: Non-overlapping identity check (left operand type: "type", right operand type: "<typing special form>") [comparison-overlap]
- src/arti/internal/type_hints.py:55: error: Non-overlapping identity check (left operand type: "type", right operand type: "<typing special form>") [comparison-overlap]
hydra-zen (https://github.com/mit-ll-responsible-ai/hydra-zen)
- src/hydra_zen/structured_configs/_implementations.py:3252: error: List comprehension has incompatible type List[tuple[str, Any | <typing special form>, Any | Field[Any]]]; expected List[tuple[str, type] | tuple[str, type, Any]] [misc]
steam.py (https://github.com/Gobot1234/steam.py)
- steam/ext/commands/converters.py:510: error: Incompatible types in assignment (expression has type "<typing special form>", variable has type "type[T]") [assignment]
+ steam/ext/commands/converters.py:510: error: Incompatible types in assignment (expression has type "type[Any]", variable has type "type[T]") [assignment]
mypy (https://github.com/python/mypy)
+ mypy/stubgenc.py:768: error: Unused "type: ignore" comment [unused-ignore]
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm in favor of this change. It better reflects the runtime, which I think is a good goal to strive for in general (although it obviously needs to be balanced against other considerations).
## Summary In python/typeshed#13520 the typeshed definition of `typing.Any` was changed from `Any = object()` to `class Any: ...`. Our automated typeshed updater pulled down this change in #17106, with the consequence that we no longer understand `Any`, which is... not good. This PR gives us the ability to understand `Any` defined as a class instead of `object()`. It doesn't remove our ability to understand the old form. Perhaps at some point we'll want to remove it, but for now we may as well support both old and new typeshed? This also directly patches typeshed to use the new form of `Any`; this is purely to work around our tests that no known class is inferred as `Unknown`, which otherwise fail with the old typeshed and the changes in this PR. (All other tests pass.) This patch to typeshed will shortly be subsumed by #17106 anyway. ## Test Plan Without the typeshed change in this PR, all tests pass except for the two `known_class_doesnt_fallback_to_unknown_unexpectedly_*` tests (so we still support the old form of defining `Any`). With the typeshed change in this PR, all tests pass, so we now support the new form in a way that is indistinguishable to our test suite from the old form. And indistinguishable to the ecosystem check: after rebasing #17106 on this PR, there's zero ecosystem impact.
At runtime,
typing.Any
is an actual class, not an object. Currently typeshed saysAny = object()
which seems wrong given it's not valid Python to useobject()
as type annotations, e.g.I'm guessing that existing type checkers probably had some special cases for
typing.Any
to make it work. But if we define it right we could probably get rid of the special cases.We ran into this issue when working on Pyre2.