Ensure we always respect --site-packages (#3836)

Ensure we always respect --site-packages
This commit is contained in:
Dan Ryan
2019-07-14 19:28:57 -04:00
committed by GitHub
5 changed files with 73 additions and 17 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``.
+13 -7
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
)
@@ -70,7 +70,7 @@ def cli(
man=False,
support=None,
help=False,
site_packages=False,
site_packages=None,
**kwargs
):
# Handle this ASAP to make shell startup fast.
@@ -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,12 @@ def lock(
):
"""Generates Pipfile.lock."""
from ..core import ensure_project, do_init, do_lock
# Ensure that virtualenv is available.
# Note that we don't pass clear on to ensure_project as it is also
# handled in do_lock
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,
)
if state.installstate.requirementstxt:
do_init(
@@ -475,8 +478,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 +510,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,
+9 -2
View File
@@ -61,7 +61,7 @@ class State(object):
self.python = None
self.two = None
self.three = None
self.site_packages = False
self.site_packages = None
self.clear = False
self.system = False
self.installstate = InstallState()
@@ -261,9 +261,10 @@ def quiet_option(f):
def site_packages_option(f):
def callback(ctx, param, value):
state = ctx.ensure_object(State)
validate_bool_or_none(ctx, param, value)
state.site_packages = value
return value
return option("--site-packages", is_flag=True, default=False, type=click.types.BOOL,
return option("--site-packages/--no-site-packages", is_flag=True, default=None,
help="Enable site-packages for the virtualenv.", callback=callback,
expose_value=False, show_envvar=True)(f)
@@ -356,6 +357,12 @@ def validate_python_path(ctx, param, value):
return value
def validate_bool_or_none(ctx, param, value):
if value is not None:
return click.types.BOOL(value)
return False
def validate_pypi_mirror(ctx, param, value):
if value and not is_valid_url(value):
raise BadParameter("Invalid PyPI mirror URL: %s" % value)
+6 -4
View File
@@ -466,7 +466,7 @@ def ensure_python(three=None, python=None):
return path_to_python
def ensure_virtualenv(three=None, python=None, site_packages=False, pypi_mirror=None):
def ensure_virtualenv(three=None, python=None, site_packages=None, pypi_mirror=None):
"""Creates a virtualenv, if one doesn't exist."""
from .environments import PIPENV_USE_SYSTEM
@@ -500,7 +500,7 @@ def ensure_virtualenv(three=None, python=None, site_packages=False, pypi_mirror=
cleanup_virtualenv(bare=False)
sys.exit(1)
# If --three, --two, or --python were passed…
elif (python) or (three is not None) or (site_packages is not False):
elif (python) or (three is not None) or (site_packages is not None):
USING_DEFAULT_PYTHON = False
# Ensure python is installed before deleting existing virtual env
python = ensure_python(three=three, python=python)
@@ -536,7 +536,7 @@ def ensure_project(
validate=True,
system=False,
warn=True,
site_packages=False,
site_packages=None,
deploy=False,
skip_requirements=False,
pypi_mirror=None,
@@ -891,7 +891,7 @@ def convert_three_to_python(three, python):
return python
def do_create_virtualenv(python=None, site_packages=False, pypi_mirror=None):
def do_create_virtualenv(python=None, site_packages=None, pypi_mirror=None):
"""Creates a virtualenv."""
click.echo(
@@ -1861,6 +1861,7 @@ def do_install(
deploy=False,
keep_outdated=False,
selective_upgrade=False,
site_packages=None,
):
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):