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 check_untyped_defs to mypy config #6795

Merged
merged 10 commits into from
Nov 19, 2024
Merged

Conversation

user27182
Copy link
Contributor

Overview

Part of #6279.

See: https://mypy.readthedocs.io/en/stable/config_file.html#confval-check_untyped_defs

This is a very powerful option that will ensure that all new code moving forward will be type checked, even if the new code is not annotated. Enabling this option requires ignoring all type errors present in pyvista.

Details

I added these ignores using https://github.com/geo7/mypy_clean_slate.
I had to modify it locally to allow it to use our config directly (otherwise it wants to use --strict).

See the generated report of all 1094 errors ignored by this PR:

mypy_error_report.txt

@user27182 user27182 requested a review from a team as a code owner November 14, 2024 02:58
@user27182
Copy link
Contributor Author

This PR might be a good candidate for .git-blame-ignore-revs

@tkoyama010 tkoyama010 requested a review from adeak November 14, 2024 03:10
@pyvista-bot pyvista-bot added the maintenance Low-impact maintenance activity label Nov 14, 2024
Copy link

codecov bot commented Nov 14, 2024

Codecov Report

Attention: Patch coverage is 96.20253% with 33 lines in your changes missing coverage. Please review.

Project coverage is 97.49%. Comparing base (60be630) to head (57a1db1).
Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #6795   +/-   ##
=======================================
  Coverage   97.49%   97.49%           
=======================================
  Files         143      143           
  Lines       28178    28178           
=======================================
  Hits        27473    27473           
  Misses        705      705           
---- 🚨 Try these New Features:

Copy link
Member

@adeak adeak left a comment

Choose a reason for hiding this comment

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

Thanks for the heads-up @tkoyama010.

@user27182 will this affect development going forward? Will it e.g. tie our hands to force type hints in all new code?

pyproject.toml Outdated
@@ -210,6 +210,7 @@ ignore_missing_imports = true
disallow_any_generics = true
pretty = true
show_error_context = true
check_untyped_defs = true
Copy link
Member

Choose a reason for hiding this comment

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

If we want to add this to ignore-revs then the commit must not contain any manual changes. So this PR must only contain the type ignores, and a follow-up PR should add this config and the ignore-revs item containing the commit hash of this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah ok. Thanks. It seems this is a repeat of #6664 (comment): the squash-merge model is not suited for this.

Our CI will fail if we remove this manual change, so it's not possible to separate the manual change from the automatic changes in this case. So, we won't add this to the blame ignore. Thanks for clarifying this (again).

Copy link
Member

Choose a reason for hiding this comment

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

Yup. But why will CI fail? I thought adding ignores would be safe.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah ok maybe it's good to remove this manual line after all and add it separately.

Mypy complains with unused ignores. I was somehow thinking it would complain about the new ignores without the rule. But I think you're right it should be OK to remove it here.

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 it is as I thought: the new ignores are considered unused without the rule and CI is failing if we remove it: https://github.com/pyvista/pyvista/actions/runs/11839504031/job/32991100716#step:5:3818

Copy link
Member

Choose a reason for hiding this comment

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

Bummer. Thanks for trying.

@pyvista-bot
Copy link
Contributor

pyvista-bot commented Nov 14, 2024

@pyvista-bot pyvista-bot temporarily deployed to pull request November 14, 2024 07:08 Inactive
@user27182
Copy link
Contributor Author

Thanks for the heads-up @tkoyama010.

@user27182 will this affect development going forward?

Yes, all code will be type-checked after this PR (previously, only code that used annotations explicitly was checked).

Will it e.g. tie our hands to force type hints in all new code?

No, type hints will not be required. Type hints will always be optional. Mypy is usually very good at inferring type on its own without any explicit annotations. But, if you push unannotated code that is not type-safe, mypy will now complain, whereas previously it would not have. Even then, you aren't forced to make any actual changes to code, but in this case you will now need to type: ignore the errors (just like the errors ignored by this PR).

So, this PR forces all code to be type-checked. It does not force code to be annotated. It's up to devs if they want to resolve any errors or if they'd rather just ignore them.

@adeak
Copy link
Member

adeak commented Nov 14, 2024

So, this PR forces all code to be type-checked. It does not force code to be annotated. It's up to devs if they want to resolve any errors or if they'd rather just ignore them.

Arguably type: ignore or Any is just a lazy work-around. So then it would indeed be a strong push to type hint things.

@user27182
Copy link
Contributor Author

Arguably type: ignore or Any is just a lazy work-around. So then it would indeed be a strong push to type hint things.

I disagree with this. Once again, this PR has little to do with type hints, it's about type checks.

Type hints are not the solution to type errors. If I do x = 1 the type is inferred as int. If I later do x = 1.0 this is a type error because a float is assigned instead of an int. This could easily be a bug, especially when you replace 1 and 1.0 with function calls (which may return int or float or something else altogether).

So the solution in this case is to ignore it (because I wrote unit tests for this explicitly and know what I'm doing), or fix the cause of the type error by modifying the code (e.g. use a different variable, call a different function, whatever).

So, in my view the statement should read more like:

Arguably type: ignore or Any is just a lazy work-around. So then it would indeed be a strong push to type hint things reduce bugs through type-checking.

@user27182 user27182 changed the title Add check_untyped_defs to mypy config Add type: ignores in preparation for adding check_untyped_defs to mypy config Nov 14, 2024
@user27182 user27182 changed the title Add type: ignores in preparation for adding check_untyped_defs to mypy config Add check_untyped_defs to mypy config Nov 14, 2024
@user27182
Copy link
Contributor Author

Will it e.g. tie our hands to force type hints in all new code?

For reference, the option that does force using type hints is disallow_untyped_defs. This would be a separate PR, and is the option which I believe would get more push-back. I don't plan on proposing adding this option though.

@pyvista-bot pyvista-bot temporarily deployed to pull request November 14, 2024 17:09 Inactive
@MatthewFlamm
Copy link
Contributor

I am softly in favor of adding this PR given that we are already typing in many places. It increases typing burden by increasing type checking coverage, not adding in extra typing restrictions. Another possible path to get to this flag turned on is to try to clean up as many problems up front (getting to 100% seems unlikely), but I think they will be hard to find.

A reason I am in favor of this PR because we could open an issue as 'good first issue' to remove typing ignore lines. While some typing problems will be quite difficult to resolve, there are some in a quick read through that are relatively straightforward to resolve if someone is familiar with typing and not familiar with pyvista, e.g. initializing a variable as None and then reassigning it to another type later.

@user27182
Copy link
Contributor Author

user27182 commented Nov 15, 2024

A reason I am in favor of this PR because we could open an issue as 'good first issue' to remove typing ignore lines. While some typing problems will be quite difficult to resolve, there are some in a quick read through that are relatively straightforward to resolve if someone is familiar with typing and not familiar with pyvista, e.g. initializing a variable as None and then reassigning it to another type later.

To add to this, I find that it is generally overwhelming at the moment to try to resolve typing issues. Part of the reason for this is that, if you even add a single annotation to a function that currently has zero annotations, this implicitly "turns on" type-checking for that function, and then suddenly all of the existing type errors related to that function are reported. So, even just trying to add a return type to a function becomes a rabbit hole of resolving previously unreported type errors. This is very off-putting and can prevent devs from even trying to fix type errors.

But, if we ignore these errors up-front and turn on type-checking for all code (this PR), this makes it a lot easier to do small, iterative fixes later. There are also tools like autotyping which we can leverage to automate a lot of "obvious" cases that are implicitly known by type-checkers. But, we can't currently use these tools for the reason above, since adding these automated annotations will cause mypy to also complain about any number of the existing largely unrelated 1000+ type errors.

@user27182
Copy link
Contributor Author

As an example, if I want to automatically add a None return type for functions in core, I can do python -m autotyping pyvista/core --none-return. If I do this on the main branch and run mypy, I get 52 errors to resolve. But if I do that from this branch, I only get 11 errors, which is much more manageable to resolve quickly. Plus, the 11 errors are now known to be directly related to None return types. It was then relatively easy for me to create #6802 and resolve the errors.

@user27182 user27182 requested a review from akaszynski November 16, 2024 18:16
@akaszynski
Copy link
Member

This PR might be a good candidate for .git-blame-ignore-revs

Absolutely.


Part of the reason for this is that, if you even add a single annotation to a function that currently has zero annotations, this implicitly "turns on" type-checking for that function, and then suddenly all of the existing type errors related to that function are reported.

Yep, encountered this in one of my private codebases as well. Only solution was a massive and overwhelming PR to do it in one go, ignore all the pyvista issues, and then merge it hoping that I wouldn't regret it later. This PR proposes a much better approach because we can incrementally add typechecking rather than being forced to do it all at once while enabling mypy.

Will it e.g. tie our hands to force type hints in all new code?

In my opinion, we should enforce type hints. It's the best (as far as I can tell) programmatic way without unit tests to enforce code safety. I've uncovered a mountain of issues that could have only been resolved through unreasonable unit tests or simply resolving user errors. Typehinting is the way to go, especially since our library is used downstream by so many applications.

@akaszynski
Copy link
Member

Also:

we could open an issue as 'good first issue' to remove typing ignore lines

100% agree with this. Request users take a look at all type: ignore and remove them, adding types along the way to be in-compliance with mypy.

@pyvista-bot pyvista-bot temporarily deployed to pull request November 16, 2024 19:04 Inactive
Copy link
Member

@akaszynski akaszynski left a comment

Choose a reason for hiding this comment

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

It's my impression that while we pay a bit in readability, we open the door for incrementally adding typehints in a maintainable (and enforceable) manner.

LGTM, but let's wait a day for reviews from others @banesullivan will weight in here. I will say, however, I'm looking forward to a fully typehinted pyvista library as my dependencies are littered with type: ignore

Copy link
Member

@banesullivan banesullivan left a comment

Choose a reason for hiding this comment

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

LGTM, I've also run into issues in downstream projects due to typing issues with pyvista

Thanks for making this happen @user27182!

@tkoyama010 tkoyama010 enabled auto-merge (squash) November 18, 2024 22:36
@user27182
Copy link
Contributor Author

Thanks for everyone's feedback and support getting this approved!

@tkoyama010 I'm thinking it's possible that some of the other None annotation PRs which are already queued to merge may cause a conflict with this PR (and require re-approval). Is it better to let this PR merge first so that conflicts may be resolved through the other PRs instead of through this one?

@tkoyama010
Copy link
Member

Thanks for everyone's feedback and support getting this approved!

@tkoyama010 I'm thinking it's possible that some of the other None annotation PRs which are already queued to merge may cause a conflict with this PR (and require re-approval). Is it better to let this PR merge first so that conflicts may be resolved through the other PRs instead of through this one?

Thank you for pointing out. We will merge this first.

@tkoyama010 tkoyama010 merged commit ce67ca8 into main Nov 19, 2024
33 checks passed
@tkoyama010 tkoyama010 deleted the maint/check-untyped-defs branch November 19, 2024 04:19
@pyvista-bot pyvista-bot temporarily deployed to pull request November 19, 2024 04:21 Inactive
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
maintenance Low-impact maintenance activity
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants