Leverage pip to access installed packages

- Use `get_installed_distributions` from pip
- This cascades to `pkg_resources.working_set` which relies on
  `sys.path` to uncover packages
- Consequently I implemented `temp_path()` as a contextmanager in the
  style of `temp_environ()`
- This requires us to know the environment's `sys.path`, so
  `load_path(python)` will do a `json.dumps` of `sys.path` to stdout
  which then gets loaded and returned
- Allows us to avoid trying to hack around `pip freeze` output to parse
  out names from comments etc
- Provides other potential uses

Signed-off-by: Dan Ryan <dan@danryan.co>
This commit is contained in:
Dan Ryan
2018-07-23 01:06:55 -04:00
parent 134927c28d
commit 02fc52b8da
3 changed files with 37 additions and 14 deletions
+1 -14
View File
@@ -2543,23 +2543,10 @@ def do_clean(
pypi_mirror=None,
):
# Ensure that virtualenv is available.
from .vendor.requirementslib import Requirement
ensure_project(three=three, python=python, validate=False, pypi_mirror=pypi_mirror)
ensure_lockfile(pypi_mirror=pypi_mirror)
installed_package_names = []
pip_freeze_command = delegator.run("{0} freeze".format(which_pip()))
for line in pip_freeze_command.out.split("\n"):
installed = line.strip()
if not installed or installed.startswith("#"): # Comment or empty.
continue
r = Requirement.from_line(installed).requirement
# Ignore editable installations.
if not r.editable:
installed_package_names.append(r.name.lower())
else:
if verbose:
click.echo("Ignoring {0}.".format(repr(r.name)), err=True)
installed_package_names = [pkg.project_name for pkg in project.get_installed_packages()]
# Remove known "bad packages" from the list.
for bad_package in BAD_PACKAGES:
if bad_package in installed_package_names:
+14
View File
@@ -269,6 +269,20 @@ class Project(object):
return os.path.join(self.project_directory, ".venv")
return str(get_workon_home().joinpath(self.virtualenv_name))
def get_installed_packages(self):
from . import PIPENV_ROOT, PIPENV_VENDOR, PIPENV_PATCHED
from .utils import temp_path, load_path, temp_environ
if self.virtualenv_exists:
with temp_path(), temp_environ():
new_path = load_path(self.which("python"))
new_path = [new_path[0], PIPENV_ROOT, PIPENV_PATCHED, PIPENV_VENDOR] + new_path[1:]
sys.path = new_path
os.environ['VIRTUAL_ENV'] = self.virtualenv_location
from .patched.notpip._internal.utils.misc import get_installed_distributions
return get_installed_distributions(local_only=True)
else:
return []
@classmethod
def _sanitize(cls, name):
# Replace dangerous characters into '_'. The length of the sanitized
+22
View File
@@ -934,6 +934,28 @@ def temp_environ():
os.environ.update(environ)
@contextmanager
def temp_path():
"""Allow the ability to set os.environ temporarily"""
path = [p for p in sys.path]
try:
yield
finally:
sys.path = [p for p in path]
def load_path(python):
import delegator
import json
python = escape_grouped_arguments(python)
json_dump_commmand = "'import json, sys; print(json.dumps(sys.path));'"
c = delegator.run("{0} -c {1}".format(python, json_dump_commmand))
if c.return_code == 0:
return json.loads(c.out.strip())
else:
return []
def is_valid_url(url):
"""Checks if a given string is an url"""
pieces = urlparse(url)