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

feat: Add mypy type checking #4863

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open

Conversation

zachaysan
Copy link
Contributor

Changes

This adds mypy type checking. Existing failures have been grandfathered in and a helper script is run to compare failures against the predefined list. Mypy was also added into the pre-commit config and will run on any commits to python code.

How did you test this code?

I altered the predefined mypy list and saw a failure crop up when running pre-commit.

@zachaysan zachaysan requested a review from a team as a code owner November 25, 2024 16:55
@zachaysan zachaysan requested review from matthewelwell and removed request for a team November 25, 2024 16:55
Copy link

vercel bot commented Nov 25, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

3 Skipped Deployments
Name Status Preview Comments Updated (UTC)
docs ⬜️ Ignored (Inspect) Visit Preview Dec 9, 2024 8:05pm
flagsmith-frontend-preview ⬜️ Ignored (Inspect) Visit Preview Dec 9, 2024 8:05pm
flagsmith-frontend-staging ⬜️ Ignored (Inspect) Visit Preview Dec 9, 2024 8:05pm

@github-actions github-actions bot added the api Issue related to the REST API label Nov 25, 2024
Copy link
Contributor

github-actions bot commented Nov 25, 2024

Docker builds report

Image Build Status Security report
ghcr.io/flagsmith/flagsmith-api-test:pr-4863 Finished ✅ Skipped
ghcr.io/flagsmith/flagsmith-e2e:pr-4863 Finished ✅ Skipped
ghcr.io/flagsmith/flagsmith-frontend:pr-4863 Finished ✅ Results
ghcr.io/flagsmith/flagsmith-api:pr-4863 Finished ✅ Results
ghcr.io/flagsmith/flagsmith:pr-4863 Finished ✅ Results
ghcr.io/flagsmith/flagsmith-private-cloud:pr-4863 Finished ✅ Results

@github-actions github-actions bot added the feature New feature or request label Nov 25, 2024
@github-actions github-actions bot added feature New feature or request and removed feature New feature or request labels Nov 25, 2024
Copy link
Contributor

github-actions bot commented Nov 25, 2024

Uffizzi Ephemeral Environment deployment-58551

☁️ https://app.uffizzi.com/github.com/Flagsmith/flagsmith/pull/4863

📄 View Application Logs etc.

What is Uffizzi? Learn more!

Copy link

codecov bot commented Nov 25, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 97.38%. Comparing base (05ab7cf) to head (c9b44d4).
Report is 14 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #4863   +/-   ##
=======================================
  Coverage   97.38%   97.38%           
=======================================
  Files        1189     1189           
  Lines       41420    41453   +33     
=======================================
+ Hits        40338    40371   +33     
  Misses       1082     1082           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

@matthewelwell matthewelwell left a comment

Choose a reason for hiding this comment

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

This seems like a decent enough solution to me but I have some general questions:

  1. What happens if we (perhaps inadvertently) resolve an existing issue. Based on the logic in your script, I guess we wouldn't know about it, right? Because we're just seeing what's left after we subtract the original errors from the new ones which wouldn't tell us anything about any that we've solved. It would obviously be great to incentivise resolving existing issues where we can.

  2. This approach works fine I guess, but another solution would have been to just add all of the distinct files into the exclude. Then every new file would need to conform to mypy, but changes to existing files wouldn't. I'm not sure how we could incentivise fixing the existing errors. (Note: I'm certainly not suggesting this is a better option, I'm just playing devil's advocate).

hooks:
- id: mypy-check
name: mypy check
entry: bash -c "cd api && poetry run python scripts/mypy_check.py"
Copy link
Contributor

Choose a reason for hiding this comment

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

Since I was looking at similar functionality yesterday (see here) , I am somewhat intrigued by this.

My understanding of pre-commit is that it passes the modified files into the command specified in entry, so the resulting command would look something like:

bash -c "cd api && poetry run python scripts/mypy_check.py" file1.py file2.py 

... which I would expect to just not work at all, or at least throw some sort of error?

Copy link
Contributor

Choose a reason for hiding this comment

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

@zachaysan I'm not sure the latest changes make any reference to this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've altered the script to run the files one at a time. I had to use git diff to list the target files of the commit because no matter what I tried to pass into the bash script it just wouldn't run with the automatic listed files. The good news is that we are now doing a mypy check on only the committed files.

api/mypy.ini Outdated Show resolved Hide resolved
@matthewelwell matthewelwell linked an issue Dec 2, 2024 that may be closed by this pull request
@github-actions github-actions bot added feature New feature or request and removed feature New feature or request labels Dec 4, 2024
@github-actions github-actions bot added feature New feature or request and removed feature New feature or request labels Dec 4, 2024
@zachaysan
Copy link
Contributor Author

This seems like a decent enough solution to me but I have some general questions:

  1. What happens if we (perhaps inadvertently) resolve an existing issue. Based on the logic in your script, I guess we wouldn't know about it, right? Because we're just seeing what's left after we subtract the original errors from the new ones which wouldn't tell us anything about any that we've solved. It would obviously be great to incentivise resolving existing issues where we can.

I think we should treat the baseline as something we can devote engineering time towards. Delete a chunk of the baseline then re-run mypy see the failures that you eliminated from the baseline then handle them. Over time, I think we can get to a point where the baseline file is deleted and we have full coverage.

  1. This approach works fine I guess, but another solution would have been to just add all of the distinct files into the exclude. Then every new file would need to conform to mypy, but changes to existing files wouldn't. I'm not sure how we could incentivise fixing the existing errors. (Note: I'm certainly not suggesting this is a better option, I'm just playing devil's advocate).

I think the approach I've taken is actually better for coverage because new errors in existing files will be surfaced. As for incentivizing fixing the existing errors, I really think we should just have engineering time to delete things from the baseline then fix them up locally.

Copy link
Contributor

@matthewelwell matthewelwell left a comment

Choose a reason for hiding this comment

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

Approving with a minor comment, but as discussed in the engineering meeting, I'd like to get everyone's eyes on this.

api/scripts/mypy_check.py Outdated Show resolved Hide resolved
@github-actions github-actions bot added feature New feature or request and removed feature New feature or request labels Dec 5, 2024
@khvn26
Copy link
Member

khvn26 commented Dec 6, 2024

pre-commit.ci run

@@ -99,6 +99,16 @@ skip = ['migrations', '.venv', '.direnv']
addopts = ['--ds=app.settings.test', '-vvvv', '-p', 'no:warnings']
console_output_style = 'count'

[tool.mypy]
exclude = "^(tests/)$"
Copy link
Member

Choose a reason for hiding this comment

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

Is there a reason to exclude the test code?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Mypy throws an error when we try to include the tests module in the coverage. We tried hard to figure out how to solve this bug but it was outside of our grasp.


# Detect new errors and remove information line filtering out third party packages
current_errors = {line for line in current_output if "site-packages" not in line}
new_errors = current_errors - baseline
Copy link
Member

Choose a reason for hiding this comment

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

I think we should check for baseline - current_errors as well to make sure the baseline file contains up to date errors. Rarely, but things may change implicitly (e.g. with a dependency update).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great idea. I've implemented this with a helpful error message.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just realized that this doesn't actually work since mypy only checks errors for the current working module. I've reverted my change.

baseline = set(line.strip() for line in f if line.strip())

# Detect new errors and remove information line filtering out third party packages
current_errors = {line for line in current_output if "site-packages" not in line}
Copy link
Member

Choose a reason for hiding this comment

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

I appreciate it's going to be slightly more difficult to compare against, but why not just have third party errors in the baseline as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since the line numbers change between installs it becomes difficult to handle it that way. Plus, site-packages is going to be only ever present in the installation path for non-local modules.

hooks:
- id: mypy-check
name: mypy check
entry: bash -c "cd api && poetry run python scripts/mypy_check.py $(git diff --name-only --cached -- '*.py')"
Copy link
Member

Choose a reason for hiding this comment

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

As seen from pre-commit.ci run here, this hook won't work when poetry isn't present globally.

Maybe try adding

        additional_dependencies: ['poetry']

or using repo: local (see docs here).

If all else fails, moving the hook to its own repo could be an option.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The additional_dependencies seems to have worked once I changed the language to python.

@matthewelwell
Copy link
Contributor

pre-commit.ci run

@zachaysan I think you'll need to rebase onto the main branch to get pre-commit to pass after I merged this PR.

@github-actions github-actions bot added feature New feature or request and removed feature New feature or request labels Dec 9, 2024
@github-actions github-actions bot added feature New feature or request and removed feature New feature or request labels Dec 9, 2024
@zachaysan zachaysan requested a review from khvn26 December 9, 2024 20:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api Issue related to the REST API feature New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Enforcing typing
4 participants