Skip to content

Commit

Permalink
Deprecate requirements format "base>=1.0[extra]"
Browse files Browse the repository at this point in the history
This requirements format does not conform to PEP-508. Currently the
extras specified like this work by accident (because _strip_extras()
also parses them). The version checks end up being done with a
misparsed version '1.0[extra]' -- this is not changed in this commit.

Add deprecation warning and fix the corresponding resolver test. Add a
command line test.

Note that we really only check that the Requirement has SpecifierSet
with a specifier that ends in a ']'. A valid version number cannot
contain ']' and no wheels currently on pypi have versions ending in ']'.
  • Loading branch information
Jussi Kukkonen committed Jul 13, 2020
1 parent 8bf5731 commit 76b20d7
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 2 deletions.
1 change: 1 addition & 0 deletions news/8288.removal
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add deprecation warning for invalid requirements format "base>=1.0[extra]"
12 changes: 12 additions & 0 deletions src/pip/_internal/req/constructors.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from pip._internal.models.wheel import Wheel
from pip._internal.pyproject import make_pyproject_path
from pip._internal.req.req_install import InstallRequirement
from pip._internal.utils.deprecation import deprecated
from pip._internal.utils.filetypes import ARCHIVE_EXTENSIONS
from pip._internal.utils.misc import is_installable_dir, splitext
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
Expand Down Expand Up @@ -370,6 +371,17 @@ def with_source(text):
if add_msg:
msg += '\nHint: {}'.format(add_msg)
raise InstallationError(msg)
else:
# Deprecate extras after specifiers: "name>=1.0[extras]"
# This currently works by accident because _strip_extras() parses
# any extras in the end of the string and those are saved in
# RequirementParts
for spec in req.specifier:
spec_str = str(spec)
if spec_str.endswith(']'):
msg = "Extras after version '{}'.".format(spec_str)
replace = "moving the extras before version specifiers"
deprecated(msg, replacement=replace, gone_in="21.0")
else:
req = None

Expand Down
21 changes: 21 additions & 0 deletions tests/functional/test_install_extras.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,27 @@ def test_nonexistent_options_listed_in_order(script, data):
assert matches == ['nonexistent', 'nope']


def test_install_deprecated_extra(script, data):
"""
Warn about deprecated order of specifiers and extras.
Test uses a requirements file to avoid a testing issue where
the specifier gets interpreted as shell redirect.
"""
script.scratch_path.joinpath("requirements.txt").write_text(
"requires_simple_extra>=0.1[extra]"
)
simple = script.site_packages / 'simple'

result = script.pip(
'install', '--no-index', '--find-links=' + data.find_links,
'-r', script.scratch_path / 'requirements.txt', expect_stderr=True,
)

result.did_create(simple)
assert ("DEPRECATION: Extras after version" in result.stderr)


def test_install_special_extra(script):
# Check that uppercase letters and '-' are dealt with
# make a dummy project
Expand Down
28 changes: 26 additions & 2 deletions tests/functional/test_new_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,6 @@ def test_new_resolver_ignore_dependencies(script):
[
"base[add]",
"base[add] >= 0.1.0",
# Non-standard syntax. To deprecate, see pypa/pip#8288.
"base >= 0.1.0[add]",
],
)
def test_new_resolver_installs_extras(tmpdir, script, root_dep):
Expand All @@ -228,6 +226,32 @@ def test_new_resolver_installs_extras(tmpdir, script, root_dep):
assert_installed(script, base="0.1.0", dep="0.1.0")


def test_new_resolver_installs_extras_deprecated(tmpdir, script):
req_file = tmpdir.joinpath("requirements.txt")
req_file.write_text("base >= 0.1.0[add]")

create_basic_wheel_for_package(
script,
"base",
"0.1.0",
extras={"add": ["dep"]},
)
create_basic_wheel_for_package(
script,
"dep",
"0.1.0",
)
result = script.pip(
"install", "--use-feature=2020-resolver",
"--no-cache-dir", "--no-index",
"--find-links", script.scratch_path,
"-r", req_file,
expect_stderr=True
)
assert "DEPRECATION: Extras after version" in result.stderr
assert_installed(script, base="0.1.0", dep="0.1.0")


def test_new_resolver_installs_extras_warn_missing(script):
create_basic_wheel_for_package(
script,
Expand Down

0 comments on commit 76b20d7

Please sign in to comment.