Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mypy/static type annotations #128

Open
warsaw opened this issue Aug 15, 2017 · 11 comments
Open

Add mypy/static type annotations #128

warsaw opened this issue Aug 15, 2017 · 11 comments
Labels
in progress Someone has started working on the issue but no PR yet tech-debt Things that needs to be tidied up to avoid being bitten in the future...

Comments

@warsaw
Copy link
Contributor

warsaw commented Aug 15, 2017

It seems reasonable to add type annotations since aiosmtpd is a Python 3 application/library and type annotations can provide better documentation to users. With the use of a tool like mypy, those assertions can even be tested.

We'll pretty quickly run into problems though since our Handler classes are duck typed, and mypy does not yet support structural typing. Meaning, examples such as Controller.__init__()'s handler argument does not have to be a subclass of anything; it simply needs to support some methods. Handlers are even weirder than straight up duck types too, because handle_*() methods are optional.

Similarly, SMTP methods smtp_*() are optional too.

Some things that might help include:

  • PEP 544 defines "Protocols" that can be used to structurally type arguments;
  • Techniques such as those described in this blog post;
  • Just using an Any type for the duck typed arguments;
  • Require that handlers derive from a common base class. I don't like this solution much as it was essentially rejected for an earlier PR, and it would be an API change requiring a major version number bump
@emmatyping
Copy link

emmatyping commented Aug 16, 2017

Protocols just landed in Mypy, so you could use Mypy from git (Zulip does this), you can take advantage of Protocols today once they are in typing extensions or mypy extensions! Most of the protocol related docs are in https://mypy.readthedocs.io/en/latest/class_basics.html. You will also want to track python/mypy#3838

@pepoluan
Copy link
Collaborator

pepoluan commented Oct 10, 2020

  • PEP 544 defines "Protocols" that can be used to structurally type arguments;

This sounds good. Since we (1) indirectly pulled typing-extensions courtesy of atpublic, and (2) drop 3.5 because of, again, atpublic, we can start implementing Protocols.

Edit: However, the fact that handle_* methods and smtp_* methods are optional and PEP 544 explicitly rejected support for annotating optional members, means that annotating the handler will be ... challenging.

@pepoluan pepoluan added the tech-debt Things that needs to be tidied up to avoid being bitten in the future... label Oct 22, 2020
@pepoluan
Copy link
Collaborator

pepoluan commented Dec 3, 2020

Just for kicks, I tried running mypy, and the result is ... not beautiful.

There are lots of instances where mypy couldn't infer the type of a variable, I had to pepper the code with lots of asserts.

@pepoluan
Copy link
Collaborator

It seems mypy does not do inference, it's 100% static.

In the code, there are lots of occasions where a name is reused but the content changes.

If we want to implement type checking, I suggest we use a type checker that does inference.

@emmatyping
Copy link

Mypy does do static type inference. For example:

a = {}
....
print(a['test'])

We don't know the best way to safely type a, so mypy asks the user what to do.

But if you do something like:

a = {}
a['test'] = 1
reveal_type(a) # main.py:3: note: Revealed type is 'builtins.dict[builtins.str, builtins.int]'

We can statically infer types. Also keep in mind that runtime and static types are not always 1-to-1.

Also for redefinition of types, there is --allow-redefinition.

I went ahead and ran mypy and it looks to me like most of the errors have to do with code paths potentially being None.

@pepoluan
Copy link
Collaborator

pepoluan commented Dec 27, 2020

I went ahead and ran mypy and it looks to me like most of the errors have to do with code paths potentially being None.

That is actually my problem with mypy; it does not understand the context.

Take, for instance, line 665 of smtp.py

mypy will complain:

aiosmtpd/smtp.py:665: error: Item "_Missing" of "Union[_Missing, bytes]" has no attribute "split"

it does not understand that at that point, it is not possible for the loginpassword to be of type _Missing.

And a lot of mypy's complaints about "potentially being None", again is due to mypy not having the ability to do contextual inference. To make the code pass mypy I have to pepper the code with lots of assert isinstance()

pytype, on the other hand, analyzes the context and does not complain about that line. (It does complain about other things, but of totally different nature, and not as finicky as mypy).

Neither are perfect, but I find mypy's over-fastidiousness to be irritating and not worth the effort to fix.

@pepoluan
Copy link
Collaborator

Okay so fixed a bunch of annotations thanks to pytype. Still on the fence w.r.t. mypy, but at least we're now on our way towards implementing proper type hinting in #202 (part of 1.3 milestone, btw)

@pepoluan pepoluan added the in progress Someone has started working on the issue but no PR yet label Feb 22, 2021
@pepoluan
Copy link
Collaborator

We'll pretty quickly run into problems though since our Handler classes are duck typed

In 2.0, if Proposal No. 13 in #229 is agreed to, we can kinda enforce this per method.

pepoluan added a commit to pepoluan/aiosmtpd that referenced this issue Feb 24, 2021
Selection criteria:

* Must be useful, even if many warnings
  For example: Plugins related to annotations. As @warsaw raised in
  aio-libs#128, it's a good idea to type-annotate as much as possible.

* If not simple, must be maintained
  "maintained" here is overly-simplified to mean only "master has an
  update in 2020 or later".
  Some plugins have not received updates for a long time, but their
  logic are simple and really does not need updates, so those can also
  be included on a case-by-case basis.

* Will not need significant change in logic
  Adding annotations, removing temp vars used just for return values,
  those are not logic changes. So even if lots of instances need to be
  fixed, that is okay.
  Plugins that need a restructurization of the logic flow (e.g.,
  "flake8-cognitive-complexity") should not be included; or, if
  included, should not be activated. At least not until the next Epic.
pepoluan added a commit to pepoluan/aiosmtpd that referenced this issue Feb 24, 2021
Selection criteria:

* Must be useful, even if many warnings
  For example: Plugins related to annotations. As @warsaw raised in
  aio-libs#128, it's a good idea to type-annotate as much as possible.

* If not simple, must be maintained
  "maintained" here is overly-simplified to mean only "master has an
  update in 2020 or later".
  Some plugins have not received updates for a long time, but their
  logic are simple and really does not need updates, so those can also
  be included on a case-by-case basis.

* Will not need significant change in logic
  Adding annotations, removing temp vars used just for return values,
  those are not logic changes. So even if lots of instances need to be
  fixed, that is okay.
  Plugins that need a restructurization of the logic flow (e.g.,
  "flake8-cognitive-complexity") should not be included; or, if
  included, should not be activated. At least not until the next Epic.
@waynew
Copy link
Collaborator

waynew commented Feb 24, 2021

I'm definitely more of a fan of duck typing, so the structural/protocol typing would be 👍

pepoluan added a commit to pepoluan/aiosmtpd that referenced this issue Feb 25, 2021
Selection criteria:

* Must be useful, even if many warnings
  For example: Plugins related to annotations. As @warsaw raised in
  aio-libs#128, it's a good idea to type-annotate as much as possible.

* If not simple, must be maintained
  "maintained" here is overly-simplified to mean only "master has an
  update in 2020 or later".
  Some plugins have not received updates for a long time, but their
  logic are simple and really does not need updates, so those can also
  be included on a case-by-case basis.

* Will not need significant change in logic
  Adding annotations, removing temp vars used just for return values,
  those are not logic changes. So even if lots of instances need to be
  fixed, that is okay.
  Plugins that need a restructurization of the logic flow (e.g.,
  "flake8-cognitive-complexity") should not be included; or, if
  included, should not be activated. At least not until the next Epic.
pepoluan added a commit to pepoluan/aiosmtpd that referenced this issue Feb 26, 2021
Selection criteria:

* Must be useful, even if many warnings
  For example: Plugins related to annotations. As @warsaw raised in
  aio-libs#128, it's a good idea to type-annotate as much as possible.

* If not simple, must be maintained
  "maintained" here is overly-simplified to mean only "master has an
  update in 2020 or later".
  Some plugins have not received updates for a long time, but their
  logic are simple and really does not need updates, so those can also
  be included on a case-by-case basis.

* Will not need significant change in logic
  Adding annotations, removing temp vars used just for return values,
  those are not logic changes. So even if lots of instances need to be
  fixed, that is okay.
  Plugins that need a restructurization of the logic flow (e.g.,
  "flake8-cognitive-complexity") should not be included; or, if
  included, should not be activated. At least not until the next Epic.
pepoluan added a commit to pepoluan/aiosmtpd that referenced this issue Feb 27, 2021
Selection criteria:

* Must be useful, even if many warnings
  For example: Plugins related to annotations. As @warsaw raised in
  aio-libs#128, it's a good idea to type-annotate as much as possible.

* If not simple, must be maintained
  "maintained" here is overly-simplified to mean only "master has an
  update in 2020 or later".
  Some plugins have not received updates for a long time, but their
  logic are simple and really does not need updates, so those can also
  be included on a case-by-case basis.

* Will not need significant change in logic
  Adding annotations, removing temp vars used just for return values,
  those are not logic changes. So even if lots of instances need to be
  fixed, that is okay.
  Plugins that need a restructurization of the logic flow (e.g.,
  "flake8-cognitive-complexity") should not be included; or, if
  included, should not be activated. At least not until the next Epic.
pepoluan added a commit to pepoluan/aiosmtpd that referenced this issue Mar 2, 2021
Selection criteria:

* Must be useful, even if many warnings
  For example: Plugins related to annotations. As @warsaw raised in
  aio-libs#128, it's a good idea to type-annotate as much as possible.

* If not simple, must be maintained
  "maintained" here is overly-simplified to mean only "master has an
  update in 2020 or later".
  Some plugins have not received updates for a long time, but their
  logic are simple and really does not need updates, so those can also
  be included on a case-by-case basis.

* Will not need significant change in logic
  Adding annotations, removing temp vars used just for return values,
  those are not logic changes. So even if lots of instances need to be
  fixed, that is okay.
  Plugins that need a restructurization of the logic flow (e.g.,
  "flake8-cognitive-complexity") should not be included; or, if
  included, should not be activated. At least not until the next Epic.
@pepoluan pepoluan mentioned this issue Mar 2, 2021
11 tasks
@pepoluan
Copy link
Collaborator

pepoluan commented Mar 2, 2021

In an ongoing attempt to fully annotate this package, I've submitted PR #259

pepoluan added a commit to pepoluan/aiosmtpd that referenced this issue Mar 4, 2021
Selection criteria:

* Must be useful, even if many warnings
  For example: Plugins related to annotations. As @warsaw raised in
  aio-libs#128, it's a good idea to type-annotate as much as possible.

* If not simple, must be maintained
  "maintained" here is overly-simplified to mean only "master has an
  update in 2020 or later".
  Some plugins have not received updates for a long time, but their
  logic are simple and really does not need updates, so those can also
  be included on a case-by-case basis.

* Will not need significant change in logic
  Adding annotations, removing temp vars used just for return values,
  those are not logic changes. So even if lots of instances need to be
  fixed, that is okay.
  Plugins that need a restructurization of the logic flow (e.g.,
  "flake8-cognitive-complexity") should not be included; or, if
  included, should not be activated. At least not until the next Epic.
pepoluan added a commit to pepoluan/aiosmtpd that referenced this issue Mar 6, 2021
Selection criteria:

* Must be useful, even if many warnings
  For example: Plugins related to annotations. As @warsaw raised in
  aio-libs#128, it's a good idea to type-annotate as much as possible.

* If not simple, must be maintained
  "maintained" here is overly-simplified to mean only "master has an
  update in 2020 or later".
  Some plugins have not received updates for a long time, but their
  logic are simple and really does not need updates, so those can also
  be included on a case-by-case basis.

* Will not need significant change in logic
  Adding annotations, removing temp vars used just for return values,
  those are not logic changes. So even if lots of instances need to be
  fixed, that is okay.
  Plugins that need a restructurization of the logic flow (e.g.,
  "flake8-cognitive-complexity") should not be included; or, if
  included, should not be activated. At least not until the next Epic.
pepoluan added a commit to pepoluan/aiosmtpd that referenced this issue Mar 7, 2021
Selection criteria:

* Must be useful, even if many warnings
  For example: Plugins related to annotations. As @warsaw raised in
  aio-libs#128, it's a good idea to type-annotate as much as possible.

* If not simple, must be maintained
  "maintained" here is overly-simplified to mean only "master has an
  update in 2020 or later".
  Some plugins have not received updates for a long time, but their
  logic are simple and really does not need updates, so those can also
  be included on a case-by-case basis.

* Will not need significant change in logic
  Adding annotations, removing temp vars used just for return values,
  those are not logic changes. So even if lots of instances need to be
  fixed, that is okay.
  Plugins that need a restructurization of the logic flow (e.g.,
  "flake8-cognitive-complexity") should not be included; or, if
  included, should not be activated. At least not until the next Epic.
pepoluan added a commit to pepoluan/aiosmtpd that referenced this issue Mar 8, 2021
Selection criteria:

* Must be useful, even if many warnings
  For example: Plugins related to annotations. As @warsaw raised in
  aio-libs#128, it's a good idea to type-annotate as much as possible.

* If not simple, must be maintained
  "maintained" here is overly-simplified to mean only "master has an
  update in 2020 or later".
  Some plugins have not received updates for a long time, but their
  logic are simple and really does not need updates, so those can also
  be included on a case-by-case basis.

* Will not need significant change in logic
  Adding annotations, removing temp vars used just for return values,
  those are not logic changes. So even if lots of instances need to be
  fixed, that is okay.
  Plugins that need a restructurization of the logic flow (e.g.,
  "flake8-cognitive-complexity") should not be included; or, if
  included, should not be activated. At least not until the next Epic.
pepoluan added a commit to pepoluan/aiosmtpd that referenced this issue Mar 8, 2021
Selection criteria:

* Must be useful, even if many warnings
  For example: Plugins related to annotations. As @warsaw raised in
  aio-libs#128, it's a good idea to type-annotate as much as possible.

* If not simple, must be maintained
  "maintained" here is overly-simplified to mean only "master has an
  update in 2020 or later".
  Some plugins have not received updates for a long time, but their
  logic are simple and really does not need updates, so those can also
  be included on a case-by-case basis.

* Will not need significant change in logic
  Adding annotations, removing temp vars used just for return values,
  those are not logic changes. So even if lots of instances need to be
  fixed, that is okay.
  Plugins that need a restructurization of the logic flow (e.g.,
  "flake8-cognitive-complexity") should not be included; or, if
  included, should not be activated. At least not until the next Epic.
pepoluan added a commit to pepoluan/aiosmtpd that referenced this issue Mar 22, 2021
Selection criteria:

* Must be useful, even if many warnings
  For example: Plugins related to annotations. As @warsaw raised in
  aio-libs#128, it's a good idea to type-annotate as much as possible.

* If not simple, must be maintained
  "maintained" here is overly-simplified to mean only "master has an
  update in 2020 or later".
  Some plugins have not received updates for a long time, but their
  logic are simple and really does not need updates, so those can also
  be included on a case-by-case basis.

* Will not need significant change in logic
  Adding annotations, removing temp vars used just for return values,
  those are not logic changes. So even if lots of instances need to be
  fixed, that is okay.
  Plugins that need a restructurization of the logic flow (e.g.,
  "flake8-cognitive-complexity") should not be included; or, if
  included, should not be activated. At least not until the next Epic.
@pepoluan
Copy link
Collaborator

Huh. rebasing + force pushing apparently causes LOTS of "referenced this issue" 😆

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in progress Someone has started working on the issue but no PR yet tech-debt Things that needs to be tidied up to avoid being bitten in the future...
Projects
None yet
Development

No branches or pull requests

4 participants