astral-sh / ruff

An extremely fast Python linter and code formatter, written in Rust.
https://docs.astral.sh/ruff
MIT License
31.02k stars 1.03k forks source link

Ruff does not recognise the fictitious `builtins.ellipsis` type from typeshed #11943

Closed RandallPittmanOrSt closed 2 months ago

RandallPittmanOrSt commented 2 months ago

ellipsis is a valid name in type signatures, but Ruff doesn't seem to have it its grammar.

trag1c commented 2 months ago

ruff is right, there's no ellipsis; you probably meant Ellipsis?

RandallPittmanOrSt commented 2 months ago

ruff is right, there's no ellipsis; you probably meant Ellipsis?

No, you can't use Ellipsis in an annotation as it is a variable (according to both mypy and Pyright).

I'm basing my annotations on those of numpy.ndarray.__getitem__: https://github.com/numpy/numpy/blob/64ee06a641f8bd087bd74712ef4b1cbd1241f08c/numpy/__init__.pyi#L1514

AlexWaygood commented 2 months ago

This is due to a historical hack in typeshed, where a fictitious type "builtins.ellipsis" is invented for static type checkers so that they are able to understand what the type of the Ellipsis singleton is: https://github.com/python/typeshed/blob/fdce887b9479a4c8a69ea040145f276caa28ce0e/stdlib/builtins.pyi#L1834-L1850. It would be nice to remove this hack over at typeshed, but it might be difficult: some people (such as numpy, apparently) are using the hack in their annotations, so there are backwards compatibility implications; the "real" type of Ellipsis is only exposed at runtime as types.EllipsisType on Python 3.10+, while typeshed still supports Python 3.8; and some type checkers might also rely on builtins.ellipsis being in the stub.

Ruff does not use typeshed's stubs for resolving builtin symbols at the moment, although we will do at some point. At that point, we might start understanding the fictious builtins.ellipsis type in the same way that mypy and pyright do. Whether we do or don't, however, I'd encourage you to use types.EllipsisType instead of builtins.ellipsis if at all possible since, unlike builtins.ellipsis, types.EllipsisType actually exists at runtime.

RandallPittmanOrSt commented 2 months ago

@AlexWaygood You're totally right, I just discovered that. My mistake, thanks.

AlexWaygood commented 2 months ago

No need to apologise @RandallPittmanOrSt, this is a pretty obscure corner case! Happy to help :-)

I think I'll close this for now, though, since there's not much actionable for us here -- we're already working on using typeshed stubs for resolving builtin symbols 👍

RandallPittmanOrSt commented 2 months ago

Thanks again, @AlexWaygood. FFR, here's ~my workaround~ a modified version of typeshed's hack as a workaround:

mypy doesn't like overriding typeshed's ellipsis hack, hence the if not TYPE_CHECKING

from typing import TYPE_CHECKING
import sys
if sys.version_info >= (3, 10):
    from types import EllipsisType
    ellipsis = EllipsisType
elif not TYPE_CHECKING:
    class ellipsis: ...

In a type stub, the @type_check_only decorator can be added to the ellipsis class.