Hi Folks!

Having dropped Python 2.7 support in Qpid Proton and Qpid Dispatch recently
(see thread "Some (long awaited?) suggestions to deprecate Python 2 and C++
03 support" from Andrew Stitcher), I propose to introduce Python type
annotations into the codebase.

Optional typing syntax was added in Python 3.5 and Python 3.6. The syntax
is (subjectively speaking) quite ugly (many people think that,
https://lwn.net/Articles/643269/). On the other hand, automatically
typechecked type annotations provide numerous benefits to the users:

  * Up-to-date documentation: oftentimes, type signatures give strong usage
hints when trying to use an API. Am I allowed to pass None here? When the
type annotation says Optional[str], the answer is clear. Can this method
return None?
  * Code completion and linting: There is IDE support as well as standalone
static type checkers for Python type annotations. The IDE uses the
annotation information for code autocompletion. Both the checkers and IDEs
report type mismatches in code.

The best single-source introduction to optional types in Python is probably
the MyPy documentation,
https://mypy.readthedocs.io/en/stable/introduction.html.

One example of AMQP 1.0 client (for JavaScript) that ships typing stubs is
https://github.com/amqp/rhea, contributed in
https://github.com/amqp/rhea/pull/70.

# Inline or stubs?

There are two possibilities for writing type annotations in Python. Either
stubs in separate .pyi files, or inline annotations in function definitions.

The stubs approach allows to keep the ugly typing syntax out of the main
sources. Historically it was the approach used when Python 2.7
compatibility had to be maintained or when the types were being written by
somebody other than the library author. To simplify working with stubs,
PyCharm IDE draws a star gutter icon for module members annotated in a
separate stub file that allows switching back and forth.

I feel that stubs would make some sense for Qpid Proton, and less sense for
Qpid Dispatch. Proton is a library defining a public API, changes to which
happen infrequently. Stubs allow to hide the syntactic ugliness. On the
other hand, inline types are easier to work with as it is not necessary to
switch between files much. From a library user's point of view, the chosen
approach does not matter.

Over all, my preference goes to inline type annotations, simply because
they are easier to write and maintain.

Due to the notorious ugliness of the inline syntax, a reformat of parameter
lists may be needed. As an example, going from

def f(self, a, b=None, c=42):
    ...

to

def f(
    self,
    a: Union[A, B, C],
    b: Optional[str] = None,
    c: int = 42
) -> Dict[str, Any]:
    ...

See https://github.com/sphinx-doc/sphinx/issues/5868 for one more hairy
example.

# Which checker?

  * https://github.com/python/mypy

  * https://github.com/facebook/pyre-check
  * https://github.com/google/pytype
  * https://github.com/microsoft/pyright

I believe MyPy should be supported at first, and one other checker can be
added later. The checkers all use the same type syntax, but they differ in
how they do type inference and therefore what errors they report on a
particular piece of code. Code which passes MyPy might fail on Pyre, for
example.

https://google.github.io/pytype/faq.html#how-is-pytype-different-from-other-type-checkers

# Initial annotations proposal

I propose to generate initial type annotations by capturing runtime types
using https://github.com/Instagram/MonkeyType, introduce them inline, and
edit out the most egregious artifacts that this method produces (i.e. large
Union types where the algorithm fails to divine the programmer's intention
and does not generalize the type). In a subsequent commit, I propose adding
a MyPy CI job and finetune the types and write more of them so that the
check passes. Finally, I'd like to attempt to make MyPy check pass in
--strict mode.

Having typechecking in place should be helpful for PROTON-2095 Move away
from SWIG to CFFI, in order to exactly preserve the API.
-- 
Mit freundlichen Grüßen / Kind regards
Jiri Daněk

Reply via email to