typing icon indicating copy to clipboard operation
typing copied to clipboard

Best Practices document

Open srittau opened this issue 4 years ago • 3 comments

  • Built-in generics
  • New union syntax
  • No union return types, use Any or T | Any
  • Discuss Any vs object
  • Callbacks: Use return type object instead of None or Any if the returned value is ignored

Please add anything you think the document should contain.

srittau avatar Aug 24 '21 06:08 srittau

What knowledge should the document assume and what are prerequisite documents to read? The typing tutorial + type stub tutorial? If yes then I think this is blocked by those two documents being created first just to build on them.

One practice common in my team is if python 3.7+ is fine (most of our code) then use from future import annotations and feel free to use features in newer version. We're currently on 3.7 and about to migrate to 3.8 before being stuck there for a couple months until 1 dependency updates. If you are in 3.6 you can still use string annotations for new syntax.

Another one is prefer TypedDict over dicts. Sometimes we do need a mapping[str, str] and don't know much about input argument, but a lot of times we do know the structure of dict. I often see people new to types used dict when a specific typeddict is actual expectation. Similarly NamedTuple > namedtuple. Most of the time where mapping is correct its utility functions while main business/api logic has more knowledge of the dict. If you are indexing elements by specific values like d['foo'] then mapping can probably be typeddict instead.

Unsure if this goes in tutorial or best practices, method compatibility check (liskov substituation) is an important one I occasionally see broken. One common reason I see it broken is from superclass really being generic on a type but not stating it.

Include partial/py.typed for any libraries you make. Avoid relying on implicit imports. Specifically the section on what symbols are publicly exported from a module here. Some libraries I use heavily ignore this rule which leads to some type checker confusion. The from x import A as A syntax is one that's pretty uncommon that I usually need to explain.

May fit better in a different doc, but one practice that's helped a lot in improving type coverage of our codebase is adding a CI check that runs type checkers twice on changed files and requires the error count to go down by 5 on every PR compared to master. It's pretty difficult for 1/2 people to add types to large codebase so making a CI rule that pushes in that direction has helped a lot. Any type checker is fine for this and I currently run 2 in CI. Simpler check of requiring 0 type errors would have been unfeasible when a lot files had 100+ type errors (mostly missing types).

There's also question of how to handle recent pep type features that are not supported by all type checkers yet. My current practice is to allow them if some checker handles it and type ignore as needed for other checker (recursive types being one). That's very debatable practice though and it may be better for a best practices doc to avoid type features missing support in a popular checker.

hmc-cs-mdrissi avatar Sep 05 '21 05:09 hmc-cs-mdrissi

We should include a section about I/O types:

  • Use protocols if possible for argument types.
  • Use concrete types for return types. Ideally, these derive from typing.IO.
  • Use typing.IO, not io.IOBase.

Came up in python/typeshed#6067.

srittau avatar Sep 27 '21 12:09 srittau

Another best practice: Use from __future__ import annotations. (Which of course @hmc-cs-mdrissi already mentioned and I overlooked.)

srittau avatar May 21 '22 15:05 srittau