Repository: pypa/pip (Python)

Issue:
Remove undocumented option --skip-requirements-regex
**What's the problem this feature will solve?**

Extra undocumented command-line argument/configuration parameter potentially complicating debugging and code refactoring.

**Describe the solution you'd like**

Deprecation of `--skip-requirements-regex` and then removal in 20.1.

**Alternative Solutions**

* Document this option and fully support it - In general we're moving away from these kind of workarounds - if this were suggested today I do not think it would be given much consideration
* Remove the option with no deprecation period - not preferred after some discussion in #7188

**Additional context**

* #7188
* #6221
* No mentions of this option online except dependabot/feedback#252 or copies of the pip source code


Discussion:

Patch:
diff --git a/news/7297.removal b/news/7297.removal
new file mode 100644
index 00000000000..332451f2541
--- /dev/null
+++ b/news/7297.removal
@@ -0,0 +1,1 @@
+Remove deprecated ``--skip-requirements-regex`` option.
diff --git a/src/pip/_internal/cli/base_command.py b/src/pip/_internal/cli/base_command.py
index c3087c8b178..722522110aa 100644
--- a/src/pip/_internal/cli/base_command.py
+++ b/src/pip/_internal/cli/base_command.py
@@ -153,17 +153,6 @@ def _main(self, args):
                 ) + message
             deprecated(message, replacement=None, gone_in=None)
 
-        if options.skip_requirements_regex:
-            deprecated(
-                "--skip-requirements-regex is unsupported and will be removed",
-                replacement=(
-                    "manage requirements/constraints files explicitly, "
-                    "possibly generating them from metadata"
-                ),
-                gone_in="20.1",
-                issue=7297,
-            )
-
         # TODO: Try to get these passing down from the command?
         #       without resorting to os.environ to hold these.
         #       This also affects isolated builds and it should.
diff --git a/src/pip/_internal/cli/cmdoptions.py b/src/pip/_internal/cli/cmdoptions.py
index ff9acfd4644..5b5647d64f4 100644
--- a/src/pip/_internal/cli/cmdoptions.py
+++ b/src/pip/_internal/cli/cmdoptions.py
@@ -275,16 +275,6 @@ class PipOption(Option):
     help='Set the socket timeout (default %default seconds).',
 )  # type: Callable[..., Option]
 
-skip_requirements_regex = partial(
-    Option,
-    # A regex to be used to skip requirements
-    '--skip-requirements-regex',
-    dest='skip_requirements_regex',
-    type='str',
-    default='',
-    help=SUPPRESS_HELP,
-)  # type: Callable[..., Option]
-
 
 def exists_action():
     # type: () -> Option
@@ -948,7 +938,6 @@ def check_list_path_option(options):
         proxy,
         retries,
         timeout,
-        skip_requirements_regex,
         exists_action,
         trusted_host,
         cert,
diff --git a/src/pip/_internal/commands/freeze.py b/src/pip/_internal/commands/freeze.py
index 4758e30343f..13171772e5c 100644
--- a/src/pip/_internal/commands/freeze.py
+++ b/src/pip/_internal/commands/freeze.py
@@ -89,7 +89,6 @@ def run(self, options, args):
             local_only=options.local,
             user_only=options.user,
             paths=options.path,
-            skip_regex=options.skip_requirements_regex,
             isolated=options.isolated_mode,
             wheel_cache=wheel_cache,
             skip=skip,
diff --git a/src/pip/_internal/operations/freeze.py b/src/pip/_internal/operations/freeze.py
index 3198c775771..aa6b052b6aa 100644
--- a/src/pip/_internal/operations/freeze.py
+++ b/src/pip/_internal/operations/freeze.py
@@ -7,7 +7,6 @@
 import collections
 import logging
 import os
-import re
 
 from pip._vendor import six
 from pip._vendor.packaging.utils import canonicalize_name
@@ -50,7 +49,6 @@ def freeze(
     local_only=None,  # type: Optional[bool]
     user_only=None,  # type: Optional[bool]
     paths=None,  # type: Optional[List[str]]
-    skip_regex=None,  # type: Optional[str]
     isolated=False,  # type: bool
     wheel_cache=None,  # type: Optional[WheelCache]
     exclude_editable=False,  # type: bool
@@ -58,10 +56,6 @@ def freeze(
 ):
     # type: (...) -> Iterator[str]
     find_links = find_links or []
-    skip_match = None
-
-    if skip_regex:
-        skip_match = re.compile(skip_regex).search
 
     for link in find_links:
         yield '-f {}'.format(link)
@@ -100,7 +94,6 @@ def freeze(
                 for line in req_file:
                     if (not line.strip() or
                             line.strip().startswith('#') or
-                            (skip_match and skip_match(line)) or
                             line.startswith((
                                 '-r', '--requirement',
                                 '-Z', '--always-unzip',
diff --git a/src/pip/_internal/req/req_file.py b/src/pip/_internal/req/req_file.py
index 2501e7c25fd..63cab76f6f2 100644
--- a/src/pip/_internal/req/req_file.py
+++ b/src/pip/_internal/req/req_file.py
@@ -13,7 +13,6 @@
 import shlex
 import sys
 
-from pip._vendor.six.moves import filterfalse
 from pip._vendor.six.moves.urllib import parse as urllib_parse
 
 from pip._internal.cli import cmdoptions
@@ -146,13 +145,8 @@ def parse_requirements(
     :param constraint:  If true, parsing a constraint file rather than
         requirements file.
     """
-    skip_requirements_regex = (
-        options.skip_requirements_regex if options else None
-    )
     line_parser = get_line_parser(finder)
-    parser = RequirementsFileParser(
-        session, line_parser, comes_from, skip_requirements_regex
-    )
+    parser = RequirementsFileParser(session, line_parser, comes_from)
 
     for parsed_line in parser.parse(filename, constraint):
         parsed_req = handle_line(
@@ -165,18 +159,15 @@ def parse_requirements(
             yield parsed_req
 
 
-def preprocess(content, skip_requirements_regex):
-    # type: (Text, Optional[str]) -> ReqFileLines
+def preprocess(content):
+    # type: (Text) -> ReqFileLines
     """Split, filter, and join lines, and return a line iterator
 
     :param content: the content of the requirements file
-    :param skip_requirements_regex: the pattern to skip lines
     """
     lines_enum = enumerate(content.splitlines(), start=1)  # type: ReqFileLines
     lines_enum = join_lines(lines_enum)
     lines_enum = ignore_comments(lines_enum)
-    if skip_requirements_regex:
-        lines_enum = skip_regex(lines_enum, skip_requirements_regex)
     lines_enum = expand_env_variables(lines_enum)
     return lines_enum
 
@@ -326,13 +317,11 @@ def __init__(
         session,  # type: PipSession
         line_parser,  # type: LineParser
         comes_from,  # type: str
-        skip_requirements_regex,  # type: Optional[str]
     ):
         # type: (...) -> None
         self._session = session
         self._line_parser = line_parser
         self._comes_from = comes_from
-        self._skip_requirements_regex = skip_requirements_regex
 
     def parse(self, filename, constraint):
         # type: (str, bool) -> Iterator[ParsedLine]
@@ -380,7 +369,7 @@ def _parse_file(self, filename, constraint):
             filename, self._session, comes_from=self._comes_from
         )
 
-        lines_enum = preprocess(content, self._skip_requirements_regex)
+        lines_enum = preprocess(content)
 
         for line_number, line in lines_enum:
             try:
@@ -517,18 +506,6 @@ def ignore_comments(lines_enum):
             yield line_number, line
 
 
-def skip_regex(lines_enum, pattern):
-    # type: (ReqFileLines, str) -> ReqFileLines
-    """
-    Skip lines that match the provided pattern
-
-    Note: the regex pattern is only built once
-    """
-    matcher = re.compile(pattern)
-    lines_enum = filterfalse(lambda e: matcher.search(e[1]), lines_enum)
-    return lines_enum
-
-
 def expand_env_variables(lines_enum):
     # type: (ReqFileLines) -> ReqFileLines
     """Replace all environment variables that can be retrieved via `os.getenv`.


Test patch:
diff --git a/tests/unit/test_options.py b/tests/unit/test_options.py
index 534bc92afc9..950075ba435 100644
--- a/tests/unit/test_options.py
+++ b/tests/unit/test_options.py
@@ -359,12 +359,6 @@ def test_timeout(self):
         options2, args2 = main(['fake', '--timeout', '-1'])
         assert options1.timeout == options2.timeout == -1
 
-    def test_skip_requirements_regex(self):
-        options1, args1 = main(['--skip-requirements-regex', 'path', 'fake'])
-        options2, args2 = main(['fake', '--skip-requirements-regex', 'path'])
-        assert options1.skip_requirements_regex == 'path'
-        assert options2.skip_requirements_regex == 'path'
-
     def test_exists_action(self):
         options1, args1 = main(['--exists-action', 'w', 'fake'])
         options2, args2 = main(['fake', '--exists-action', 'w'])
diff --git a/tests/unit/test_req_file.py b/tests/unit/test_req_file.py
index 57b50017a7f..4ef4f7217e0 100644
--- a/tests/unit/test_req_file.py
+++ b/tests/unit/test_req_file.py
@@ -27,7 +27,6 @@
     join_lines,
     parse_requirements,
     preprocess,
-    skip_regex,
 )
 from tests.lib import make_test_finder, requirements_file
 
@@ -45,9 +44,10 @@ def finder(session):
 @pytest.fixture
 def options(session):
     return stub(
-        isolated_mode=False, index_url='default_url',
-        skip_requirements_regex=False,
-        format_control=FormatControl(set(), set()))
+        isolated_mode=False,
+        index_url='default_url',
+        format_control=FormatControl(set(), set()),
+    )
 
 
 def parse_reqfile(
@@ -79,7 +79,7 @@ def test_comments_and_joins_case1(self):
           # comment \\
           req2
         """)
-        result = preprocess(content, None)
+        result = preprocess(content)
         assert list(result) == [(1, 'req1'), (3, 'req2')]
 
     def test_comments_and_joins_case2(self):
@@ -87,7 +87,7 @@ def test_comments_and_joins_case2(self):
           req1\\
           # comment
         """)
-        result = preprocess(content, None)
+        result = preprocess(content)
         assert list(result) == [(1, 'req1')]
 
     def test_comments_and_joins_case3(self):
@@ -96,29 +96,9 @@ def test_comments_and_joins_case3(self):
           # comment
           req2
         """)
-        result = preprocess(content, None)
+        result = preprocess(content)
         assert list(result) == [(1, 'req1'), (3, 'req2')]
 
-    def test_skip_regex_after_joining_case1(self, options):
-        content = textwrap.dedent("""\
-          patt\\
-          ern
-          line2
-        """)
-        skip_requirements_regex = 'pattern'
-        result = preprocess(content, skip_requirements_regex)
-        assert list(result) == [(3, 'line2')]
-
-    def test_skip_regex_after_joining_case2(self, options):
-        content = textwrap.dedent("""\
-          pattern \\
-          line2
-          line3
-        """)
-        skip_requirements_regex = 'pattern'
-        result = preprocess(content, skip_requirements_regex)
-        assert list(result) == [(3, 'line3')]
-
 
 class TestIgnoreComments(object):
     """tests for `ignore_comment`"""
@@ -172,25 +152,6 @@ def test_last_line_with_escape(self):
         assert expect == list(join_lines(lines))
 
 
-class TestSkipRegex(object):
-    """tests for `skip_reqex``"""
-
-    def test_skip_regex_pattern_match(self):
-        pattern = '.*Bad.*'
-        line = '--extra-index-url Bad'
-        assert [] == list(skip_regex(enumerate([line]), pattern))
-
-    def test_skip_regex_pattern_not_match(self):
-        pattern = '.*Bad.*'
-        line = '--extra-index-url Good'
-        assert [(0, line)] == list(skip_regex(enumerate([line]), pattern))
-
-    def test_skip_regex_no_options(self):
-        pattern = None
-        line = '--extra-index-url Good'
-        assert [(1, line)] == list(preprocess(line, pattern))
-
-
 @pytest.fixture
 def line_processor(
     monkeypatch,
@@ -616,17 +577,6 @@ def test_multiple_appending_options(self, tmpdir, finder, options):
 
         assert finder.index_urls == ['url1', 'url2']
 
-    def test_skip_regex(self, tmpdir, finder, options):
-        options.skip_requirements_regex = '.*Bad.*'
-        with open(tmpdir.joinpath("req1.txt"), "w") as fp:
-            fp.write("--extra-index-url Bad \n")
-            fp.write("--extra-index-url Good ")
-
-        list(parse_reqfile(tmpdir.joinpath("req1.txt"), finder=finder,
-                           options=options, session=PipSession()))
-
-        assert finder.index_urls == ['Good']
-
     def test_expand_existing_env_variables(self, tmpdir, finder):
         template = (
             'https://{}:x-oauth-basic@github.com/'