Skip to content

Commit

Permalink
Merge pull request #3007 from pypa/fix-normalization-during-install
Browse files Browse the repository at this point in the history
Fix normalization during install
  • Loading branch information
techalchemy authored Oct 12, 2018
2 parents a88cda0 + cfd8bf4 commit aac05b8
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 28 deletions.
1 change: 1 addition & 0 deletions news/2998.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed a bug which prevented installing pinned versions which used redirection symbols from the command line.
1 change: 1 addition & 0 deletions news/3007.vendor
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Upgraded ``pythonfinder => 1.1.1`` and ``vistir => 0.1.7``.
2 changes: 2 additions & 0 deletions pipenv/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
rmtree,
clean_resolved_dep,
parse_indexes,
escape_cmd
)
from . import environments, pep508checker, progress
from .environments import (
Expand Down Expand Up @@ -1398,6 +1399,7 @@ def pip_install(
pip_command.append("--upgrade-strategy=only-if-needed")
if no_deps:
pip_command.append("--no-deps")
install_reqs = [escape_cmd(req) for req in install_reqs]
pip_command.extend(install_reqs)
pip_command.extend(prepare_pip_source_args(sources))
if not ignore_hashes:
Expand Down
6 changes: 6 additions & 0 deletions pipenv/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1048,6 +1048,12 @@ def handle_remove_readonly(func, path, exc):
raise


def escape_cmd(cmd):
if any(special_char in cmd for special_char in ["<", ">", "&", ".", "^", "|", "?"]):
cmd = '\"{0}\"'.format(cmd)
return cmd


@contextmanager
def atomic_open_for_write(target, binary=False, newline=None, encoding=None):
"""Atomically open `target` for writing.
Expand Down
2 changes: 1 addition & 1 deletion pipenv/vendor/pythonfinder/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import print_function, absolute_import

__version__ = '1.1.0'
__version__ = '1.1.1'

__all__ = ["Finder", "WindowsFinder", "SystemPath", "InvalidPythonVersion"]
from .pythonfinder import Finder
Expand Down
10 changes: 9 additions & 1 deletion pipenv/vendor/pythonfinder/models/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class PythonVersion(object):
is_prerelease = attr.ib(default=False)
is_postrelease = attr.ib(default=False)
is_devrelease = attr.ib(default=False)
is_debug = attr.ib(default=False)
version = attr.ib(default=None, validator=optional_instance_of(Version))
architecture = attr.ib(default=None)
comes_from = attr.ib(default=None)
Expand All @@ -46,6 +47,8 @@ def version_sort(self):
release_sort = 1
elif self.is_devrelease:
release_sort = 0
elif self.is_debug:
release_sort = 1
return (self.major, self.minor, self.patch if self.patch else 0, release_sort)

@property
Expand Down Expand Up @@ -102,6 +105,10 @@ def parse(cls, version):
:rtype: dict.
"""

is_debug = False
if version.endswith("-debug"):
is_debug = True
version, _, _ = verson.rpartition("-")
try:
version = parse_version(str(version))
except TypeError:
Expand All @@ -125,6 +132,7 @@ def parse(cls, version):
"is_prerelease": version.is_prerelease,
"is_postrelease": version.is_postrelease,
"is_devrelease": version.is_devrelease,
"is_debug": is_debug,
"version": version,
}

Expand Down Expand Up @@ -206,7 +214,7 @@ class VersionMap(object):
def add_entry(self, entry):
version = entry.as_python
if version:
entries = versions[version.version_tuple]
entries = self.versions[version.version_tuple]
paths = {p.path for p in self.versions.get(version.version_tuple, [])}
if entry.path not in paths:
self.versions[version.version_tuple].append(entry)
Expand Down
4 changes: 2 additions & 2 deletions pipenv/vendor/vendor.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pipdeptree==0.13.0
pipreqs==0.4.9
docopt==0.6.2
yarg==0.1.9
pythonfinder==1.1.0
pythonfinder==1.1.1
requests==2.19.1
chardet==3.0.4
idna==2.7
Expand All @@ -41,7 +41,7 @@ semver==2.8.1
shutilwhich==1.1.0
toml==0.10.0
cached-property==1.4.3
vistir==0.1.6
vistir==0.1.7
pip-shims==0.3.1
ptyprocess==0.6.0
enum34==1.1.6
Expand Down
2 changes: 1 addition & 1 deletion pipenv/vendor/vistir/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from .path import mkdir_p, rmtree


__version__ = '0.1.6'
__version__ = '0.1.7'


__all__ = [
Expand Down
105 changes: 82 additions & 23 deletions pipenv/vendor/vistir/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,15 @@ def dedup(iterable):
return iter(OrderedDict.fromkeys(iterable))


def _spawn_subprocess(script, env={}, block=True, cwd=None):
def _spawn_subprocess(script, env={}, block=True, cwd=None, combine_stderr=True):
from distutils.spawn import find_executable

command = find_executable(script.command)
options = {
"env": env,
"universal_newlines": True,
"stdout": subprocess.PIPE,
"stderr": subprocess.PIPE if block else subprocess.STDOUT,
"stderr": subprocess.PIPE if not combine_stderr else subprocess.STDOUT,
"shell": False,
}
if not block:
Expand Down Expand Up @@ -117,58 +117,90 @@ def _create_subprocess(
cwd=os.curdir,
verbose=False,
spinner=None,
combine_stderr=False,
display_limit=200
):
try:
c = _spawn_subprocess(cmd, env=env, block=block, cwd=cwd)
c = _spawn_subprocess(cmd, env=env, block=block, cwd=cwd,
combine_stderr=combine_stderr)
except Exception as exc:
print("Error %s while executing command %s", exc, " ".join(cmd._parts))
raise
if not block:
c.stdin.close()
output = []
err = []
spinner_orig_text = ""
if spinner:
spinner_orig_text = spinner.text
if c.stdout is not None:
while True:
line = to_text(c.stdout.readline())
streams = {
"stdout": c.stdout,
"stderr": c.stderr
}
while True:
stdout_line = None
stderr_line = None
for outstream in streams.keys():
stream = streams[outstream]
if not stream:
continue
line = to_text(stream.readline())
if not line:
break
continue
line = line.rstrip()
output.append(line)
display_line = line
if len(line) > 200:
display_line = "{0}...".format(line[:200])
if outstream == "stderr":
stderr_line = line
else:
stdout_line = line
if not (stdout_line or stderr_line):
break
if stderr_line:
err.append(line)
if stdout_line:
output.append(stdout_line)
display_line = stdout_line
if len(stdout_line) > display_limit:
display_line = "{0}...".format(stdout_line[:display_limit])
if verbose:
spinner.write(display_line)
else:
spinner.text = "{0} {1}".format(spinner_orig_text, display_line)
continue
spinner.text = "{0} {1}".format(spinner_orig_text, display_line)
continue
try:
c.wait()
finally:
if c.stdout:
c.stdout.close()
if c.stderr:
c.stderr.close()
if spinner:
if c.returncode > 0:
spinner.fail("Failed...cleaning up...")
spinner.text = "Complete!"
spinner.ok("✔")
c.out = "".join(output)
c.err = ""
c.out = "\n".join(output)
c.err = "\n".join(err) if err else ""
else:
c.out, c.err = c.communicate()
if not return_object:
return c.out.strip(), c.err.strip()
if not block:
c.wait()
out = c.out if c.out else ""
err = c.err if c.err else ""
return out.strip(), err.strip()
return c


def run(
cmd,
env={},
env=None,
return_object=False,
block=True,
cwd=None,
verbose=False,
nospin=False,
spinner=None,
combine_stderr=True,
display_limit=200
):
"""Use `subprocess.Popen` to get the output of a command and decode it.
Expand All @@ -179,38 +211,63 @@ def run(
:param str cwd: Current working directory contect to use for spawning the subprocess.
:param bool verbose: Whether to print stdout in real time when non-blocking.
:param bool nospin: Whether to disable the cli spinner.
:param str spinner: The name of the spinner to use if enabled, defaults to bouncingBar
:param bool combine_stderr: Optionally merge stdout and stderr in the subprocess, false if nonblocking.
:param int dispay_limit: The max width of output lines to display when using a spinner.
:returns: A 2-tuple of (output, error) or a :class:`subprocess.Popen` object.
.. Warning:: Merging standard out and standarad error in a nonblocking subprocess
can cause errors in some cases and may not be ideal. Consider disabling
this functionality.
"""

if not env:
env = os.environ.copy()
if six.PY2:
fs_encode = partial(to_bytes, encoding=locale_encoding)
_env = {fs_encode(k): fs_encode(v) for k, v in os.environ.items()}
for key, val in env.items():
_env[fs_encode(key)] = fs_encode(val)
else:
_env = {k: fs_str(v) for k, v in os.environ.items()}
if not spinner:
spinner = "bouncingBar"
if six.PY2:
if isinstance(cmd, six.string_types):
cmd = cmd.encode("utf-8")
elif isinstance(cmd, (list, tuple)):
cmd = [c.encode("utf-8") for c in cmd]
if not isinstance(cmd, Script):
cmd = Script.parse(cmd)
if block or not return_object:
combine_stderr = False
sigmap = {}
if nospin is False:
try:
import signal
from yaspin import yaspin
from yaspin import spinners
from yaspin.signal_handlers import fancy_handler
except ImportError:
raise RuntimeError(
"Failed to import spinner! Reinstall vistir with command:"
" pip install --upgrade vistir[spinner]"
)
else:
spinner = yaspin
animation = spinners.Spinners.bouncingBar
animation = getattr(spinners.Spinners, spinner)
sigmap = {
signal.SIGINT: fancy_handler
}
if os.name == "nt":
sigmap.update({
signal.CTRL_C_EVENT: fancy_handler,
signal.CTRL_BREAK_EVENT: fancy_handler
})
spinner_func = yaspin
else:

@contextmanager
def spinner(spin_type, text):
def spinner_func(spin_type, text, **kwargs):
class FakeClass(object):
def __init__(self, text=""):
self.text = text
Expand All @@ -225,7 +282,7 @@ def write(self, text):
yield myobj

animation = None
with spinner(animation, text="Running...") as sp:
with spinner_func(animation, sigmap=sigmap, text="Running...") as sp:
return _create_subprocess(
cmd,
env=_env,
Expand All @@ -234,6 +291,7 @@ def write(self, text):
cwd=cwd,
verbose=verbose,
spinner=sp,
combine_stderr=combine_stderr
)


Expand All @@ -249,7 +307,8 @@ def load_path(python):
"""

python = Path(python).as_posix()
out, err = run([python, "-c", "import json, sys; print(json.dumps(sys.path))"])
out, err = run([python, "-c", "import json, sys; print(json.dumps(sys.path))"],
nospin=True)
if out:
return json.loads(out)
else:
Expand Down

0 comments on commit aac05b8

Please sign in to comment.