Merge pull request #3007 from pypa/fix-normalization-during-install

Fix normalization during install
This commit is contained in:
Dan Ryan
2018-10-12 15:33:02 -04:00
committed by GitHub
9 changed files with 105 additions and 28 deletions
+1
View File
@@ -0,0 +1 @@
Fixed a bug which prevented installing pinned versions which used redirection symbols from the command line.
+1
View File
@@ -0,0 +1 @@
Upgraded ``pythonfinder => 1.1.1`` and ``vistir => 0.1.7``.
+2
View File
@@ -40,6 +40,7 @@ from .utils import (
rmtree,
clean_resolved_dep,
parse_indexes,
escape_cmd
)
from . import environments, pep508checker, progress
from .environments import (
@@ -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:
+6
View File
@@ -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.
+1 -1
View File
@@ -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
+9 -1
View File
@@ -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)
@@ -46,6 +47,8 @@ class PythonVersion(object):
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
@@ -102,6 +105,10 @@ class PythonVersion(object):
:rtype: dict.
"""
is_debug = False
if version.endswith("-debug"):
is_debug = True
version, _, _ = verson.rpartition("-")
try:
version = parse_version(str(version))
except TypeError:
@@ -125,6 +132,7 @@ class PythonVersion(object):
"is_prerelease": version.is_prerelease,
"is_postrelease": version.is_postrelease,
"is_devrelease": version.is_devrelease,
"is_debug": is_debug,
"version": version,
}
@@ -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)
+2 -2
View File
@@ -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
@@ -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
+1 -1
View File
@@ -13,7 +13,7 @@ from .misc import load_path, partialclass, run, shell_escape
from .path import mkdir_p, rmtree
__version__ = '0.1.6'
__version__ = '0.1.7'
__all__ = [
+82 -23
View File
@@ -75,7 +75,7 @@ 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)
@@ -83,7 +83,7 @@ def _spawn_subprocess(script, env={}, block=True, cwd=None):
"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:
@@ -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.
@@ -179,8 +211,18 @@ 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()}
@@ -188,6 +230,8 @@ def run(
_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")
@@ -195,22 +239,35 @@ def run(
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
@@ -225,7 +282,7 @@ def run(
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,
@@ -234,6 +291,7 @@ def run(
cwd=cwd,
verbose=verbose,
spinner=sp,
combine_stderr=combine_stderr
)
@@ -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: