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

Fix MyPy to work by default with Python 3.6+ code and Black with 3.8+ code #10750

Merged
merged 4 commits into from Sep 9, 2020

Conversation

Eric-Arellano
Copy link
Contributor

@Eric-Arellano Eric-Arellano commented Sep 9, 2020

Problem

MyPy and Black both use typed-ast for parsing, just like we use it for dependency inference. Typed-ast understands Py27 and 34-37, but not Py38. For Py38, you instead must run with a Python 3.8 interpreter (which also understands 27 and 34-37).

This meant that our default Black and MyPy installations would break if encountering Py38 syntax like the walrus operator. You had to explicitly set --black-interpreter-constrains and --mypy-interpreter-constraints, which is not at all obvious.

Solution

Detect if the user has constraints for 3.8+. If so, use the constraints >=3.8 for the tools. Otherwise, use the tool's default constraints.

We check that the constraints do not include anything less than 3.8, such as 3.7. Why? If they need to support 3.7, then a user won't be using 3.8 syntax. We don't want to constraint to >=3.8 because this would mean that they must have 3.8 on their machine, even though 3.7 would do just fine.

To facilitate this, we change PexInterpreterConstraints to store parsed Requirement objects.

Also fixes MyPy handing of Python 3.6 and Python 3.7

If the user leaves off python_version, MyPy defaults to the version of the interpreter used to run. MyPy also tells the AST parser to target this specific python_version. So, if you're running with our minimum requirement of Python 3.5, it will fail to parse any Python 3.6+ code like f-strings, unless you specifically set python_version in mypy.ini.

We apply the same fix as above so that MyPy now also works "out of the box" with Python 3.6-only and 3.7-only codebases. If users set python_version in mypy.ini, MyPy will respect this - we only influence the default behavior.

Result

Toolchain's Python 3.8 codebase now works "out of the box" with Pants, i.e. without extra settings.

[ci skip-rust]
[ci skip-build-wheels]

@Eric-Arellano
Copy link
Contributor Author

Gr. Our CI fails because I removed our internal setting, and now MyPy runs with Python 3.5. This complains that it doesn't understand f strings, even though typed-ast should allow this to work! Looks like I need more debugging with MyPy...

@Eric-Arellano
Copy link
Contributor Author

Gr. Our CI fails because I removed our internal setting, and now MyPy runs with Python 3.5. This complains that it doesn't understand f strings, even though typed-ast should allow this to work! Looks like I need more debugging with MyPy...

I figured it out. MyPy tells typed-ast what Python version to use based on the python_version option in mypy.ini:

https://github.com/python/mypy/blob/d7553fe181f91b6bbf100ca16db83f67690e669a/mypy/fastparse.py#L163

If unspecified, I think this defaults to the interpreter being used to run. I can fix this by adding python_version: 3.6 to our mypy.ini. Note that this fix is different than the Py38 fix, which requires running with Py38 because only a Py38 interpreter has access to the Py38 AST.

--

We could tell users to set python_version in their mypy.ini, but we can also fix this automatically using very similar logic to our Py38 handling. We'll work our way upwards; first check if Py36+ is used, then Py37+, then Py38+. Use the default of >=3.5 if none of those are true, else use the most specific we safely can, like >=3.8, then >=3.7, etc.

They can still set python_version. This will still change which AST MyPy uses. This is only about getting MyPy to work "out of the box".

Also fix both Black and MyPy to respect the --interpreter-constraints option if it was configured.

# Rust tests and lints will be skipped. Delete if not intended.
[ci skip-rust]

# Building wheels and fs_util will be skipped. Delete if not intended.
[ci skip-build-wheels]
@Eric-Arellano Eric-Arellano changed the title WIP: Fix Black and MyPy to work by default with Python 3.8+ code Fix MyPy to work by default with Python 3.6+ code and Black with 3.8+ code Sep 9, 2020
Comment on lines +192 to +200
import datetime

x = True
if y := x:
print("x is truthy and now assigned to y")


class Foo:
pass
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was a real pain to get MyPy to error. If I just had the walrus operator portion, MyPy was fine..I don't know why we need the import and class declaration. I spent so much time trying to figure out how to get MyPy to error that I went with it once I got it working.

Comment on lines 95 to 98
# * We must resolve third-party dependencies. This should use whatever the actual code's
# constraints are, as the constraints for the tool can be different than for the
# requirements.
# * The runner Pex should use the same constraints as the tool Pex.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This part is tomorrow's project..

Comment on lines -84 to -86
# https://mypy.readthedocs.io/en/stable/config_file.html#platform-configuration. We do
# not auto-configure this for simplicity and to avoid Pants magically setting values for
# users.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Lol to the simpler days where I thought this would work.

Comment on lines -408 to +407
py2 = is_python2(
python_setup.compatibilities_or_constraints(
target_with_origin.target.get(PythonInterpreterCompatibility).value
interpreter_constraints = PexInterpreterConstraints.create_from_compatibility_fields(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This refactor was convenient because we added similar new methods to PexInterpreterConstraints for Black and MyPy. Now they're located together.

# Rust tests and lints will be skipped. Delete if not intended.
[ci skip-rust]

# Building wheels and fs_util will be skipped. Delete if not intended.
[ci skip-build-wheels]
# Rust tests and lints will be skipped. Delete if not intended.
[ci skip-rust]

# Building wheels and fs_util will be skipped. Delete if not intended.
[ci skip-build-wheels]
@coveralls
Copy link

Coverage Status

Coverage remained the same at 0.0% when pulling 2c24ecb on Eric-Arellano:mypy-version into 7f76dbc on pantsbuild:master.

Copy link
Sponsor Contributor

@benjyw benjyw left a comment

Choose a reason for hiding this comment

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

Neat!!!! Some handy trickery there. Great stuff!

@Eric-Arellano Eric-Arellano merged commit 54b9a79 into pantsbuild:master Sep 9, 2020
@Eric-Arellano Eric-Arellano deleted the mypy-version branch September 9, 2020 17:58
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.

None yet

4 participants