Skip to content

Commit

Permalink
Merge branch 'master' into shell-fallback-improvement
Browse files Browse the repository at this point in the history
  • Loading branch information
uranusjr committed Jul 30, 2018
2 parents 7758d74 + 5a55c88 commit 9ec4ff1
Show file tree
Hide file tree
Showing 33 changed files with 897 additions and 365 deletions.
2 changes: 1 addition & 1 deletion docs/advanced.rst
Original file line number Diff line number Diff line change
Expand Up @@ -242,11 +242,11 @@ different products which integrate with Pipenv projects:
- `Emacs <https://github.com/pwalsh/pipenv.el>`_ (Editor Integration)
- `Fish Shell <https://github.com/fisherman/pipenv>`_ (Automatic ``$ pipenv shell``!)
- `VS Code <https://code.visualstudio.com/docs/python/environments>`_ (Editor Integration)
- `PyCharm <https://www.jetbrains.com/pycharm/download/>`_ (Editor Integration)

Works in progress:

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


Expand Down
1 change: 1 addition & 0 deletions news/2434.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed the ability of pipenv to parse ``dependency_links`` from ``setup.py`` when ``PIP_PROCESS_DEPENDENCY_LINKS`` is enabled.
1 change: 1 addition & 0 deletions news/2582.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed multiple issues with finding the correct system python locations.
4 changes: 4 additions & 0 deletions news/2582.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Greatly enhanced python discovery functionality:

- Added pep514 (windows launcher/finder) support for python discovery.
- Introduced architecture discovery for python installations which support different architectures.
1 change: 1 addition & 0 deletions news/2582.vendor
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Update ``pythonfinder`` to major release ``1.0.0`` for integration.
2 changes: 2 additions & 0 deletions news/2607.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Catch JSON decoding error to prevent exception when the lock file is of
invalid format.
1 change: 1 addition & 0 deletions news/2643.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +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``.
1 change: 1 addition & 0 deletions news/2643.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Enhanced resolution of editable and VCS dependencies.
177 changes: 51 additions & 126 deletions pipenv/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import shutil
import time
import tempfile
from glob import glob
import json as simplejson
import click
import click_completion
Expand Down Expand Up @@ -51,8 +50,6 @@
PIPENV_SKIP_VALIDATION,
PIPENV_HIDE_EMOJIS,
PIPENV_INSTALL_TIMEOUT,
PYENV_ROOT,
PYENV_INSTALLED,
PIPENV_YES,
PIPENV_DONT_LOAD_ENV,
PIPENV_DEFAULT_PYTHON_VERSION,
Expand Down Expand Up @@ -317,76 +314,38 @@ def ensure_pipfile(validate=True, skip_requirements=False, system=False):
project.write_toml(p)


def find_python_from_py(python):
"""Find a Python executable from on Windows.
def find_a_system_python(line):
"""Find a Python installation from a given line.
Ask py.exe for its opinion.
"""
py = system_which("py")
if not py:
return None

version_args = ["-{0}".format(python[0])]
if len(python) >= 2:
version_args.append("-{0}.{1}".format(python[0], python[2]))
import subprocess

for ver_arg in reversed(version_args):
try:
python_exe = subprocess.check_output(
[py, ver_arg, "-c", "import sys; print(sys.executable)"]
)
except subprocess.CalledProcessError:
continue
This tries to parse the line in various of ways:
if not isinstance(python_exe, str):
python_exe = python_exe.decode(sys.getdefaultencoding())
python_exe = python_exe.strip()
version = python_version(python_exe)
if (version or "").startswith(python):
return python_exe


def find_python_in_path(python):
"""Find a Python executable from a version number.
This uses the PATH environment variable to locate an appropriate Python.
* Looks like an absolute path? Use it directly.
* Looks like a py.exe call? Use py.exe to get the executable.
* Starts with "py" something? Looks like a python command. Try to find it
in PATH, and use it directly.
* Search for "python" and "pythonX.Y" executables in PATH to find a match.
* Nothing fits, return None.
"""
possibilities = ["python", "python{0}".format(python[0])]
if len(python) >= 2:
possibilities.extend(
[
"python{0}{1}".format(python[0], python[2]),
"python{0}.{1}".format(python[0], python[2]),
"python{0}.{1}m".format(python[0], python[2]),
]
)
# Reverse the list, so we find specific ones first.
possibilities = reversed(possibilities)
for possibility in possibilities:
# Windows compatibility.
if os.name == "nt":
possibility = "{0}.exe".format(possibility)
pythons = system_which(possibility, mult=True)
for p in pythons:
version = python_version(p)
if (version or "").startswith(python):
return p


def find_a_system_python(python):
"""Finds a system python, given a version (e.g. 2 / 2.7 / 3.6.2), or a full path."""
if python.startswith("py"):
return system_which(python)

elif os.path.isabs(python):
return python

python_from_py = find_python_from_py(python)
if python_from_py:
return python_from_py

return find_python_in_path(python)
if not line:
return None
if os.path.isabs(line):
return line
from .vendor.pythonfinder import Finder
finder = Finder(system=False, global_search=True)
if ((line.startswith("py ") or line.startswith("py.exe "))
and os.name == 'nt'):
line = line.split(" ", 1)[1].lstrip("-")
elif line.startswith("py"):
python_entry = finder.which(line)
if python_entry:
return python_entry.path.as_posix()
return None
python_entry = finder.find_python_version(line)
if not python_entry:
python_entry = finder.which("python{0}".format(line))
if python_entry:
return python_entry.path.as_posix()
return None


def ensure_python(three=None, python=None):
Expand All @@ -409,35 +368,7 @@ def abort():
)
sys.exit(1)

def activate_pyenv():
from notpip._vendor.packaging.version import parse as parse_version

"""Adds all pyenv installations to the PATH."""
if PYENV_INSTALLED:
if PYENV_ROOT:
pyenv_paths = {}
for found in glob("{0}{1}versions{1}*".format(PYENV_ROOT, os.sep)):
pyenv_paths[os.path.split(found)[1]] = "{0}{1}bin".format(
found, os.sep
)
for version_str, pyenv_path in pyenv_paths.items():
version = parse_version(version_str)
if version.is_prerelease and pyenv_paths.get(version.base_version):
continue

add_to_path(pyenv_path)
else:
click.echo(
"{0}: PYENV_ROOT is not set. New python paths will "
"probably not be exported properly after installation."
"".format(crayons.red("Warning", bold=True)),
err=True,
)

global USING_DEFAULT_PYTHON
# Add pyenv paths to PATH.
activate_pyenv()
path_to_python = None
USING_DEFAULT_PYTHON = three is None and not python
# Find out which python is desired.
if not python:
Expand All @@ -446,8 +377,7 @@ def activate_pyenv():
python = project.required_python_version
if not python:
python = PIPENV_DEFAULT_PYTHON_VERSION
if python:
path_to_python = find_a_system_python(python)
path_to_python = find_a_system_python(python)
if not path_to_python and python is not None:
# We need to install Python.
click.echo(
Expand All @@ -459,6 +389,7 @@ def activate_pyenv():
err=True,
)
# Pyenv is installed
from .vendor.pythonfinder.environment import PYENV_INSTALLED
if not PYENV_INSTALLED:
abort()
else:
Expand Down Expand Up @@ -501,8 +432,6 @@ def activate_pyenv():
click.echo(crayons.blue(e.err), err=True)
# Print the results, in a beautiful blue…
click.echo(crayons.blue(c.out), err=True)
# Add new paths to PATH.
activate_pyenv()
# Find the newly installed Python, hopefully.
version = str(version)
path_to_python = find_a_system_python(version)
Expand Down Expand Up @@ -915,21 +844,17 @@ def do_create_virtualenv(python=None, site_packages=False, pypi_mirror=None):

# Actually create the virtualenv.
with spinner():
try:
c = delegator.run(cmd, block=False, timeout=PIPENV_TIMEOUT, env=pip_config)
except OSError:
click.echo(
"{0}: it looks like {1} is not in your {2}. "
"We cannot continue until this is resolved."
"".format(
crayons.red("Warning", bold=True),
crayons.red(cmd[0]),
crayons.normal("PATH", bold=True),
),
err=True,
)
sys.exit(1)
click.echo(crayons.blue(c.out), err=True)
c = delegator.run(
cmd, block=False, timeout=PIPENV_TIMEOUT, env=pip_config,
)
c.block()
click.echo(crayons.blue("{0}".format(c.out)), err=True)
if c.return_code != 0:
click.echo(crayons.blue("{0}".format(c.err)), err=True)
click.echo(u"{0}: Failed to create virtual environment.".format(
crayons.red("Warning", bold=True),
), err=True)
sys.exit(1)

# Associate project directory with the environment.
# This mimics Pew's "setproject".
Expand Down Expand Up @@ -1258,19 +1183,19 @@ def do_init(
err=True,
)
else:
click.echo(
crayons.red(
u"Pipfile.lock ({0}) out of date, updating to ({1})…".format(
old_hash[-6:], new_hash[-6:]
),
bold=True,
),
err=True,
)
if old_hash:
msg = u"Pipfile.lock ({1}) out of date, updating to ({0})…"
else:
msg = u"Pipfile.lock is corrupted, replaced with ({0})…"
click.echo(crayons.red(
msg.format(old_hash[-6:], new_hash[-6:]),
bold=True,
), err=True)
do_lock(
system=system,
pre=pre,
keep_outdated=keep_outdated,
verbose=verbose,
write=True,
pypi_mirror=pypi_mirror,
)
Expand Down
7 changes: 0 additions & 7 deletions pipenv/environments.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,12 +213,5 @@
os.environ.get("COMSPEC")
)

# Internal, to tell if pyenv is installed.
PYENV_ROOT = os.environ.get("PYENV_ROOT", os.path.expanduser("~/.pyenv"))
PYENV_INSTALLED = (
bool(os.environ.get("PYENV_SHELL")) or
bool(os.environ.get("PYENV_ROOT"))
)

# Internal, to tell whether the command line session is interactive.
SESSION_IS_INTERACTIVE = bool(os.isatty(sys.stdout.fileno()))
32 changes: 13 additions & 19 deletions pipenv/help.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# coding: utf-8
import os
import pprint
import sys

import pipenv

from pprint import pprint
from .__version__ import __version__
from .core import project, system_which, find_python_in_path, python_version
from .core import project
from .pep508checker import lookup
from .vendor import pythonfinder


def print_utf(line):
Expand All @@ -19,32 +20,25 @@ def print_utf(line):
def get_pipenv_diagnostics():
print("<details><summary>$ pipenv --support</summary>")
print("")
print("Pipenv version: `{0!r}`".format(__version__))
print("Pipenv version: `{0!r}`".format(pipenv.__version__))
print("")
print("Pipenv location: `{0!r}`".format(os.path.dirname(pipenv.__file__)))
print("")
print("Python location: `{0!r}`".format(sys.executable))
print("")
print("Other Python installations in `PATH`:")
print("")
for python_v in ("2.5", "2.6", "2.7", "3.4", "3.5", "3.6", "3.7"):
found = find_python_in_path(python_v)
if found:
print(" - `{0}`: `{1}`".format(python_v, found))
found = system_which("python{0}".format(python_v), mult=True)
if found:
for f in found:
print(" - `{0}`: `{1}`".format(python_v, f))
print("Python installations found:")
print("")
for p in ("python", "python2", "python3", "py"):
found = system_which(p, mult=True)
for f in found:
print(" - `{0}`: `{1}`".format(python_version(f), f))

finder = pythonfinder.Finder(system=False, global_search=True)
python_paths = finder.find_all_python_versions()
for python in python_paths:
print(" - `{}`: `{}`".format(python.py_version.version, python.path))

print("")
print("PEP 508 Information:")
print("")
print("```")
pprint(lookup)
pprint.pprint(lookup)
print("```")
print("")
print("System environment variables:")
Expand Down
Loading

0 comments on commit 9ec4ff1

Please sign in to comment.