-
-
Notifications
You must be signed in to change notification settings - Fork 631
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improve support of MyPy requirements when Python 2 used (#10853)
### Problem The wheels used by third-party code might not use the same constraints as what the MyPy Pex uses. This was causing errors (#10819), which we fixed in #10820. However, that fix was not perfect. Even if there were valid typed Python 2 wheels, we would ignore them. WIth Python 3, we had to be careful that `mypy.pex` used the exact same constraints as `requirements.pex`, so that, for example, we don't run MyPy with Py36 but resolve requirements with Py37. ### Solution Stop using `--pex-path` to join the `requirements.pex`, which has the effect of the wheels being put on `PYTHONPATH`. (We were then teaching MyPy to read from this PYTHONPATH through a custom launcher script.) Instead, we extract the wheel folders from `requirements.pex`, and teach our custom launcher script to look in these extracted `.deps/` folders through a new `EXTRACTED_WHEELS` env var. MyPy will use this to resolve the requirements, without us actually including the wheels in the final PEX or modifying PYTHONPATH. ### Result Our Python 2 test now fully works, even though one of the wheels is not compatible with Python 3. We no longer need to align `mypy.pex` with `requirements.pex`. #### Caveat: MyPy plugins must be installed with `--mypy-extra-requirements` An initial implementation did not use `EXTRACTED_WHEELS` and instead loaded the wheels via `PYTHONPATH`. This allowed for you to specify MyPy plugins via normal dependencies on `python_requirement_library` targets, because that requirement would get loaded via PYTHONPATH. Now, you cannot do this; you must specify the requirement via `--mypy-extra-requirements`. This caveat seems acceptable. For Pytest, it's convenient that we allow you to load plugins via normal requirements, as it allows you to only sometimes use the plugin. MyPy is much more uniform, however; MyPy runs in a single batch over the entire transitive closure, unlike Pytest running one test file at-a-time. Likewise, without Pants, it's hard to imagine how you could only sometimes use a MyPy plugin, given that MyPy only works with a single config file.
- Loading branch information
1 parent
209e9aa
commit ee98ef6
Showing
6 changed files
with
218 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,5 +6,5 @@ python_library() | |
python_integration_tests( | ||
name='integration', | ||
uses_pants_run=False, | ||
timeout=300, | ||
timeout=360, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# Copyright 2020 Pants project contributors (see CONTRIBUTORS.md). | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). | ||
|
||
from dataclasses import dataclass | ||
from typing import Tuple | ||
|
||
from pants.backend.python.util_rules.pex import Pex | ||
from pants.core.util_rules.archive import UnzipBinary | ||
from pants.engine.fs import Digest, Snapshot | ||
from pants.engine.process import FallibleProcessResult, Process | ||
from pants.engine.rules import Get, collect_rules, rule | ||
from pants.util.logging import LogLevel | ||
|
||
|
||
@dataclass(frozen=True) | ||
class ExtractedPexDistributions: | ||
digest: Digest | ||
wheel_directory_paths: Tuple[str, ...] | ||
|
||
|
||
@rule | ||
async def extract_distributions(pex: Pex, unzip_binary: UnzipBinary) -> ExtractedPexDistributions: | ||
# We only unzip the `.deps` folder to avoid unnecessary work. Note that this will cause the | ||
# process to fail if there is no `.deps` folder, so we need to use `FallibleProcessResult`. | ||
argv = (*unzip_binary.extract_argv(archive_path=pex.name, output_dir="."), ".deps/*") | ||
unzipped_pex = await Get( | ||
FallibleProcessResult, | ||
Process( | ||
argv=argv, | ||
input_digest=pex.digest, | ||
output_directories=(".deps",), | ||
description=f"Unzip {pex.name} to determine its distributions", | ||
level=LogLevel.DEBUG, | ||
), | ||
) | ||
snapshot = await Get(Snapshot, Digest, unzipped_pex.output_digest) | ||
directory_paths = tuple(sorted(d for d in snapshot.dirs if d.endswith(".whl"))) | ||
return ExtractedPexDistributions(snapshot.digest, directory_paths) | ||
|
||
|
||
def rules(): | ||
return collect_rules() |
Oops, something went wrong.