Просмотр исходного кода

ref: forbid other forms of git urls in requirements (#42220)

for example:

```
lint-requirements........................................................Failed
- hook id: lint-requirements
- exit code: 1

You cannot use dependencies that are not on PyPI directly.
See PEP440: https://www.python.org/dev/peps/pep-0440/#direct-references

requirements-frozen.txt:110: sentry-arroyo @ git+https://github.com/getsentry/arroyo@2.2.0

```
anthony sottile 2 лет назад
Родитель
Сommit
0599a01715
3 измененных файлов с 86 добавлено и 19 удалено
  1. 3 3
      .pre-commit-config.yaml
  2. 49 0
      tests/tools/test_lint_requirements.py
  3. 34 16
      tools/lint_requirements.py

+ 3 - 3
.pre-commit-config.yaml

@@ -49,9 +49,9 @@ repos:
     - id: lint-requirements
       name: lint-requirements
       entry: python -m tools.lint_requirements
-      language: system
-      files: requirements-frozen.txt
-      pass_filenames: false
+      language: python
+      files: requirements-.*\.txt$
+      additional_dependencies: [packaging==21.3]
     - id: pyright
       name: pyright
       entry: pyright

+ 49 - 0
tests/tools/test_lint_requirements.py

@@ -0,0 +1,49 @@
+import pytest
+
+from tools import lint_requirements
+
+
+def test_ok(tmp_path):
+    f = tmp_path.joinpath("f.txt")
+    f.write_text(
+        "# allow comments\n"
+        "# and allow pip settings\n"
+        "--index-url https://pypi.devinfra.sentry.io/simple\n"
+        "a==1\n"
+        "b==2\n"
+    )
+    assert lint_requirements.main((str(f),)) == 0
+
+
+def test_not_ok_classic_git_url(tmp_path):
+    f = tmp_path.joinpath("f.txt")
+    f.write_text("git+https://github.com/asottile/astpretty@3.0.0#egg=astpretty")
+
+    with pytest.raises(SystemExit) as excinfo:
+        lint_requirements.main((str(f),))
+
+    expected = f"""\
+You cannot use dependencies that are not on PyPI directly.
+See PEP440: https://www.python.org/dev/peps/pep-0440/#direct-references
+
+{f}:1: git+https://github.com/asottile/astpretty@3.0.0#egg=astpretty
+"""
+    (msg,) = excinfo.value.args
+    assert msg == expected.rstrip()
+
+
+def test_not_ok_new_style_git_url(tmp_path):
+    f = tmp_path.joinpath("f.txt")
+    f.write_text("astpretty @ git+https://github.com/asottile/astpretty@3.0.0")
+
+    with pytest.raises(SystemExit) as excinfo:
+        lint_requirements.main((str(f),))
+
+    expected = f"""\
+You cannot use dependencies that are not on PyPI directly.
+See PEP440: https://www.python.org/dev/peps/pep-0440/#direct-references
+
+{f}:1: astpretty @ git+https://github.com/asottile/astpretty@3.0.0
+"""
+    (msg,) = excinfo.value.args
+    assert msg == expected.rstrip()

+ 34 - 16
tools/lint_requirements.py

@@ -1,27 +1,45 @@
+from __future__ import annotations
+
+import argparse
+from typing import Sequence
+
 import packaging.requirements
 
 
-def main() -> None:
+def main(argv: Sequence[str] | None = None) -> int:
     """
     We cannot have non-specifier requirements if we want to publish to PyPI
     due to security concerns. This check ensures we don't have/add any URL/VCS
     dependencies in the base requirements file.
     """
-    with open("requirements-frozen.txt") as reqs_file:
-        for lineno, line in enumerate(reqs_file, start=1):
-            line = line.strip()
-            if not line or line.startswith(("--", "#")):
-                continue
-
-            try:
-                packaging.requirements.Requirement(line)
-            except packaging.requirements.InvalidRequirement:
-                raise SystemExit(
-                    f"You cannot use dependencies that are not on PyPI directly.\n"
-                    f"See PEP440: https://www.python.org/dev/peps/pep-0440/#direct-references\n\n"
-                    f"{reqs_file.name}:{lineno}: {line}"
-                )
+    parser = argparse.ArgumentParser()
+    parser.add_argument("filenames", nargs="*")
+    args = parser.parse_args(argv)
+
+    for filename in args.filenames:
+        with open(filename) as reqs_file:
+            for lineno, line in enumerate(reqs_file, start=1):
+                line = line.strip()
+                if not line or line.startswith(("--", "#")):
+                    continue
+
+                invalid_requirement = False
+                try:
+                    req = packaging.requirements.Requirement(line)
+                except packaging.requirements.InvalidRequirement:
+                    invalid_requirement = True
+                else:
+                    invalid_requirement = bool(req.url)
+
+                if invalid_requirement:
+                    raise SystemExit(
+                        f"You cannot use dependencies that are not on PyPI directly.\n"
+                        f"See PEP440: https://www.python.org/dev/peps/pep-0440/#direct-references\n\n"
+                        f"{reqs_file.name}:{lineno}: {line}"
+                    )
+
+    return 0
 
 
 if __name__ == "__main__":
-    main()
+    raise SystemExit(main())