-
-
Notifications
You must be signed in to change notification settings - Fork 646
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
mypy contrib plugin #5172
mypy contrib plugin #5172
Conversation
This PR will likely fail because Where should we discuss optionally making Python 3.x available from within the Pants venv? I can open a separate issue for that. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
awesome! this lgtm w/ a few comments.
a basic integration test would also be good.
'src/python/pants/goal:task_registrar', | ||
], | ||
distribution_name='pantsbuild.pants.contrib.mypy', | ||
description='RPM package builder for pants', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wrong description
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
build-support/pants_venv
Outdated
@@ -21,6 +21,9 @@ function activate_venv() { | |||
|
|||
function create_venv() { | |||
rm -rf "$(venv_dir)" | |||
if which python3; then | |||
python3 -m venv "$(venv_dir)" | |||
fi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
intended? probably best to rely on interpreter/compatibility constraints in pants vs constructing the venv with python3 here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought I had to get python3 into the venv. Plugin works with the python3 I have on my PATH
.
|
||
|
||
class MypyTask(PythonTask): | ||
"""Invoke the mypy static type analzyer for Python.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
analyzer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
return True | ||
|
||
def __init__(self, *args, **kwargs): | ||
super(MypyTask, self).__init__(*args, **kwargs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably better to just remove this __init__
method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
pex_info = PexInfo.default() | ||
pex_info.entry_point = 'mypy' | ||
chroot = self.cached_chroot(interpreter=py3_interpreter, pex_info=pex_info, targets=[], | ||
extra_requirements=[PythonRequirement('mypy==0.550')]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this probably deserves either an option to override the version (e.g. --mypy-version
w default='0.550'
) or a tool address (e.g. --mypy-tool-address
w default='3rdparty/python:mypy'
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@tdyas would file an issue for the python3 venv discussion and we can all chime in there. |
a4f2cd4
to
233cb63
Compare
I'll need to make some adjustments to the plugin. I have to expose the Python environment expected by the source code to mypy via the |
if directory: | ||
roots.add(directory) | ||
|
||
import pdb ; pdb.set_trace() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Debugging leftover. Pushed too early. Made better progress locally. Will push when the integration test is working.
) | ||
return list(sources) | ||
|
||
def _calculate_python_package_roots(self, targets): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't {target.target_base for target in targets}
give you what you want?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I switched to that after getting that from John on Slack general.
233cb63
to
290c181
Compare
Ok I pushed my current version. I added an integration test but it can't really do anything because the CI environment may not provide Python 3.x? How should I go about getting Py3 into the CI environment to ensure a proper integration test? |
CI does provide 3.x (https://docs.travis-ci.com/user/reference/trusty/#Python-images), but a guard is good sanity since Pants developers may not have this. Inspiration for the guard here: https://github.com/jsirois/pants/blob/master/tests/python/pants_test/python/test_interpreter_selection_integration.py#L24 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Just one typo.
self.context.log.debug('No Python sources to check.') | ||
return | ||
|
||
# Determine interprter used by the sources so we can tell mypy. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/interprter/interpreter/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
I modified the integration test to call
|
290c181
to
f58e75a
Compare
return True | ||
|
||
def find_py3_interpreter(self): | ||
interpreters = self._interpreter_cache.setup(filters=['Python>=3']) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/Python/CPython/
or else s/Python//
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
@tdyas fwiw, @CMLivingston recently added this utility to pex for materializing version-specific interpreters on demand in tests: pex-tool/pex@df326ff#diff-b619e563eafa44775c3411bd4c79e252R283 and its likely that we'll want to land something similar in pants soon as part of other python3 work - but it might be worth pow-wowing with him on Slack to see if it would make sense to break the landing of that utility into a separate earlier PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm!
# Construct the MYPYPATH from both the source roots and targets' sys.path. | ||
mypy_path = ':'.join([os.path.join(get_buildroot(), root) for root in source_roots]) | ||
mypy_path += ':' | ||
mypy_path += pythonpath_for_targets |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe:
mypy_path = ':'.join((mypy_path, pythonpath_for_targets)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also could combine the two lines into mypy_path += ':' + pythonpath_for_targets
. Using .join
here seems too fancy for plain old string concatenation, unless the style here is to avoid mutability?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No strong opinion on join, but it's easy enough to use os.pathsep
instead of a raw :
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea. Will do that. (os.pathsep
)
f5cee4b
to
6f36f34
Compare
This plugin runs the `mypy` static type analyzer.
6f36f34
to
620b35e
Compare
Anything else remaining for me to do with this PR? |
return | ||
|
||
# Determine interpreter used by the sources so we can tell mypy. | ||
interpreter_for_targets = self._interpreter_cache.select_interpreter_for_targets(self.context.target_roots) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tdyas : I'm deprecating PythonTask, as it's part of the old pipeline (should have noticed you were relying on it in this change, my bad). So quick question - why are you selecting the interpreter only for self.context.target_roots rather than all targets (later you collect source roots for all targets, which I assume means that mypy operates on all of them)? Does this matter, or can I have this select an interpreter for all targets (and then use the already-computed product for that)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I chose to use target_roots
since I only wanted mypy
to operate on the targets passed on the pants
command-line. I'm not wedded to that idea though. What is expected of a Pants code linting task?
As for switching to another Python pipeline, just point me at what I should be using.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm still figuring out how this would fit into the new pipeline, since it has some unusual usage patterns (e.g., having to select two interpreters, one for running mypy and one for discovering the python version of the input files).
And another question: don't the external requirements need to be on the MYPYPATH for mypy to work properly? E.g., if the code being checked relies on some code from 3rdparty, doesn't mypy need to know that? And while I have your attention, why do the input source roots need to be on MYPYPATH? The documentation states that "The directories containing the sources given on the command line" are searched for imports anyway, so it seems superfluous to also add them to MYPYPATH? |
Hmm, https://mypy.readthedocs.io/en/latest/basics.html says
So what are you supposed to do about 3rdparty code? Either create typeshed stubs, or give up on that information? |
I'm not actually sure what the "right" answer should be for third party code. In an earlier version of the task, I had the task compute the locations within the pex for third party modules and then added those paths to the I'm still trying to understand the recommended way to use |
This was true in a prior version of the task. See comment above for more info. I basically found it too verbose for issues that the user couldn't fix, so removed the code that put the third party code on the |
So that |
FYI I just found PEP 561 which proposes how to distribute type information with modules. |
This plugin runs the
mypy
static type analyzer.