Ensure we always respect --site-packages

- Sometimes we fail to respect `--site-packages` when it is passed with
`--install`
- This resolves that and ensures we always pass it to `ensure_project`
- Fixes #3718

Signed-off-by: Dan Ryan <dan@danryan.co>
This commit is contained in:
Dan Ryan
2019-07-11 01:37:36 -04:00
parent d618cad3d1
commit 829e41976f
4 changed files with 57 additions and 10 deletions
+1
View File
@@ -0,0 +1 @@
Fixed a bug which sometimes caused pipenv to fail to respect the ``--site-packages`` flag when passed with ``pipenv install``.
+10 -6
View File
@@ -18,7 +18,7 @@ from .options import (
general_options, install_options, lock_options, pass_state,
pypi_mirror_option, python_option, requirementstxt_option,
skip_lock_option, sync_options, system_option, three_option,
uninstall_options, verbose_option
uninstall_options, verbose_option, site_packages_option
)
@@ -217,6 +217,7 @@ def cli(
@system_option
@code_option
@deploy_option
@site_packages_option
@skip_lock_option
@install_options
@pass_state
@@ -249,6 +250,7 @@ def install(
extra_index_url=state.extra_index_urls,
packages=state.installstate.packages,
editable_packages=state.installstate.editables,
site_packages=state.site_packages
)
if retcode:
ctx.abort()
@@ -310,11 +312,10 @@ def lock(
):
"""Generates Pipfile.lock."""
from ..core import ensure_project, do_init, do_lock
# Ensure that virtualenv is available.
ensure_project(
three=state.three, python=state.python, pypi_mirror=state.pypi_mirror,
warn=(not state.quiet)
warn=(not state.quiet), site_packages=state.site_packages, clear=state.clear
)
if state.installstate.requirementstxt:
do_init(
@@ -475,8 +476,10 @@ def update(
do_sync,
project,
)
ensure_project(three=state.three, python=state.python, warn=True, pypi_mirror=state.pypi_mirror)
ensure_project(
three=state.three, python=state.python, pypi_mirror=state.pypi_mirror,
warn=(not state.quiet), site_packages=state.site_packages, clear=state.clear
)
if not outdated:
outdated = bool(dry_run)
if outdated:
@@ -505,12 +508,13 @@ def update(
err=True,
)
ctx.abort()
do_lock(
ctx=ctx,
clear=state.clear,
pre=state.installstate.pre,
keep_outdated=state.installstate.keep_outdated,
pypi_mirror=state.pypi_mirror,
write=not state.quiet,
)
do_sync(
ctx=ctx,
+2
View File
@@ -1861,6 +1861,7 @@ def do_install(
deploy=False,
keep_outdated=False,
selective_upgrade=False,
site_packages=False,
):
from .environments import PIPENV_VIRTUALENV, PIPENV_USE_SYSTEM
from .vendor.pip_shims.shims import PipError
@@ -1888,6 +1889,7 @@ def do_install(
deploy=deploy,
skip_requirements=skip_requirements,
pypi_mirror=pypi_mirror,
site_packages=site_packages,
)
# Don't attempt to install develop and default packages if Pipfile is missing
if not project.pipfile_exists and not (package_args or dev) and not code:
+44 -4
View File
@@ -195,7 +195,7 @@ def make_base_requirements(reqs):
requirements.add(req)
elif isinstance(req, pkg_resources_requirements.Requirement):
requirements.add(BaseRequirement.from_req(req))
elif req and not req.startswith("#"):
elif req and isinstance(req, six.string_types) and not req.startswith("#"):
requirements.add(BaseRequirement.from_string(req))
return requirements
@@ -240,10 +240,15 @@ def get_package_dir_from_setupcfg(parser, base_dir=None):
if "package_dir" in setup_py:
package_lookup = setup_py["package_dir"]
if not isinstance(package_lookup, Mapping):
return package_lookup
return package_lookup.get(
package_dir = package_lookup
package_dir = package_lookup.get(
next(iter(list(package_lookup.keys()))), package_dir
)
if not os.path.isabs(package_dir):
if not base_dir:
package_dir = os.path.join(os.path.getcwd(), package_dir)
else:
package_dir = os.path.join(base_dir, package_dir)
return package_dir
@@ -296,7 +301,8 @@ def parse_setup_cfg(setup_cfg_path):
parser = configparser.ConfigParser(default_opts)
parser.read(setup_cfg_path)
results = {}
package_dir = get_package_dir_from_setupcfg(parser, base_dir=os.getcwd())
base_dir = os.path.dirname(os.path.abspath(setup_cfg_path))
package_dir = get_package_dir_from_setupcfg(parser, base_dir=base_dir)
name, version = get_name_and_version_from_setupcfg(parser, package_dir)
results["name"] = name
results["version"] = version
@@ -638,6 +644,8 @@ class Analyzer(ast.NodeVisitor):
self.functions = []
self.strings = []
self.assignments = {}
self.binOps = []
self.binOps_map = {}
super(Analyzer, self).__init__()
def generic_visit(self, node):
@@ -652,6 +660,17 @@ class Analyzer(ast.NodeVisitor):
self.assignments.update(ast_unparse(node, initial_mapping=True))
super(Analyzer, self).generic_visit(node)
def visit_BinOp(self, node):
left = ast_unparse(node.left, initial_mapping=True)
right = ast_unparse(node.right, initial_mapping=True)
node.left = left
node.right = right
self.binOps.append(node)
def unmap_binops(self):
for binop in self.binOps:
self.binOps_map[binop] = ast_unparse(binop, analyzer=self)
def match_assignment_str(self, match):
return next(
iter(k for k in self.assignments if getattr(k, "id", "") == match), None
@@ -678,6 +697,26 @@ def ast_unparse(item, initial_mapping=False, analyzer=None, recurse=True): # no
unparsed = item.s
elif isinstance(item, ast.Subscript):
unparsed = unparse(item.value)
elif isinstance(item, ast.BinOp):
if analyzer and item in analyzer.binOps_map:
unparsed = analyzer.binOps_map[item]
elif isinstance(item.op, ast.Add):
if not initial_mapping:
right_item = unparse(item.right)
left_item = unparse(item.left)
if not all(
isinstance(side, (six.string_types, int, float, list, tuple))
for side in (left_item, right_item)
):
item.left = left_item
item.right = right_item
unparsed = item
else:
unparsed = right_item + left_item
else:
unparsed = item
elif isinstance(item.op, ast.Sub):
unparsed = unparse(item.left) - unparse(item.right)
elif isinstance(item, ast.Name):
if not initial_mapping:
unparsed = item.id
@@ -787,6 +826,7 @@ def ast_parse_setup_py(path):
# type: (S) -> Dict[Any, Any]
ast_analyzer = ast_parse_file(path)
setup = {} # type: Dict[Any, Any]
ast_analyzer.unmap_binops()
for k, v in ast_analyzer.function_map.items():
fn_name = ""
if isinstance(k, ast.Name):