From 02fc52b8da7dc76d429504864704d59ea29426fb Mon Sep 17 00:00:00 2001 From: Dan Ryan Date: Mon, 23 Jul 2018 01:06:55 -0400 Subject: [PATCH] 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 --- pipenv/core.py | 15 +-------------- pipenv/project.py | 14 ++++++++++++++ pipenv/utils.py | 22 ++++++++++++++++++++++ 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/pipenv/core.py b/pipenv/core.py index ca79536c..a648e776 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -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: diff --git a/pipenv/project.py b/pipenv/project.py index 79fa1b91..b12f7c64 100644 --- a/pipenv/project.py +++ b/pipenv/project.py @@ -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 diff --git a/pipenv/utils.py b/pipenv/utils.py index 98169515..a721eca4 100644 --- a/pipenv/utils.py +++ b/pipenv/utils.py @@ -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)