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 and typing_copilot checks to the CI pipeline. #1528

Merged
merged 5 commits into from
Mar 19, 2021

Conversation

obi1kenobi
Copy link
Contributor

@obi1kenobi obi1kenobi commented Jan 29, 2021

Add the tightest-possible passing mypy.ini file, autogenerated with typing_copilot by running typing_copilot init from inside an arviz development environment with arviz installed within.

Part of #1496. Next steps (in this PR or in separate, future PRs, per the maintainers' choice) would be:

  • Add a CI check to ensure mypy continues to pass (i.e. the "no type-hint regressions" check)
  • Add a CI check that typing_copilot tighten --error-if-can-tighten continues to pass (i.e. the "type hinting ratchet is as tight as possible" check)

Technically, typing_copilot tighten --error-if-can-tighten will run mypy with the current settings first and therefore will duplicate some of the same work from the first check, but I'd recommend running mypy directly anyway just to be sure and avoid the risk of typing_copilot bugs causing type-hinting quality to decrease unnoticed.

@obi1kenobi
Copy link
Contributor Author

cc @ColCarroll

@OriolAbril
Copy link
Member

I think we can add CI checks in this same PR, but I don't really have a preference about this. Whatever works best for you.

Can this be merged before #1504?

@obi1kenobi
Copy link
Contributor Author

obi1kenobi commented Feb 22, 2021

Mypy version 0.800 changed how files for type-checking are selected, making more files eligible for type checking by default. This makes typing_copilot catch the fact that the examples directory is not type-checked and has some type errors at the moment, as well as pointing out a missing type annotation in the doc/source/conf.py file:

examples/matplotlib/mpl_plot_trace_vlines.py:15: error: Argument "var_names" to "plot_trace" has incompatible type "Tuple[str, str]"; expected "Optional[List[str]]"  [arg-type]
examples/matplotlib/mpl_plot_trace_bars.py:15: error: Argument "var_names" to "plot_trace" has incompatible type "Tuple[str, str]"; expected "Optional[List[str]]"  [arg-type]
examples/matplotlib/mpl_plot_trace.py:15: error: Argument "var_names" to "plot_trace" has incompatible type "Tuple[str, str]"; expected "Optional[List[str]]"  [arg-type]
examples/bokeh/bokeh_plot_trace_vlines.py:10: error: Argument "var_names" to "plot_trace" has incompatible type "Tuple[str, str]"; expected "Optional[List[str]]"  [arg-type]
examples/bokeh/bokeh_plot_trace_bars.py:10: error: Argument "var_names" to "plot_trace" has incompatible type "Tuple[str, str]"; expected "Optional[List[str]]"  [arg-type]
examples/bokeh/bokeh_plot_trace.py:10: error: Argument "var_names" to "plot_trace" has incompatible type "Tuple[str, str]"; expected "Optional[List[str]]"  [arg-type]
doc/source/conf.py:184: error: Need type annotation for 'latex_elements' (hint: "latex_elements: Dict[<type>, <type>] = ...")  [var-annotated]

This is frustrating user experience, and I've opened obi1kenobi/typing_copilot#10 to attempt to improve the experience from typing_copilot's side. I think a fix is possible but it's not trivial so I'll need to find some time to work on it.

I see a few possible options here, and would love to hear the thoughts of the maintainers:

My preference would be one of the first two options. I'd probably pick the first one myself, since adopting ratcheting and starting to enforce statically typed Python with mypy<0.800 is probably more beneficial than the new mypy functionality in mypy>=0.800.

@OriolAbril
Copy link
Member

Are these the only 7 errors?

I like the first option. It looks like checking the examples folder is catching some false positives, var_names can be a tuple without any problem, and we could use that as a trial run for people like me to start learning type-hints and seeing how to fix the errors in the examples folder. For example, what would one use to indicate list-like instead of necessarily a list? After a quick look at mypy docs I still have doubts between Iterable or Sequence.

@obi1kenobi
Copy link
Contributor Author

Are these the only 7 errors?

Yes, there are the only 7 I'm seeing at the moment.

I like the first option. It looks like checking the examples folder is catching some false positives, var_names can be a tuple without any problem, and we could use that as a trial run for people like me to start learning type-hints and seeing how to fix the errors in the examples folder. For example, what would one use to indicate list-like instead of necessarily a list? After a quick look at mypy docs I still have doubts between Iterable or Sequence.

Sounds good to me! I'm happy to review and help you along the way, if you'd like.

I keep finding myself going back to this link when I need to reference which abstract base class supports which methods: https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes

It shows that Sequence is simply Iterable + Sized + Container + Reversible + __getitem__. In other words, Iterable just means that one could iterate through the elements (potentially only once ever!), whereas Sequence supports random access, has a fixed size that can be read directly, etc. So based on this, I'd suggest that Sequence is the "list-like" data structure we want.

@OriolAbril OriolAbril mentioned this pull request Feb 24, 2021
@OriolAbril
Copy link
Member

I have added #1581 to keep track of this.

@MarcoGorelli
Copy link
Contributor

Regarding Sequence vs Iterable, in case it's of use to you, I find this little explanation from the mypy docs to be helpful:

# Use Iterable for generic iterables (anything usable in "for"),
# and Sequence where a sequence (supporting "len" and "__getitem__") is
# required

So unless var_names gets modified (Sequence doesn't allow that), then Sequence seems correct 👍

@OriolAbril OriolAbril mentioned this pull request Feb 26, 2021
3 tasks
@OriolAbril
Copy link
Member

Mypy errors have been fixed, everything should now work even with latest mypy version. Should make adding the check to CI easier and not require modifying it after a while to unpin mypy.

@obi1kenobi
Copy link
Contributor Author

obi1kenobi commented Mar 10, 2021

Unfortunately I ran into a regression in mypy in both 0.800 and 0.812 where it seems to ignore some of the error suppression rules in the config file. A small excerpt of the mypy error log I'm getting, slimmed down to just the errors from one particular file:

asv_benchmarks/benchmarks/benchmarks.py:16: error: Function is missing a type annotation  [no-untyped-def]
asv_benchmarks/benchmarks/benchmarks.py:19: error: Call to untyped function "enable_numba" of "Numba" in typed context  [no-untyped-call]
asv_benchmarks/benchmarks/benchmarks.py:21: error: Call to untyped function "disable_numba" of "Numba" in typed context  [no-untyped-call]
asv_benchmarks/benchmarks/benchmarks.py:23: error: Function is missing a type annotation  [no-untyped-def]
asv_benchmarks/benchmarks/benchmarks.py:31: error: Function is missing a type annotation  [no-untyped-def]
asv_benchmarks/benchmarks/benchmarks.py:34: error: Call to untyped function "enable_numba" of "Numba" in typed context  [no-untyped-call]
asv_benchmarks/benchmarks/benchmarks.py:36: error: Call to untyped function "disable_numba" of "Numba" in typed context  [no-untyped-call]
asv_benchmarks/benchmarks/benchmarks.py:38: error: Function is missing a type annotation  [no-untyped-def]
asv_benchmarks/benchmarks/benchmarks.py:39: error: Call to untyped function "stats_variance_2d" in typed context  [no-untyped-call]
asv_benchmarks/benchmarks/benchmarks.py:46: error: Function is missing a type annotation  [no-untyped-def]
asv_benchmarks/benchmarks/benchmarks.py:53: error: Function is missing a type annotation  [no-untyped-def]
asv_benchmarks/benchmarks/benchmarks.py:54: error: Call to untyped function (unknown) in typed context  [no-untyped-call]
asv_benchmarks/benchmarks/benchmarks.py:61: error: Function is missing a type annotation  [no-untyped-def]
asv_benchmarks/benchmarks/benchmarks.py:64: error: Call to untyped function "enable_numba" of "Numba" in typed context  [no-untyped-call]
asv_benchmarks/benchmarks/benchmarks.py:66: error: Call to untyped function "disable_numba" of "Numba" in typed context  [no-untyped-call]
asv_benchmarks/benchmarks/benchmarks.py:68: error: Function is missing a type annotation  [no-untyped-def]
asv_benchmarks/benchmarks/benchmarks.py:69: error: Call to untyped function "kde" in typed context  [no-untyped-call]
asv_benchmarks/benchmarks/benchmarks.py:76: error: Function is missing a type annotation  [no-untyped-def]
asv_benchmarks/benchmarks/benchmarks.py:80: error: Call to untyped function "enable_numba" of "Numba" in typed context  [no-untyped-call]
asv_benchmarks/benchmarks/benchmarks.py:82: error: Call to untyped function "disable_numba" of "Numba" in typed context  [no-untyped-call]
asv_benchmarks/benchmarks/benchmarks.py:84: error: Function is missing a type annotation  [no-untyped-def]
asv_benchmarks/benchmarks/benchmarks.py:85: error: Call to untyped function "_fast_kde_2d" in typed context  [no-untyped-call]
asv_benchmarks/benchmarks/benchmarks.py:92: error: Function is missing a type annotation  [no-untyped-def]
asv_benchmarks/benchmarks/benchmarks.py:102: error: Function is missing a type annotation  [no-untyped-def]
asv_benchmarks/benchmarks/benchmarks.py:103: error: Call to untyped function (unknown) in typed context  [no-untyped-call]
asv_benchmarks/benchmarks/benchmarks.py:105: error: Function is missing a type annotation  [no-untyped-def]
asv_benchmarks/benchmarks/benchmarks.py:106: error: Call to untyped function (unknown) in typed context  [no-untyped-call]

These are all unexpected (and cause an AssertionError in typing_copilot) because the mypy.ini file that produces those errors explicitly asks for them to be suppressed by including the following lines:

[mypy-asv_benchmarks.benchmarks.*]
disallow_untyped_calls = False
disallow_untyped_defs = False

Unfortunately this is a serious regression that I don't think typing_copilot can work around until it's fixed upstream in mypy. My current plan:

  • Open a GitHub issue about the mypy regression.
  • Get this PR working with mypy<0.800.
  • Open an issue in this repo about upgrading beyond mypy<0.800 once the mypy regression is fixed, and linking it to the mypy GitHub issue.

@codecov
Copy link

codecov bot commented Mar 10, 2021

Codecov Report

Merging #1528 (32e0d3b) into main (c4005b1) will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@           Coverage Diff           @@
##             main    #1528   +/-   ##
=======================================
  Coverage   90.86%   90.86%           
=======================================
  Files         108      108           
  Lines       11620    11620           
=======================================
  Hits        10558    10558           
  Misses       1062     1062           

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c4005b1...36a8b4f. Read the comment docs.

@obi1kenobi
Copy link
Contributor Author

I have completed the checklist for this PR, I believe it is ready to merge at your convenience. Thanks for bearing with me, and apologies for the delay in getting it to the finish line.

@obi1kenobi obi1kenobi changed the title Add a passing mypy.ini generated by typing_copilot. Add mypy and typing_copilot checks to the CI pipeline. Mar 10, 2021
Copy link
Member

@OriolAbril OriolAbril left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I'll wait a bit to give other people the chance to review too and then merge if nobody requests changes

@@ -127,6 +127,14 @@ jobs:
python -m black --check arviz examples asv_benchmarks
displayName: 'black'

- script: |
python -m mypy .
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it help with anything/is it possible to run mypy only on main arviz and example folders?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, not at the moment. typing_copilot doesn't yet have the capability to run mypy with custom flags, so even if you made this command different, the typing_copilot step would still essentially run mypy . and still fail.

Hoping to fix this in a future version of typing_copilot since this is obviously going to be painful for many use cases.

@OriolAbril OriolAbril merged commit ef4f13c into arviz-devs:main Mar 19, 2021
@OriolAbril
Copy link
Member

It looks like I waited to long and now CI is not passing anymore. Here is the link: https://dev.azure.com/ArviZ/ArviZ/_build/results?buildId=4169&view=logs&j=af9a1d3a-ff8f-5483-6823-86518c773a9b&t=d1f9f1df-bd17-5f75-5a7b-8e272c593e8b&l=20. Any ideas @obi1kenobi? I can see all the modules mypy is complaining about have ignore imports in mypy.ini 😕

@obi1kenobi obi1kenobi deleted the add_mypy_ini branch March 19, 2021 19:40
@obi1kenobi
Copy link
Contributor Author

It looks like I waited to long and now CI is not passing anymore. Here is the link: https://dev.azure.com/ArviZ/ArviZ/_build/results?buildId=4169&view=logs&j=af9a1d3a-ff8f-5483-6823-86518c773a9b&t=d1f9f1df-bd17-5f75-5a7b-8e272c593e8b&l=20. Any ideas @obi1kenobi? I can see all the modules mypy is complaining about have ignore imports in mypy.ini 😕

The missing type hint warnings aren't blocking. The failure is actually because of good news — type coverage has improved since the point in time when I ran typing_copilot to make this PR. The executed command was typing_copilot tighten --error-if-can-tighten and it obliged and returned Error: The current mypy.ini does not contain the tightest available configuration:

To resolve, one can do one of two things:

  • run typing_copilot tighten in an initialized arviz dev environment with all the dependencies installed, or
  • simply copy-paste the config printed after that error message into mypy.ini, because that's the new-tightest config.

I'm happy to open a PR to fix it, but this is a great opportunity to get some practice using typing_copilot so I'd recommend giving it a shot if you have the time. I'm happy to help if you run into any issues!

@OriolAbril
Copy link
Member

Oh, cool! I'll give it a go now 😄

@OriolAbril
Copy link
Member

Updated at #1622

utkarsh-maheshwari pushed a commit to utkarsh-maheshwari/arviz that referenced this pull request May 27, 2021
* Add a passing mypy.ini generated by typing_copilot.

* Add a working mypy.ini file with mypy<0.800.

* Add CI pipeline steps for mypy and typing_copilot checks.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants