Closed o11c closed 7 years ago
Thanks for that detailed summary! Yes, we definitely have a lot of "hidden" types that, nonetheless, need to be exposed in the .pyi of a module.
So far, the standard strategy seems to be to add a leading underscore and a comment, e.g. in _codecs.pyi:
# Not exposed. In Python 2, this is defined in unicode.c:
class _EncodingMap(object):
def size(self) -> int: ...
I wonder whether PEP 484 should have some special syntax to hide things more explicitly. A class decorator, perhaps. But on the other hand, Python already has a convention (prefix "_") for marking things as private, and there's no really good reason for reinventing the wheel.
I vote for always clearly marking them as "not exposed", though, to avoid confusion.
Also, I'm not against sticking these special types into a file of their own (stubtool.types
or similar), but we should only do it for types that are actually used by more than one stub. If a type is only used locally in a single module, that's where it should go.
For manually written stubs that are only used by checkers, the underscore rule makes sense. But remember typing.get_type_hints
(and also tools that generate stubs).
I like the decorator approach - perhaps @typing.deleted
(since the effect is that of class Foo: pass; del Foo
) - but what exactly does it mean? We need to decide who can see the name then. Perhaps "when checking a .pyi
file, allow full access; in a .py
file, forbid from ... import ...
and forbid mod.foo
unless it is stringified"? But that seems complicated, which is bad. And .pyi
files aren't used at all for typing.get_type_hints
at all ...
I'm quite surprised to find no return type declared for csv.writer
.
I think there's nothing actionable here at this point. We've been handling types like this by using leading underscores and I don't think that has caused problems. As we develop tools like https://github.com/JelleZijlstra/stubcheck, we'll be able to find any types that don't exist at runtime but that have crept into the stubs.
Some classes don't exist under their inspected name. I define inspected name as
__module__.__qualname__
, but only if__qualname__
ends with__name__
, else__module__.__name__
.There are several (overlapping) cases:
types
..<locals>.
in__qualname__
), and may be created more than once.In some cases, later python versions do add a name for the type, but since we need to support all of them, we can't rely on that.
Note that
_builtin()
means either__builtin__
orbuiltins
, and_collections_abc
means either_collections_abc
,collections.abc
, or_abccoll
depending on Python version.Here is my initial mapping, based on importing every module (except test modules and dummy implementations, and being careful with a few modules that can't be imported in the wrong order) in the standard library (for CPython 2.7 and 3.2 though 3.5), checking every module for top-level attributes, and then finally checking
object.__subclasses__
. This is actually quite fast, just a few seconds.Notes:
.__class__
, that means that there is a single canonical instance.''
and there is a comment, see it.''
otherwise, I could not locate a canonical location for the class.My thought is to add a
stubtool.types
module similar totypes
and ask people to use it where appropriate. I think we do need to supply classes likebytearray_iterator
, even if most stubs will just useIterator[int]
(though we do need to do something about__length_hint__()
in general).Feedback?