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

Implement long-term solution for cross-interpreter requirement handling the in pex resolver #456

Closed
CMLivingston opened this issue Apr 5, 2018 · 5 comments · Fixed by #582
Assignees

Comments

@CMLivingston
Copy link
Contributor

CMLivingston commented Apr 5, 2018

When a setup.py file specifies install_requires requirements with interpreter-specific constraints, the pex resolver cannot parse and handle these constraints to produce a cross-interpreter pex. See https://github.com/Julian/jsonschema/blob/master/setup.py#L43 for an example of such a constraint.

Interpreter constraints like this are typically for stdlib backport libraries and the like that only make sense to include in a universal dist, so this problem is not as significant when the pex is targeting a specific interpreter version. In the targeted case, the resolver only needs to ignore requirements that are pinned to non-targeted interpreter versions. The short term workaround for the targeted-interpreter case is to allow the caller of pex.resolver#resolve to supply a blacklist dict that maps package name string -> interpreter constraint string, allowing the resolver to effectively skip them as implemented in #457.

The scope of this ticket is to remove the blacklist implementation and replace it with smarter handling for the "pex-targeting-X-interpreter-version" case, specifically where all requirements are compared against interpreter constraints for a pex. Then at run time, the pex environment selectively activates packaged dependencies in accordance with a given pex's interpreter constraints. If no constraints are specified and the pex is intended to run with Python 2 or 3, then there should be support for determining this at runtime and handling env activation accordingly as well.

This approach would solve the cross-interpreter case. This is a similar problem to the one surfaced in #455, however that issue deals with cross-platform specifications, but this is effectively the same concept.

@kmannislands
Copy link

kmannislands commented Apr 12, 2018

Is there any workaround for this currently available?
I tried something like:

$ pex jupyter --python=python2.7 --python=python3.4 -o jup_pex.pex

Which results in:

**** Failed to install functools32-3.2.3-2 (caused by: NonZeroExit("received exit code 1 during execution of `['/Users/kieran/.pyenv/versions/3.4.8/bin/python3.4', '-', 'bdist_wheel', '--dist-dir=/var/folders/7d/cnv90sjj7rx4nhq38nvx44nr0000gn/T/tmpZEG5fd']` while trying to execute `['/Users/kieran/.pyenv/versions/3.4.8/bin/python3.4', '-', 'bdist_wheel', '--dist-dir=/var/folders/7d/cnv90sjj7rx4nhq38nvx44nr0000gn/T/tmpZEG5fd']`",)
):
stdout:

stderr:
This backport is for Python 2.7 only.


Traceback (most recent call last):
  File "/Users/kieran/.pyenv/versions/2.7.13/bin/pex", line 11, in <module>
    sys.exit(main())
  File "/Users/kieran/.pyenv/versions/2.7.13/lib/python2.7/site-packages/pex/bin/pex.py", line 663, in main
    pex_builder = build_pex(reqs, options, resolver_options_builder)
  File "/Users/kieran/.pyenv/versions/2.7.13/lib/python2.7/site-packages/pex/bin/pex.py", line 601, in build_pex
    for dist in resolveds:
  File "/Users/kieran/.pyenv/versions/2.7.13/lib/python2.7/site-packages/pex/resolver.py", line 438, in resolve_multi
    allow_prereleases):
  File "/Users/kieran/.pyenv/versions/2.7.13/lib/python2.7/site-packages/pex/resolver.py", line 376, in resolve
    return resolver.resolve(resolvables_from_iterable(requirements, builder))
  File "/Users/kieran/.pyenv/versions/2.7.13/lib/python2.7/site-packages/pex/resolver.py", line 209, in resolve
    dist = self.build(package, resolvable.options)
  File "/Users/kieran/.pyenv/versions/2.7.13/lib/python2.7/site-packages/pex/resolver.py", line 282, in build
    dist = super(CachingResolver, self).build(package, options)
  File "/Users/kieran/.pyenv/versions/2.7.13/lib/python2.7/site-packages/pex/resolver.py", line 177, in build
    raise Untranslateable('Package %s is not translateable by %s' % (package, translator))
pex.resolver.Untranslateable: Package SourcePackage('file:///Users/kieran/.pex/build/functools32-3.2.3-2.tar.gz') is not translateable by ChainedTranslator(WheelTranslator, EggTranslator, SourceTranslator)

Due to, I believe, the specific line in jsonschema's setup py (a nested subdependency of jupyter) that you referenced above.

Tried flattering my dependencies and trying to specify a environment marker directly in requirements.txt in an attempt to help pex's resolver out, so something like:

$ echo "functools32;python_version<'3.0'\njupyter" >> requirements.txt
$ pex -r requirements.txt --python=python2.7 --python=python3.4 -o jup_pex.pex

Which lead to the same error. So it would seem that pex fails to parse environment markers in requirements.txt (PEP508) as well as in install_requires of sub-packages.

Anyone have a different work around idea?

kwlzn pushed a commit that referenced this issue Apr 12, 2018
Problem
When resolving for a specific interpreter version, the pex resolver fails to resolve universal requirements that transitively require libraries with interpreter constraints that conflict with the target interpreter version (see: https://github.com/Julian/jsonschema/blob/master/setup.py#L43 ).

Solution
The short term solution is to allow the caller of pex.resolver#resolve to supply a blacklist dict that maps package name -> interpreter constraint, enabling the resolver to skip blacklisted requirement names at resolve time if the target interpreter conforms with the interpreter constraint. The long-term solution is to be addressed by #456.

Result
It is now possible for systems that build pex files (Pants) via the pex api to blacklist certain requirements that are redundant (backports) or otherwise should not be included in the output pex.
@CMLivingston
Copy link
Contributor Author

I was unable to reproduce the error with that command, but it appears to be the same issue that I am describing here. There is no workaround that I am aware of, so unfortunately you will need to either build multiple pex files or target only one interpreter for the time being.

@kmannislands
Copy link

Hm, wish I could.
My use case is bundling python dependencies for a spark apps on a cluster where (clients, don't ask me why) some of the data nodes have varying versions of python installed.

My plan was to create a multi-interpreter pex file, ship it, and then specify PYSPARK_PYTHON=/path/to/multi_interpreter.pex, which would then act as the node's py interpreter, picking up on the actual python installed on the node but with the correct libraries depended on by a given app. Specifying different paths for nodes on a version dependent basis can be a bit messy.

Simple tests of this technique with SciPy worked, so I figured if it could bundle all that c and fortran, other dependencies would be fine. Whoops 😅

I'm still very interested in pursuing this approach. What does the timeline look like on this work?

I'll give the source a closer read and see if I can be of assistance as well.

@CMLivingston
Copy link
Contributor Author

CMLivingston commented Apr 16, 2018

I see. In terms of a timeline on my availability to knock this out, I would say it would be ~1 month to a merged fix. The long-term solution boils down to providing the pex resolver module with the intelligence to parse environmental markers correctly and propagate its decisions to include/exclude a requirement through to the PEX-INFO metadata. That second part is to ensure that there are no requirements that are registered but actually excluded due to an incompatible marker.

jsirois added a commit to jsirois/pex that referenced this issue Oct 8, 2018
We've had support for environment markers on the resolve side for a
while and with just a little plumbing we can now support multi-python
pexes with environment-specific requirements.

Fixes pex-tool#456
@jsirois jsirois self-assigned this Oct 8, 2018
jsirois added a commit that referenced this issue Oct 8, 2018
We've had support for environment markers on the resolve side for a
while and with just a little plumbing we can now support multi-python
pexes with environment-specific requirements.

Fixes #456
This was referenced Oct 8, 2018
Laevos pushed a commit to Laevos/pex that referenced this issue Oct 9, 2018
# The first commit's message is:
pex-tool#572: Allow import of ctypes to be skipped if use_manylinux is false

# This is the 2nd commit message:

Narrow the env marker test. (pex-tool#578)

The jupyter dist is just a meta-dist with fully unconstrained deps on ~6
other dists. This test was added to test environment marker support in
pex, which ipython - not jupyter - leverages heavily.

# This is the 3rd commit message:

Fix resolve regressions introduced by the 1.4.8. (pex-tool#580)

PR pex-tool#571 regressed the half-broken state of having
`--interpreter_constraint` selected interpreters not setup to also
having `--python` selected interpreters also not setup. In addition,
PR pex-tool#568 incorrectly classified the current Platform passed by
`resolve_multi` as a user-specified extended platform specification
breaking custom interpreter resolution. Fix both and add tests that
failed prior to this combination of fixes.

A more comprehensive fix is tracked in part by pex-tool#579.

# This is the 4th commit message:

Cleanup `PexInfo` and `PythonInterpreter`. (pex-tool#581)

Kill an unused type in `PexInfo` as well as our last remaining use of
`pkg_resources.get_platform`. Also kill unused `COMPATIBLE_SETUPTOOLS`
constants in `PythonInterpreter`.

# This is the 5th commit message:

Support environment markers during pex activation. (pex-tool#582)

We've had support for environment markers on the resolve side for a
while and with just a little plumbing we can now support multi-python
pexes with environment-specific requirements.

Fixes pex-tool#456

# This is the 6th commit message:

Revert "Support environment markers during pex activation. (pex-tool#582)"

This reverts commit 5f1f00f.

We want to do a 1.4.9 bugfix release before this ~API change.

# This is the 7th commit message:

Prepare the 1.4.9 release. (pex-tool#588)

Work towards pex-tool#583

# This is the 8th commit message:

Revert "Revert "Support environment markers during pex activation. (pex-tool#582)""

This reverts commit 44ff463.

This restores pex-tool#582 for the 1.5.0 release tracked by pex-tool#585.
@jsirois jsirois reopened this Oct 9, 2018
@jsirois
Copy link
Member

jsirois commented Oct 9, 2018

As discovered in pantsbuild/pants#6613 there is a bit more work needed here to filter out top-level requirements passed to pex. The work in #582 only hit transitive requirements.

jsirois added a commit to jsirois/pex that referenced this issue Oct 9, 2018
Previously, only transitive requirements were filtered.

Additionally, the monkey-patching in `patched_packing_env` is removed in
favor of directly evaluating markers.

Fixes pex-tool#456
jsirois added a commit to jsirois/pex that referenced this issue Oct 10, 2018
Previously, only transitive requirements were filtered.

Fixes pex-tool#456
jsirois added a commit that referenced this issue Oct 10, 2018
Previously, only transitive requirements were filtered.

Fixes #456
Fixes #122
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 a pull request may close this issue.

3 participants