Skip to content

Commit 9ec4ff1

Browse files
authored
Merge branch 'master' into shell-fallback-improvement
2 parents 7758d74 + 5a55c88 commit 9ec4ff1

33 files changed

+897
-365
lines changed

docs/advanced.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,11 @@ different products which integrate with Pipenv projects:
242242
- `Emacs <https://github.com/pwalsh/pipenv.el>`_ (Editor Integration)
243243
- `Fish Shell <https://github.com/fisherman/pipenv>`_ (Automatic ``$ pipenv shell``!)
244244
- `VS Code <https://code.visualstudio.com/docs/python/environments>`_ (Editor Integration)
245+
- `PyCharm <https://www.jetbrains.com/pycharm/download/>`_ (Editor Integration)
245246

246247
Works in progress:
247248

248249
- `Sublime Text <https://github.com/kennethreitz/pipenv-sublime>`_ (Editor Integration)
249-
- `PyCharm <https://www.jetbrains.com/pycharm/download/>`_ (Editor Integration)
250250
- Mysterious upcoming Google Cloud product (Cloud Hosting)
251251

252252

news/2434.bugfix

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed the ability of pipenv to parse ``dependency_links`` from ``setup.py`` when ``PIP_PROCESS_DEPENDENCY_LINKS`` is enabled.

news/2582.bugfix

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed multiple issues with finding the correct system python locations.

news/2582.feature

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Greatly enhanced python discovery functionality:
2+
3+
- Added pep514 (windows launcher/finder) support for python discovery.
4+
- Introduced architecture discovery for python installations which support different architectures.

news/2582.vendor

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Update ``pythonfinder`` to major release ``1.0.0`` for integration.

news/2607.bugfix

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Catch JSON decoding error to prevent exception when the lock file is of
2+
invalid format.

news/2643.bugfix

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Dependency links to private repositories defined via ``ssh://`` schemes will now install correctly and skip hashing as long as ``PIP_PROCESS_DEPENDENCY_LINKS=1``.

news/2643.feature

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Enhanced resolution of editable and VCS dependencies.

pipenv/core.py

+51-126
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import shutil
77
import time
88
import tempfile
9-
from glob import glob
109
import json as simplejson
1110
import click
1211
import click_completion
@@ -51,8 +50,6 @@
5150
PIPENV_SKIP_VALIDATION,
5251
PIPENV_HIDE_EMOJIS,
5352
PIPENV_INSTALL_TIMEOUT,
54-
PYENV_ROOT,
55-
PYENV_INSTALLED,
5653
PIPENV_YES,
5754
PIPENV_DONT_LOAD_ENV,
5855
PIPENV_DEFAULT_PYTHON_VERSION,
@@ -317,76 +314,38 @@ def ensure_pipfile(validate=True, skip_requirements=False, system=False):
317314
project.write_toml(p)
318315

319316

320-
def find_python_from_py(python):
321-
"""Find a Python executable from on Windows.
317+
def find_a_system_python(line):
318+
"""Find a Python installation from a given line.
322319
323-
Ask py.exe for its opinion.
324-
"""
325-
py = system_which("py")
326-
if not py:
327-
return None
328-
329-
version_args = ["-{0}".format(python[0])]
330-
if len(python) >= 2:
331-
version_args.append("-{0}.{1}".format(python[0], python[2]))
332-
import subprocess
333-
334-
for ver_arg in reversed(version_args):
335-
try:
336-
python_exe = subprocess.check_output(
337-
[py, ver_arg, "-c", "import sys; print(sys.executable)"]
338-
)
339-
except subprocess.CalledProcessError:
340-
continue
320+
This tries to parse the line in various of ways:
341321
342-
if not isinstance(python_exe, str):
343-
python_exe = python_exe.decode(sys.getdefaultencoding())
344-
python_exe = python_exe.strip()
345-
version = python_version(python_exe)
346-
if (version or "").startswith(python):
347-
return python_exe
348-
349-
350-
def find_python_in_path(python):
351-
"""Find a Python executable from a version number.
352-
353-
This uses the PATH environment variable to locate an appropriate Python.
322+
* Looks like an absolute path? Use it directly.
323+
* Looks like a py.exe call? Use py.exe to get the executable.
324+
* Starts with "py" something? Looks like a python command. Try to find it
325+
in PATH, and use it directly.
326+
* Search for "python" and "pythonX.Y" executables in PATH to find a match.
327+
* Nothing fits, return None.
354328
"""
355-
possibilities = ["python", "python{0}".format(python[0])]
356-
if len(python) >= 2:
357-
possibilities.extend(
358-
[
359-
"python{0}{1}".format(python[0], python[2]),
360-
"python{0}.{1}".format(python[0], python[2]),
361-
"python{0}.{1}m".format(python[0], python[2]),
362-
]
363-
)
364-
# Reverse the list, so we find specific ones first.
365-
possibilities = reversed(possibilities)
366-
for possibility in possibilities:
367-
# Windows compatibility.
368-
if os.name == "nt":
369-
possibility = "{0}.exe".format(possibility)
370-
pythons = system_which(possibility, mult=True)
371-
for p in pythons:
372-
version = python_version(p)
373-
if (version or "").startswith(python):
374-
return p
375-
376-
377-
def find_a_system_python(python):
378-
"""Finds a system python, given a version (e.g. 2 / 2.7 / 3.6.2), or a full path."""
379-
if python.startswith("py"):
380-
return system_which(python)
381-
382-
elif os.path.isabs(python):
383-
return python
384-
385-
python_from_py = find_python_from_py(python)
386-
if python_from_py:
387-
return python_from_py
388-
389-
return find_python_in_path(python)
329+
if not line:
330+
return None
331+
if os.path.isabs(line):
332+
return line
333+
from .vendor.pythonfinder import Finder
334+
finder = Finder(system=False, global_search=True)
335+
if ((line.startswith("py ") or line.startswith("py.exe "))
336+
and os.name == 'nt'):
337+
line = line.split(" ", 1)[1].lstrip("-")
338+
elif line.startswith("py"):
339+
python_entry = finder.which(line)
340+
if python_entry:
341+
return python_entry.path.as_posix()
342+
return None
343+
python_entry = finder.find_python_version(line)
344+
if not python_entry:
345+
python_entry = finder.which("python{0}".format(line))
346+
if python_entry:
347+
return python_entry.path.as_posix()
348+
return None
390349

391350

392351
def ensure_python(three=None, python=None):
@@ -409,35 +368,7 @@ def abort():
409368
)
410369
sys.exit(1)
411370

412-
def activate_pyenv():
413-
from notpip._vendor.packaging.version import parse as parse_version
414-
415-
"""Adds all pyenv installations to the PATH."""
416-
if PYENV_INSTALLED:
417-
if PYENV_ROOT:
418-
pyenv_paths = {}
419-
for found in glob("{0}{1}versions{1}*".format(PYENV_ROOT, os.sep)):
420-
pyenv_paths[os.path.split(found)[1]] = "{0}{1}bin".format(
421-
found, os.sep
422-
)
423-
for version_str, pyenv_path in pyenv_paths.items():
424-
version = parse_version(version_str)
425-
if version.is_prerelease and pyenv_paths.get(version.base_version):
426-
continue
427-
428-
add_to_path(pyenv_path)
429-
else:
430-
click.echo(
431-
"{0}: PYENV_ROOT is not set. New python paths will "
432-
"probably not be exported properly after installation."
433-
"".format(crayons.red("Warning", bold=True)),
434-
err=True,
435-
)
436-
437371
global USING_DEFAULT_PYTHON
438-
# Add pyenv paths to PATH.
439-
activate_pyenv()
440-
path_to_python = None
441372
USING_DEFAULT_PYTHON = three is None and not python
442373
# Find out which python is desired.
443374
if not python:
@@ -446,8 +377,7 @@ def activate_pyenv():
446377
python = project.required_python_version
447378
if not python:
448379
python = PIPENV_DEFAULT_PYTHON_VERSION
449-
if python:
450-
path_to_python = find_a_system_python(python)
380+
path_to_python = find_a_system_python(python)
451381
if not path_to_python and python is not None:
452382
# We need to install Python.
453383
click.echo(
@@ -459,6 +389,7 @@ def activate_pyenv():
459389
err=True,
460390
)
461391
# Pyenv is installed
392+
from .vendor.pythonfinder.environment import PYENV_INSTALLED
462393
if not PYENV_INSTALLED:
463394
abort()
464395
else:
@@ -501,8 +432,6 @@ def activate_pyenv():
501432
click.echo(crayons.blue(e.err), err=True)
502433
# Print the results, in a beautiful blue…
503434
click.echo(crayons.blue(c.out), err=True)
504-
# Add new paths to PATH.
505-
activate_pyenv()
506435
# Find the newly installed Python, hopefully.
507436
version = str(version)
508437
path_to_python = find_a_system_python(version)
@@ -915,21 +844,17 @@ def do_create_virtualenv(python=None, site_packages=False, pypi_mirror=None):
915844

916845
# Actually create the virtualenv.
917846
with spinner():
918-
try:
919-
c = delegator.run(cmd, block=False, timeout=PIPENV_TIMEOUT, env=pip_config)
920-
except OSError:
921-
click.echo(
922-
"{0}: it looks like {1} is not in your {2}. "
923-
"We cannot continue until this is resolved."
924-
"".format(
925-
crayons.red("Warning", bold=True),
926-
crayons.red(cmd[0]),
927-
crayons.normal("PATH", bold=True),
928-
),
929-
err=True,
930-
)
931-
sys.exit(1)
932-
click.echo(crayons.blue(c.out), err=True)
847+
c = delegator.run(
848+
cmd, block=False, timeout=PIPENV_TIMEOUT, env=pip_config,
849+
)
850+
c.block()
851+
click.echo(crayons.blue("{0}".format(c.out)), err=True)
852+
if c.return_code != 0:
853+
click.echo(crayons.blue("{0}".format(c.err)), err=True)
854+
click.echo(u"{0}: Failed to create virtual environment.".format(
855+
crayons.red("Warning", bold=True),
856+
), err=True)
857+
sys.exit(1)
933858

934859
# Associate project directory with the environment.
935860
# This mimics Pew's "setproject".
@@ -1258,19 +1183,19 @@ def do_init(
12581183
err=True,
12591184
)
12601185
else:
1261-
click.echo(
1262-
crayons.red(
1263-
u"Pipfile.lock ({0}) out of date, updating to ({1})…".format(
1264-
old_hash[-6:], new_hash[-6:]
1265-
),
1266-
bold=True,
1267-
),
1268-
err=True,
1269-
)
1186+
if old_hash:
1187+
msg = u"Pipfile.lock ({1}) out of date, updating to ({0})…"
1188+
else:
1189+
msg = u"Pipfile.lock is corrupted, replaced with ({0})…"
1190+
click.echo(crayons.red(
1191+
msg.format(old_hash[-6:], new_hash[-6:]),
1192+
bold=True,
1193+
), err=True)
12701194
do_lock(
12711195
system=system,
12721196
pre=pre,
12731197
keep_outdated=keep_outdated,
1198+
verbose=verbose,
12741199
write=True,
12751200
pypi_mirror=pypi_mirror,
12761201
)

pipenv/environments.py

-7
Original file line numberDiff line numberDiff line change
@@ -213,12 +213,5 @@
213213
os.environ.get("COMSPEC")
214214
)
215215

216-
# Internal, to tell if pyenv is installed.
217-
PYENV_ROOT = os.environ.get("PYENV_ROOT", os.path.expanduser("~/.pyenv"))
218-
PYENV_INSTALLED = (
219-
bool(os.environ.get("PYENV_SHELL")) or
220-
bool(os.environ.get("PYENV_ROOT"))
221-
)
222-
223216
# Internal, to tell whether the command line session is interactive.
224217
SESSION_IS_INTERACTIVE = bool(os.isatty(sys.stdout.fileno()))

pipenv/help.py

+13-19
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
# coding: utf-8
22
import os
3+
import pprint
34
import sys
5+
46
import pipenv
57

6-
from pprint import pprint
7-
from .__version__ import __version__
8-
from .core import project, system_which, find_python_in_path, python_version
8+
from .core import project
99
from .pep508checker import lookup
10+
from .vendor import pythonfinder
1011

1112

1213
def print_utf(line):
@@ -19,32 +20,25 @@ def print_utf(line):
1920
def get_pipenv_diagnostics():
2021
print("<details><summary>$ pipenv --support</summary>")
2122
print("")
22-
print("Pipenv version: `{0!r}`".format(__version__))
23+
print("Pipenv version: `{0!r}`".format(pipenv.__version__))
2324
print("")
2425
print("Pipenv location: `{0!r}`".format(os.path.dirname(pipenv.__file__)))
2526
print("")
2627
print("Python location: `{0!r}`".format(sys.executable))
2728
print("")
28-
print("Other Python installations in `PATH`:")
29-
print("")
30-
for python_v in ("2.5", "2.6", "2.7", "3.4", "3.5", "3.6", "3.7"):
31-
found = find_python_in_path(python_v)
32-
if found:
33-
print(" - `{0}`: `{1}`".format(python_v, found))
34-
found = system_which("python{0}".format(python_v), mult=True)
35-
if found:
36-
for f in found:
37-
print(" - `{0}`: `{1}`".format(python_v, f))
29+
print("Python installations found:")
3830
print("")
39-
for p in ("python", "python2", "python3", "py"):
40-
found = system_which(p, mult=True)
41-
for f in found:
42-
print(" - `{0}`: `{1}`".format(python_version(f), f))
31+
32+
finder = pythonfinder.Finder(system=False, global_search=True)
33+
python_paths = finder.find_all_python_versions()
34+
for python in python_paths:
35+
print(" - `{}`: `{}`".format(python.py_version.version, python.path))
36+
4337
print("")
4438
print("PEP 508 Information:")
4539
print("")
4640
print("```")
47-
pprint(lookup)
41+
pprint.pprint(lookup)
4842
print("```")
4943
print("")
5044
print("System environment variables:")

0 commit comments

Comments
 (0)