lint_requirements.py 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445
  1. from __future__ import annotations
  2. import argparse
  3. from collections.abc import Sequence
  4. import packaging.requirements
  5. def main(argv: Sequence[str] | None = None) -> int:
  6. """
  7. We cannot have non-specifier requirements if we want to publish to PyPI
  8. due to security concerns. This check ensures we don't have/add any URL/VCS
  9. dependencies in the base requirements file.
  10. """
  11. parser = argparse.ArgumentParser()
  12. parser.add_argument("filenames", nargs="*")
  13. args = parser.parse_args(argv)
  14. for filename in args.filenames:
  15. with open(filename) as reqs_file:
  16. for lineno, line in enumerate(reqs_file, start=1):
  17. line = line.strip()
  18. if not line or line.startswith(("--", "#")):
  19. continue
  20. invalid_requirement = False
  21. try:
  22. req = packaging.requirements.Requirement(line)
  23. except packaging.requirements.InvalidRequirement:
  24. invalid_requirement = True
  25. else:
  26. invalid_requirement = bool(req.url)
  27. if invalid_requirement:
  28. raise SystemExit(
  29. f"You cannot use dependencies that are not on PyPI directly.\n"
  30. f"See PEP440: https://www.python.org/dev/peps/pep-0440/#direct-references\n\n"
  31. f"{reqs_file.name}:{lineno}: {line}"
  32. )
  33. return 0
  34. if __name__ == "__main__":
  35. raise SystemExit(main())