Skip to content

Commit

Permalink
Fix pex force local to handle PEP-420.
Browse files Browse the repository at this point in the history
Previously, pex would blow up trying to adjust import paths if a PEP-420
implicit namespace package was encountered. Add a test of the path
adjustment functionality both to confirm it was needed (it was) and that
the fix technique works with all forms of namespace packages by only
assuming they support `append`.

Fixes #598
  • Loading branch information
jsirois committed Nov 26, 2018
1 parent 8fd5e1b commit 11f1886
Show file tree
Hide file tree
Showing 7 changed files with 437 additions and 113 deletions.
40 changes: 20 additions & 20 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,101 +92,101 @@ matrix:
- <<: *x-linux-shard
name: TOXENV=style
env: *x-py27
script: ./scripts/ci.sh style
script: tox -ve style

- <<: *x-linux-shard
name: TOXENV=isort-check
env: *x-py27
script: ./scripts/ci.sh isort-check
script: tox -ve isort-check

- <<: *x-linux-shard
name: TOXENV=py27
env: *x-py27
script: ./scripts/ci.sh py27
script: tox -ve py27

- <<: *x-linux-shard
name: TOXENV=py27-subprocess
env: *x-py27
script: ./scripts/ci.sh py27-subprocess
script: tox -ve py27-subprocess

- <<: *x-linux-shard
name: TOXENV=py27-requests
env: *x-py27
script: ./scripts/ci.sh py27-requests
script: tox -ve py27-requests

- <<: *x-linux-shard
name: TOXENV=py27-requests-cachecontrol
env: *x-py27
script: ./scripts/ci.sh py27-requests-cachecontrol
script: tox -ve py27-requests-cachecontrol

- <<: *x-linux-shard
name: TOXENV=py34
env:
- *env
- PYENV_VERSION=3.4.9
script: ./scripts/ci.sh py34
script: tox -ve py34

- <<: *x-linux-shard
name: TOXENV=py35
env:
- *env
- PYENV_VERSION=3.5.6
script: ./scripts/ci.sh py35
script: tox -ve py35

- <<: *x-linux-shard
name: TOXENV=py36
env:
- *env
- PYENV_VERSION=3.6.6
script: ./scripts/ci.sh py36
script: tox -ve py36

- <<: *x-linux-37-shard
name: TOXENV=py37
script: ./scripts/ci.sh py37
script: tox -ve py37

- <<: *x-linux-37-shard
name: TOXENV=py37-requests
script: ./scripts/ci.sh py37-requests
script: tox -ve py37-requests

- <<: *x-linux-37-shard
name: TOXENV=py37-requests-cachecontrol
script: ./scripts/ci.sh py37-requests-cachecontrol
script: tox -ve py37-requests-cachecontrol

- <<: *x-linux-shard
name: TOXENV=pypy
env: *x-pypy
script: ./scripts/ci.sh pypy
script: tox -ve pypy

- <<: *x-linux-shard
name: TOXENV=py27-integration
env: *x-py27
script: ./scripts/ci.sh py27-integration
script: tox -ve py27-integration

- <<: *x-linux-37-shard
name: TOXENV=py37-integration
script: ./scripts/ci.sh py37-integration
script: tox -ve py37-integration

- <<: *x-linux-shard
name: TOXENV=pypy-integration
env: *x-pypy
script: ./scripts/ci.sh pypy-integration
script: tox -ve pypy-integration

- <<: *x-osx-shard
name: TOXENV=py27-requests
env: *x-py27
script: ./scripts/ci.sh py27-requests
script: tox -ve py27-requests

- <<: *x-osx-shard
name: TOXENV=py37-requests
env: *x-py37
script: ./scripts/ci.sh py37-requests
script: tox -ve py37-requests

- <<: *x-osx-shard
name: TOXENV=py27-integration
env: *x-py27
script: ./scripts/ci.sh py27-integration
script: tox -ve py27-integration

- <<: *x-osx-shard
name: TOXENV=py37-integration
env: *x-py37
script: ./scripts/ci.sh py37-integration
script: tox -ve py37-integration
69 changes: 69 additions & 0 deletions pex/bootstrap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# coding=utf-8
# Copyright 2018 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

import os


class Bootstrap(object):
"""Supports introspection of the PEX bootstrap code."""

_INSTANCE = None

@classmethod
def locate(cls):
"""Locates the active PEX bootstrap.
:rtype: :class:`Bootstrap`
"""
if cls._INSTANCE is None:
bootstrap_path = __file__
module_import_path = __name__.split('.')

# For example, our __file__ might be requests.pex/.bootstrap/pex/bootstrap.pyc and our import
# path pex.bootstrap; so we walk back through all the module components of our import path to
# find the base sys.path entry where we were found (requests.pex/.bootstrap in this example).
for _ in module_import_path:
bootstrap_path = os.path.dirname(bootstrap_path)

cls._INSTANCE = cls(path_entry=bootstrap_path)
return cls._INSTANCE

def __init__(self, path_entry):
self._path_entry = path_entry

def demote(self, log):
"""Demote the bootstrap code to the end of the `sys.path` so it is found last."""
import sys # Grab a hold of `sys` early since we'll be unimporting our module in this process.

log('Demoting bootstrap code %s' % self._path_entry, V=2)
for name, module in reversed(sorted(sys.modules.items())):
if self.imported_from_bootstrap(module):
sys.modules.pop(name)
log('un-imported {}'.format(name), V=9)

sys.path.remove(self._path_entry)
sys.path.append(self._path_entry)

def imported_from_bootstrap(self, module):
"""Return ``True`` if the given ``module_name`` was imported from bootstrap code.
:rtype: bool
"""
# Otherwise we may have an exposed vendored module whose name is not in the root set, but whose
# import comes from our path entry. EG: `pkg_resources` imported from
# `.bootstrap/pex/vendor/_vendored/setuptools/pkg_resources/__init__.py`

# A vendored module.
path = getattr(module, '__file__', None)
if path and path.startswith(self._path_entry):
return True

# A vendored package.
path = getattr(module, '__path__', None)
if path:
for path in path:
if path.startswith(self._path_entry):
return True

return False
Loading

0 comments on commit 11f1886

Please sign in to comment.