Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix version comparisons in do_outdated #3798

Merged
merged 6 commits into from
Jun 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ Pipenv: Python Development Workflow for Humans

[![image](https://img.shields.io/pypi/v/pipenv.svg)](https://python.org/pypi/pipenv)
[![image](https://img.shields.io/pypi/l/pipenv.svg)](https://python.org/pypi/pipenv)
[![image](https://badge.buildkite.com/79c7eccf056b17c3151f3c4d0e4c4b8b724539d84f1e037b9b.svg?branch=master)](https://code.kennethreitz.org/source/pipenv/)
[![Azure Pipelines build status (Linux)](https://dev.azure.com/pypa/pipenv/_apis/build/status/pipenv%20CI%20(Linux)?branchName=master&label=Linux)](https://dev.azure.com/pypa/pipenv/_build/latest?definitionId=13&branchName=master)
[![Azure Pipelines build status (Windows)](https://dev.azure.com/pypa/pipenv/_apis/build/status/pipenv%20CI%20(Windows)?branchName=master&label=Windows)](https://dev.azure.com/pypa/pipenv/_build/latest?definitionId=12&branchName=master)
[![Azure Pipelines Build Status](https://dev.azure.com/pypa/pipenv/_apis/build/status/Pipenv%20CI?branchName=master)](https://dev.azure.com/pypa/pipenv/_build/latest?definitionId=16&branchName=master)
[![image](https://img.shields.io/pypi/pyversions/pipenv.svg)](https://python.org/pypi/pipenv)
[![image](https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg)](https://saythanks.io/to/kennethreitz)

Expand Down
1 change: 1 addition & 0 deletions news/3766.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
``pipenv update --outdated`` will now correctly handle comparisons between pre/post-releases and normal releases.
1 change: 1 addition & 0 deletions news/3766.vendor.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Updated ``pip_shims`` to support ``--outdated`` with new pip versions.
124 changes: 79 additions & 45 deletions pipenv/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
PIPENV_CACHE_DIR, PIPENV_COLORBLIND, PIPENV_DEFAULT_PYTHON_VERSION,
PIPENV_DONT_USE_PYENV, PIPENV_HIDE_EMOJIS, PIPENV_MAX_SUBPROCESS,
PIPENV_PYUP_API_KEY, PIPENV_SHELL_FANCY, PIPENV_SKIP_VALIDATION,
PIPENV_YES, SESSION_IS_INTERACTIVE, PIP_EXISTS_ACTION
PIPENV_YES, SESSION_IS_INTERACTIVE, PIP_EXISTS_ACTION, PIPENV_RESOLVE_VCS
)
from .project import Project, SourceNotFound
from .utils import (
Expand Down Expand Up @@ -1798,7 +1798,9 @@ def do_py(system=False):
def do_outdated(pypi_mirror=None):
# TODO: Allow --skip-lock here?
from .vendor.requirementslib.models.requirements import Requirement
from .vendor.requirementslib.models.utils import get_version
from .vendor.packaging.utils import canonicalize_name
from .vendor.vistir.compat import Mapping
from collections import namedtuple

packages = {}
Expand All @@ -1810,6 +1812,7 @@ def do_outdated(pypi_mirror=None):
(pkg.project_name, pkg.parsed_version, pkg.latest_version)
for pkg in project.environment.get_outdated_packages()
}
reverse_deps = project.environment.reverse_dependencies()
for result in installed_packages:
dep = Requirement.from_line(str(result.as_requirement()))
packages.update(dep.as_pipfile())
Expand All @@ -1833,10 +1836,26 @@ def do_outdated(pypi_mirror=None):
elif canonicalize_name(package) in outdated_packages:
skipped.append(outdated_packages[canonicalize_name(package)])
for package, old_version, new_version in skipped:
click.echo(crayons.yellow(
"Skipped Update of Package {0!s}: {1!s} installed, {2!s} available.".format(
package, old_version, new_version
)), err=True
name_in_pipfile = project.get_package_name_in_pipfile(package)
pipfile_version_text = ""
required = ""
version = None
if name_in_pipfile:
version = get_version(project.packages[name_in_pipfile])
reverse_deps = reverse_deps.get(name_in_pipfile)
if isinstance(reverse_deps, Mapping) and "required" in reverse_deps:
required = " {0} required".format(reverse_deps["required"])
if version:
pipfile_version_text = " ({0} set in Pipfile)".format(version)
else:
pipfile_version_text = " (Unpinned in Pipfile)"
click.echo(
crayons.yellow(
"Skipped Update of Package {0!s}: {1!s} installed,{2!s}{3!s}, "
"{4!s} available.".format(
package, old_version, required, pipfile_version_text, new_version
)
), err=True
)
if not outdated:
click.echo(crayons.green("All packages are up to date!", bold=True))
Expand Down Expand Up @@ -2064,6 +2083,7 @@ def do_install(
os.environ["PIP_USER"] = vistir.compat.fs_str("0")
if "PYTHONHOME" in os.environ:
del os.environ["PYTHONHOME"]
sp.text = "Resolving {0}...".format(pkg_line)
try:
pkg_requirement = Requirement.from_line(pkg_line)
except ValueError as e:
Expand All @@ -2072,30 +2092,45 @@ def do_install(
sys.exit(1)
if index_url:
pkg_requirement.index = index_url
deps = []
if pkg_requirement.is_vcs and PIPENV_RESOLVE_VCS:
deps = pkg_requirement.req.dependencies
to_install = [pkg_requirement,]
no_deps = False
sp.text = "Installing..."
try:
c = pip_install(
pkg_requirement,
ignore_hashes=True,
allow_global=system,
selective_upgrade=selective_upgrade,
no_deps=False,
pre=pre,
requirements_dir=requirements_directory,
index=index_url,
extra_indexes=extra_index_url,
pypi_mirror=pypi_mirror,
)
if not c.ok:
sp.write_err(vistir.compat.fs_str(
"{0}: {1}".format(
crayons.red("WARNING"),
"Failed installing package {0}".format(pkg_line)
),
))
sp.write_err(vistir.compat.fs_str(
"Error text: {0}".format(c.out)
))
raise RuntimeError(c.err)
if deps:
to_install.extend([
Requirement.from_line(d) for d in list(deps[0].values())
])
no_deps = True
for dep in to_install:
sp.text = "Installing {0}...".format(dep.name)
c = pip_install(
dep,
ignore_hashes=True,
allow_global=system,
selective_upgrade=selective_upgrade,
no_deps=no_deps,
pre=pre,
requirements_dir=requirements_directory,
index=index_url,
extra_indexes=extra_index_url,
pypi_mirror=pypi_mirror,
)
if not c.ok:
sp.write_err(vistir.compat.fs_str(
"{0}: {1}".format(
crayons.red("WARNING"),
"Failed installing package {0}".format(pkg_line)
),
))
sp.write_err(vistir.compat.fs_str(
"Error text: {0}".format(c.out)
))
raise RuntimeError(c.err)
if environments.is_verbose():
click.echo(crayons.blue(format_pip_output(c.out)))
except (ValueError, RuntimeError) as e:
sp.write_err(vistir.compat.fs_str(
"{0}: {1}".format(crayons.red("WARNING"), e),
Expand All @@ -2105,7 +2140,7 @@ def do_install(
))
sys.exit(1)
# Warn if --editable wasn't passed.
if pkg_requirement.is_vcs and not pkg_requirement.editable:
if pkg_requirement.is_vcs and not pkg_requirement.editable and not PIPENV_RESOLVE_VCS:
sp.write_err(
"{0}: You installed a VCS dependency in non-editable mode. "
"This will work fine, but sub-dependencies will not be resolved by {1}."
Expand All @@ -2115,24 +2150,23 @@ def do_install(
crayons.red("$ pipenv lock"),
)
)
click.echo(crayons.blue(format_pip_output(c.out)))
# Ensure that package was successfully installed.
if c.return_code != 0:
# Ensure that package was successfully installed.
if c.return_code != 0:
sp.write_err(vistir.compat.fs_str(
"{0} An error occurred while installing {1}!".format(
crayons.red("Error: ", bold=True), crayons.green(pkg_line)
),
))
sp.write_err(vistir.compat.fs_str(crayons.blue(format_pip_error(c.err))))
if "setup.py egg_info" in c.err:
sp.write_err(vistir.compat.fs_str(
"{0} An error occurred while installing {1}!".format(
crayons.red("Error: ", bold=True), crayons.green(pkg_line)
),
"This is likely caused by a bug in {0}. "
"Report this to its maintainers.".format(
crayons.green(pkg_requirement.name)
)
))
sp.write_err(vistir.compat.fs_str(crayons.blue(format_pip_error(c.err))))
if "setup.py egg_info" in c.err:
sp.write_err(vistir.compat.fs_str(
"This is likely caused by a bug in {0}. "
"Report this to its maintainers.".format(
crayons.green(pkg_requirement.name)
)
))
sp.fail(environments.PIPENV_SPINNER_FAIL_TEXT.format("Installation Failed"))
sys.exit(1)
sp.fail(environments.PIPENV_SPINNER_FAIL_TEXT.format("Installation Failed"))
sys.exit(1)
sp.write(vistir.compat.fs_str(
u"{0} {1} {2} {3}{4}".format(
crayons.normal(u"Adding", bold=True),
Expand Down
39 changes: 25 additions & 14 deletions pipenv/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,6 @@ def get_paths(self):
"prefix='{1}');paths['platlib'] = distutils.sysconfig.get_python_lib("
"plat_specific=1, prefix='{1}');print(json.dumps(paths))"
)
vistir.misc.echo("command: {0}".format(py_command.format(install_scheme, prefix)), fg="white", style="bold", err=True)
command = [self.python, "-c", py_command.format(install_scheme, prefix)]
c = vistir.misc.run(
command, return_object=True, block=True, nospin=True, write_to_stdout=False
Expand Down Expand Up @@ -368,7 +367,9 @@ def get_installed_packages(self):

@contextlib.contextmanager
def get_finder(self, pre=False):
from .vendor.pip_shims.shims import Command, cmdoptions, index_group, PackageFinder
from .vendor.pip_shims.shims import (
Command, cmdoptions, index_group, PackageFinder, parse_version, pip_version
)
from .environments import PIPENV_CACHE_DIR
index_urls = [source.get("url") for source in self.sources]

Expand All @@ -387,25 +388,35 @@ class PipCommand(Command):
pip_options.cache_dir = PIPENV_CACHE_DIR
pip_options.pre = self.pipfile.get("pre", pre)
with pip_command._build_session(pip_options) as session:
finder = PackageFinder(
find_links=pip_options.find_links,
index_urls=index_urls, allow_all_prereleases=pip_options.pre,
trusted_hosts=pip_options.trusted_hosts,
process_dependency_links=pip_options.process_dependency_links,
session=session
)
finder_args = {
"find_links": pip_options.find_links,
"index_urls": index_urls,
"allow_all_prereleases": pip_options.pre,
"trusted_hosts": pip_options.trusted_hosts,
"session": session
}
if parse_version(pip_version) < parse_version("19.0"):
finder_args.update(
{"process_dependency_links": pip_options.process_dependency_links}
)
finder = PackageFinder(**finder_args)
yield finder

def get_package_info(self, pre=False):
from .vendor.pip_shims.shims import pip_version, parse_version
dependency_links = []
packages = self.get_installed_packages()
# This code is borrowed from pip's current implementation
for dist in packages:
if dist.has_metadata('dependency_links.txt'):
dependency_links.extend(dist.get_metadata_lines('dependency_links.txt'))
if parse_version(pip_version) < parse_version("19.0"):
for dist in packages:
if dist.has_metadata('dependency_links.txt'):
dependency_links.extend(
dist.get_metadata_lines('dependency_links.txt')
)

with self.get_finder() as finder:
finder.add_dependency_links(dependency_links)
if parse_version(pip_version) < parse_version("19.0"):
finder.add_dependency_links(dependency_links)

for dist in packages:
typ = 'unknown'
Expand Down Expand Up @@ -433,7 +444,7 @@ def get_package_info(self, pre=False):
def get_outdated_packages(self, pre=False):
return [
pkg for pkg in self.get_package_info(pre=pre)
if pkg.latest_version._version > pkg.parsed_version._version
if pkg.latest_version._key > pkg.parsed_version._key
]

@classmethod
Expand Down
5 changes: 4 additions & 1 deletion pipenv/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class Entry(object):

def __init__(self, name, entry_dict, project, resolver, reverse_deps=None, dev=False):
super(Entry, self).__init__()
from pipenv.vendor.requirementslib.models.utils import tomlkit_value_to_python
self.name = name
if isinstance(entry_dict, dict):
self.entry_dict = self.clean_initial_dict(entry_dict)
Expand All @@ -106,7 +107,9 @@ def __init__(self, name, entry_dict, project, resolver, reverse_deps=None, dev=F
section = "develop" if dev else "default"
pipfile_section = "dev-packages" if dev else "packages"
self.dev = dev
self.pipfile = project.parsed_pipfile.get(pipfile_section, {})
self.pipfile = tomlkit_value_to_python(
project.parsed_pipfile.get(pipfile_section, {})
)
self.lockfile = project.lockfile_content.get(section, {})
self.pipfile_dict = self.pipfile.get(self.pipfile_name, {})
if self.dev and self.name in project.lockfile_content.get("default", {}):
Expand Down
2 changes: 1 addition & 1 deletion pipenv/vendor/pip_shims/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import sys

__version__ = '0.3.3'
__version__ = "0.3.3"

from . import shims

Expand Down
45 changes: 41 additions & 4 deletions pipenv/vendor/pip_shims/shims.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@


class _shims(object):
CURRENT_PIP_VERSION = "19.0.3"
CURRENT_PIP_VERSION = "19.1.1"
BASE_IMPORT_PATH = os.environ.get("PIP_SHIMS_BASE_MODULE", "pip")
path_info = namedtuple("PathInfo", "path start_version end_version")

Expand Down Expand Up @@ -141,14 +141,25 @@ def __init__(self):
),
"Link": ("index.Link", "7.0.0", "9999"),
"make_abstract_dist": (
("operations.prepare.make_abstract_dist", "10.0.0", "9999"),
(
"distributions.make_distribution_for_install_requirement",
"19.1.2",
"9999",
),
("operations.prepare.make_abstract_dist", "10.0.0", "19.1.1"),
("req.req_set.make_abstract_dist", "7.0.0", "9.0.3"),
),
"make_distribution_for_install_requirement": (
"distributions.make_distribution_for_install_requirement",
"19.1.2",
"9999",
),
"make_option_group": (
("cli.cmdoptions.make_option_group", "18.1", "9999"),
("cmdoptions.make_option_group", "7.0.0", "18.0"),
),
"PackageFinder": ("index.PackageFinder", "7.0.0", "9999"),
"CandidateEvaluator": ("index.CandidateEvaluator", "19.1", "9999"),
"parse_requirements": ("req.req_file.parse_requirements", "7.0.0", "9999"),
"path_to_url": ("download.path_to_url", "7.0.0", "9999"),
"PipError": ("exceptions.PipError", "7.0.0", "9999"),
Expand All @@ -159,18 +170,44 @@ def __init__(self):
),
"RequirementSet": ("req.req_set.RequirementSet", "7.0.0", "9999"),
"RequirementTracker": ("req.req_tracker.RequirementTracker", "7.0.0", "9999"),
"Resolver": ("resolve.Resolver", "7.0.0", "9999"),
"Resolver": (
("resolve.Resolver", "7.0.0", "19.1.1"),
("legacy_resolve.Resolver", "19.1.2", "9999"),
),
"SafeFileCache": ("download.SafeFileCache", "7.0.0", "9999"),
"UninstallPathSet": ("req.req_uninstall.UninstallPathSet", "7.0.0", "9999"),
"url_to_path": ("download.url_to_path", "7.0.0", "9999"),
"USER_CACHE_DIR": ("locations.USER_CACHE_DIR", "7.0.0", "9999"),
"VcsSupport": ("vcs.VcsSupport", "7.0.0", "9999"),
"VcsSupport": (
("vcs.VcsSupport", "7.0.0", "19.1.1"),
("vcs.versioncontrol.VcsSupport", "19.2", "9999"),
),
"Wheel": ("wheel.Wheel", "7.0.0", "9999"),
"WheelCache": (
("cache.WheelCache", "10.0.0", "9999"),
("wheel.WheelCache", "7", "9.0.3"),
),
"WheelBuilder": ("wheel.WheelBuilder", "7.0.0", "9999"),
"AbstractDistribution": (
"distributions.base.AbstractDistribution",
"19.1.2",
"9999",
),
"InstalledDistribution": (
"distributions.installed.InstalledDistribution",
"19.1.2",
"9999",
),
"SourceDistribution": (
("req.req_set.IsSDist", "7.0.0", "9.0.3"),
("operations.prepare.IsSDist", "10.0.0", "19.1.1"),
("distributions.source.SourceDistribution", "19.1.2", "9999"),
),
"WheelDistribution": (
"distributions.wheel.WheelDistribution",
"19.1.2",
"9999",
),
"PyPI": ("models.index.PyPI", "7.0.0", "9999"),
"stdlib_pkgs": (
("utils.compat.stdlib_pkgs", "18.1", "9999"),
Expand Down
Loading