Skip to content

Commit a0b03ef

Browse files
committed
* Dropped support for Python 3.8 as newer versions of dependencies have
already done so, and 3.8 has been EoL for some time * Included tomli for toml parsing support (see #55) * Updated various dependencies and pre-commit hooks
1 parent 6709dbb commit a0b03ef

28 files changed

+414
-872
lines changed

.github/workflows/ci.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ jobs:
115115
runs-on: ubuntu-latest
116116
strategy:
117117
matrix:
118-
python-version: [3.8, 3.9, "3.10", "3.11", "3.12"]
118+
python-version: [3.9, "3.10", "3.11", "3.12", "3.13"]
119119
outputs:
120120
python-key: ${{ steps.generate-python-key.outputs.key }}
121121
steps:
@@ -159,7 +159,7 @@ jobs:
159159
strategy:
160160
fail-fast: false
161161
matrix:
162-
python-version: [3.8, 3.9, "3.10", "3.11", "3.12"]
162+
python-version: [3.9, "3.10", "3.11", "3.12", "3.13"]
163163
steps:
164164
- name: Check out code from GitHub
165165
uses: actions/checkout@v3

.pre-commit-config.yaml

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,41 @@
1-
fail_fast: true
1+
# https://pre-commit.com/
22
repos:
3-
- repo: https://github.com/pre-commit/pre-commit-hooks
4-
rev: v4.4.0
3+
- repo: https://github.com/pre-commit/pre-commit-hooks
4+
rev: v5.0.0
55
hooks:
6-
- id: trailing-whitespace
7-
- id: end-of-file-fixer
8-
- id: mixed-line-ending
6+
# tidy up extra whitespace at end of files
7+
- id: trailing-whitespace
8+
# ensure consistent newline endings for files (\n only)
9+
- id: end-of-file-fixer
10+
# fix newlines in general to prevent pesky Windows users writing CRLF
11+
- id: mixed-line-ending
912
args: [ --fix=lf ]
10-
- id: check-added-large-files
11-
- repo: https://github.com/psf/black
12-
rev: 23.3.0
13+
# do not allow pdb/breakpoint statements to be checked in
14+
- id: debug-statements
15+
# validate syntax of some file types
16+
- id: check-yaml
17+
- id: check-toml
18+
# prevent large files being added
19+
- id: check-added-large-files
20+
21+
# Code formatting and linting
22+
- repo: https://github.com/tox-dev/pyproject-fmt
23+
rev: "v2.5.1"
1324
hooks:
14-
- id: black
15-
args:
16-
- --force-exclude=tests/
17-
- --line-length=120
18-
- repo: https://github.com/PyCQA/isort
19-
rev: 5.12.0
25+
- id: pyproject-fmt
26+
- repo: https://github.com/pycqa/flake8
27+
rev: 7.2.0
2028
hooks:
21-
- id: isort
22-
args: ["--profile", "black", "--filter-files"]
29+
- id: flake8
30+
args: [--max-line-length=120]
31+
- repo: https://github.com/python/black
32+
rev: 25.1.0
33+
hooks:
34+
- id: black
35+
- repo: https://github.com/PyCQA/isort
36+
rev: 6.0.1
37+
hooks:
38+
- id: isort
39+
exclude: migrations
40+
args: [ "--profile", "black", "--filter-files" ]
41+
exclude: "tests/.*"

poetry.lock

Lines changed: 139 additions & 747 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,31 @@
1+
[build-system]
2+
build-backend = "poetry.core.masonry.api"
3+
requires = [ "poetry-core>=1" ]
4+
15
[tool.poetry]
26
name = "requirements-detector"
3-
version = "1.3.2"
4-
authors = ["Landscape.io <[email protected]>"]
7+
version = "1.4.0"
8+
authors = [ "Landscape.io <[email protected]>" ]
59
classifiers = [
6-
'Development Status :: 5 - Production/Stable',
7-
'Environment :: Console',
8-
'Intended Audience :: Developers',
9-
'Operating System :: Unix',
10-
'Topic :: Software Development :: Quality Assurance',
11-
'Programming Language :: Python :: 3.8',
12-
'Programming Language :: Python :: 3.9',
13-
'Programming Language :: Python :: 3.10',
14-
'Programming Language :: Python :: 3.11',
15-
'Programming Language :: Python :: 3.12',
16-
'License :: OSI Approved :: MIT License',
10+
'Development Status :: 5 - Production/Stable',
11+
'Environment :: Console',
12+
'Intended Audience :: Developers',
13+
'Operating System :: Unix',
14+
'Topic :: Software Development :: Quality Assurance',
15+
'Programming Language :: Python :: 3.9',
16+
'Programming Language :: Python :: 3.10',
17+
'Programming Language :: Python :: 3.11',
18+
'Programming Language :: Python :: 3.12',
19+
'Programming Language :: Python :: 3.13',
20+
'License :: OSI Approved :: MIT License',
1721
]
1822
license = "MIT"
19-
keywords = ["python","requirements detector"]
23+
keywords = [ "python", "requirements detector" ]
2024
description = "Python tool to find and list requirements of a Python project"
2125
readme = "README.md"
2226
homepage = "https://github.com/landscapeio/requirements-detector"
2327
packages = [
24-
{ include = "requirements_detector/"}
28+
{ include = "requirements_detector/" },
2529
]
2630
include = [
2731
"c2cgeoform/py.typed",
@@ -31,22 +35,17 @@ include = [
3135
detect-requirements = 'requirements_detector.run:run'
3236

3337
[tool.poetry.dependencies]
34-
python = ">=3.8,<4.0"
38+
python = ">=3.9,<4.0"
3539
astroid = "^3.0"
3640
packaging = ">=21.3"
37-
toml = {version = "^0.10.2", python = "<3.11"}
41+
tomli = { version = "^2.2.1", python = "<3.11" }
3842
semver = "^3.0.0"
3943

4044
[tool.poetry.dev-dependencies]
4145
tox = "^3.24.5"
42-
pre-commit = "^2.17.0"
46+
pre-commit = "^4.2.0"
4347
pytest = "^6.2.4"
4448
coverage = "^5.5"
45-
twine = "^3.8.0"
4649
pytest-benchmark = "^3.4.1"
4750
pytest-cov = "^2.12.1"
4851
types-toml = "^0.10.4"
49-
50-
[build-system]
51-
requires = ["poetry-core>=1.0.0"]
52-
build-backend = "poetry.core.masonry.api"

requirements_detector/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,14 @@
88
from_requirements_txt,
99
from_setup_py,
1010
)
11+
12+
__all__ = [
13+
"CouldNotParseRequirements",
14+
"RequirementsNotFound",
15+
"find_requirements",
16+
"from_pyproject_toml",
17+
"from_requirements_blob",
18+
"from_requirements_dir",
19+
"from_requirements_txt",
20+
"from_setup_py",
21+
]

requirements_detector/detect.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ def find_requirements(path: P) -> List[DetectedRequirement]:
8787
if reqfile.exists and reqfile.is_file():
8888
try:
8989
requirements += from_requirements_txt(reqfile)
90-
except CouldNotParseRequirements as e:
90+
except CouldNotParseRequirements:
9191
pass
9292

9393
requirements_dir = path / "requirements"
@@ -154,7 +154,12 @@ def from_pyproject_toml(toml_file: P) -> List[DetectedRequirement]:
154154
continue
155155
assert parsed_spec_obj is not None
156156
parsed_spec = str(parsed_spec_obj)
157-
if "," not in parsed_spec and "<" not in parsed_spec and ">" not in parsed_spec and "=" not in parsed_spec:
157+
if (
158+
"," not in parsed_spec
159+
and "<" not in parsed_spec
160+
and ">" not in parsed_spec
161+
and "=" not in parsed_spec
162+
):
158163
parsed_spec = f"=={parsed_spec}"
159164

160165
req = DetectedRequirement.parse(f"{name}{parsed_spec}", toml_file)

requirements_detector/poetry_semver/__init__.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import re
22

3-
from .empty_constraint import EmptyConstraint
43
from .patterns import (
54
BASIC_CONSTRAINT,
65
CARET_CONSTRAINT,
@@ -23,7 +22,9 @@ def parse_constraint(constraints: str) -> VersionConstraint:
2322
or_constraints = re.split(r"\s*\|\|?\s*", constraints.strip())
2423
or_groups = []
2524
for constraints in or_constraints:
26-
and_constraints = re.split("(?<!^)(?<![=>< ,]) *(?<!-)[, ](?!-) *(?!,|$)", constraints)
25+
and_constraints = re.split(
26+
"(?<!^)(?<![=>< ,]) *(?<!-)[, ](?!-) *(?!,|$)", constraints
27+
)
2728
constraint_objects = []
2829

2930
if len(and_constraints) > 1:
@@ -61,7 +62,9 @@ def parse_single_constraint(constraint: str) -> VersionConstraint:
6162
if len(m.group(1).split(".")) == 1:
6263
high = version.stable.next_major
6364

64-
return VersionRange(version, high, include_min=True, always_include_max_prerelease=True)
65+
return VersionRange(
66+
version, high, include_min=True, always_include_max_prerelease=True
67+
)
6568

6669
# PEP 440 Tilde range (~=)
6770
m = TILDE_PEP440_CONSTRAINT.match(constraint)
@@ -82,7 +85,9 @@ def parse_single_constraint(constraint: str) -> VersionConstraint:
8285
low = Version(version.major, version.minor, version.patch)
8386
high = version.stable.next_minor
8487

85-
return VersionRange(low, high, include_min=True, always_include_max_prerelease=True)
88+
return VersionRange(
89+
low, high, include_min=True, always_include_max_prerelease=True
90+
)
8691

8792
# Caret range
8893
m = CARET_CONSTRAINT.match(constraint)
@@ -142,7 +147,9 @@ def parse_single_constraint(constraint: str) -> VersionConstraint:
142147
try:
143148
version = Version.parse(version)
144149
except ValueError:
145-
raise ValueError("Could not parse version constraint: {}".format(constraint))
150+
raise ValueError(
151+
"Could not parse version constraint: {}".format(constraint)
152+
)
146153

147154
if op == "<":
148155
return VersionRange(max=version)

requirements_detector/poetry_semver/patterns.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@
66
r"([+-]?([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?"
77
)
88

9-
_COMPLETE_VERSION = r"v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?{}(?:\+[^\s]+)?".format(MODIFIERS)
9+
_COMPLETE_VERSION = (
10+
r"v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?{}(?:\+[^\s]+)?".format(MODIFIERS)
11+
)
1012

1113
COMPLETE_VERSION = re.compile("(?i)" + _COMPLETE_VERSION)
1214

1315
CARET_CONSTRAINT = re.compile(r"(?i)^\^({})$".format(_COMPLETE_VERSION))
1416
TILDE_CONSTRAINT = re.compile("(?i)^~(?!=)({})$".format(_COMPLETE_VERSION))
1517
TILDE_PEP440_CONSTRAINT = re.compile("(?i)^~=({})$".format(_COMPLETE_VERSION))
1618
X_CONSTRAINT = re.compile(r"^(!=|==)?\s*v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.[xX*])+$")
17-
BASIC_CONSTRAINT = re.compile(r"(?i)^(<>|!=|>=?|<=?|==?)?\s*({}|dev)".format(_COMPLETE_VERSION))
19+
BASIC_CONSTRAINT = re.compile(
20+
r"(?i)^(<>|!=|>=?|<=?|==?)?\s*({}|dev)".format(_COMPLETE_VERSION)
21+
)

requirements_detector/poetry_semver/version.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,9 @@ def next_breaking(self) -> "Version":
168168

169169
@property
170170
def first_prerelease(self) -> "Version":
171-
return Version.parse("{}.{}.{}-alpha.0".format(self.major, self.minor, self.patch))
171+
return Version.parse(
172+
"{}.{}.{}-alpha.0".format(self.major, self.minor, self.patch)
173+
)
172174

173175
@property
174176
def min(self):
@@ -271,7 +273,11 @@ def difference(self, other: VersionConstraint) -> VersionConstraint:
271273
return self
272274

273275
def equals_without_prerelease(self, other: "Version") -> bool:
274-
return self.major == other.major and self.minor == other.minor and self.patch == other.patch
276+
return (
277+
self.major == other.major
278+
and self.minor == other.minor
279+
and self.patch == other.patch
280+
)
275281

276282
def _increment_major(self) -> "Version":
277283
return Version(self.major + 1, 0, 0, precision=self._precision)
@@ -280,7 +286,9 @@ def _increment_minor(self) -> "Version":
280286
return Version(self.major, self.minor + 1, 0, precision=self._precision)
281287

282288
def _increment_patch(self) -> "Version":
283-
return Version(self.major, self.minor, self.patch + 1, precision=self._precision)
289+
return Version(
290+
self.major, self.minor, self.patch + 1, precision=self._precision
291+
)
284292

285293
def _normalize_prerelease(self, pre: str) -> str:
286294
if not pre:

requirements_detector/poetry_semver/version_range.py

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ def __init__(
2222
and not include_max
2323
and not full_max.is_prerelease()
2424
and not full_max.build
25-
and (min is None or not min.is_prerelease() or not min.equals_without_prerelease(full_max))
25+
and (
26+
min is None
27+
or not min.is_prerelease()
28+
or not min.equals_without_prerelease(full_max)
29+
)
2630
):
2731
full_max = full_max.first_prerelease
2832

@@ -105,7 +109,9 @@ def allows_any(self, other: VersionConstraint) -> bool:
105109
return any([self.allows_any(constraint) for constraint in other.ranges])
106110

107111
if isinstance(other, VersionRange):
108-
return not other.is_strictly_lower(self) and not other.is_strictly_higher(self)
112+
return not other.is_strictly_lower(self) and not other.is_strictly_higher(
113+
self
114+
)
109115

110116
raise ValueError("Unknown VersionConstraint type {}.".format(other))
111117

@@ -160,7 +166,9 @@ def intersect(self, other: VersionConstraint) -> VersionConstraint:
160166
return intersect_min
161167

162168
# If we got here, there is an actual range.
163-
return VersionRange(intersect_min, intersect_max, intersect_include_min, intersect_include_max)
169+
return VersionRange(
170+
intersect_min, intersect_max, intersect_include_min, intersect_include_max
171+
)
164172

165173
def union(self, other: VersionConstraint) -> VersionConstraint:
166174
from .version import Version
@@ -170,19 +178,23 @@ def union(self, other: VersionConstraint) -> VersionConstraint:
170178
return self
171179

172180
if other == self.min:
173-
return VersionRange(self.min, self.max, include_min=True, include_max=self.include_max)
181+
return VersionRange(
182+
self.min, self.max, include_min=True, include_max=self.include_max
183+
)
174184

175185
if other == self.max:
176-
return VersionRange(self.min, self.max, include_min=self.include_min, include_max=True)
186+
return VersionRange(
187+
self.min, self.max, include_min=self.include_min, include_max=True
188+
)
177189

178190
return VersionUnion.of(self, other)
179191

180192
if isinstance(other, VersionRange):
181193
# If the two ranges don't overlap, we won't be able to create a single
182194
# VersionRange for both of them.
183-
edges_touch = (self.max == other.min and (self.include_max or other.include_min)) or (
184-
self.min == other.max and (self.include_min or other.include_max)
185-
)
195+
edges_touch = (
196+
self.max == other.min and (self.include_max or other.include_min)
197+
) or (self.min == other.max and (self.include_min or other.include_max))
186198

187199
if not edges_touch and not self.allows_any(other):
188200
return VersionUnion.of(self, other)
@@ -245,14 +257,18 @@ def difference(self, other: VersionConstraint) -> VersionConstraint:
245257
elif self.min == other.min:
246258
before = self.min
247259
else:
248-
before = VersionRange(self.min, other.min, self.include_min, not other.include_min)
260+
before = VersionRange(
261+
self.min, other.min, self.include_min, not other.include_min
262+
)
249263

250264
if not self.allows_higher(other):
251265
after = None
252266
elif self.max == other.max:
253267
after = self.max
254268
else:
255-
after = VersionRange(other.max, self.max, not other.include_max, self.include_max)
269+
after = VersionRange(
270+
other.max, self.max, not other.include_max, self.include_max
271+
)
256272

257273
if before is None and after is None:
258274
return EmptyConstraint()
@@ -345,7 +361,12 @@ def is_adjacent_to(self, other: "VersionRange") -> bool:
345361
if self.max != other.min:
346362
return False
347363

348-
return self.include_max and not other.include_min or not self.include_max and other.include_min
364+
return (
365+
self.include_max
366+
and not other.include_min
367+
or not self.include_max
368+
and other.include_min
369+
)
349370

350371
def __eq__(self, other):
351372
if not isinstance(other, VersionRange):

0 commit comments

Comments
 (0)