Add typing stubs
This pull request adds some basic configuration for non-automated running of mypy, and obviously the typing stubs and py.typed marker. Only one source file was modified to replace the private _frozen_importlib(_external) import with importlib.machinery. I could see no reason for this being used, and it made typehinting the Loaders extremely verbose, difficult, and required silencing of multiple errors.
If you do not want the maintenance-burden, feel free to close this, and I will submit to typeshed instead.
The stubs were compared to API reference docs. The private code that had doc-strings that were not exported in the API reference was also checked against, but there may be some discrepancies that I missed there.
In the stubs, I added simple # documented comments to functions/classes/methods/attributes. Additionally, discrepancies like contradicting documentation such as missing or extra parameters, different parameter types, or normal attributes being called properties were commented on. You can get the more relevant comments with something like grep -RIP '#(?!\s(?:documented$|START|END|noqa|@))'.
However, in distlib.resources, two probable Liskov Substitution Principle violations and one outright error are explicitly marked with FIXME. Besides these, it passes mypy's strict mode as well as stubtest.
These stubs are essentially complete except for the omission of compat; however, for any maintainers or for anyone else that comes across this, the following are ways these stubs may be improved or revised:
-
Fix the commented documentation discrepancies and then remove said comments
-
Add
ClassVar[...]to class variables that should not be useable as an instance variable. -
Add
Final[...]to variables/attributes that should not be reassigned, redefined, or overridden. This alone is not quite the same as a constant. It does not work with try/except or with if/else statements unless it is written as a ternary.Finalonly prevents name re-binding. If immutability is desired, anABCis required (also see 6. below). Example from docs:
Alpha: Final[str] = ['a', 'b']
Beta: Final[Sequence[str]] = ['a', 'b']
Alpha.append('c') # this works
Beta.append('c') # Error
4.1 Add @final decorator to:
A. Classes that should not be inherited from.
B. Methods that should not be overriden in a subclass.
4.2 Evaluate whether existing @property methods should be @final to prevent overriding.
-
Add
TypedDicts fordistlib.metadata. In that module, I made a comment about this and not knowing all possible keys nor their acceptable types. -
Use
collection.abc ABC's for parameters where appropriate. -
Protocols could be used to enforce instance attribute requirements, e.g.distlib.index'sPackageIndex.upload_file'smetadataparameter is documented as requiring aMetadatainstance with at leastNameandVersionfields set. If this level of enforcement is desired, I believeProtocols can do it.