requirements-detector 1.3.2__tar.gz → 1.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {requirements_detector-1.3.2 → requirements_detector-1.5.0}/PKG-INFO +10 -9
- {requirements_detector-1.3.2 → requirements_detector-1.5.0}/README.md +1 -1
- requirements_detector-1.5.0/pyproject.toml +49 -0
- {requirements_detector-1.3.2 → requirements_detector-1.5.0}/requirements_detector/__init__.py +11 -0
- {requirements_detector-1.3.2 → requirements_detector-1.5.0}/requirements_detector/detect.py +15 -14
- {requirements_detector-1.3.2 → requirements_detector-1.5.0}/requirements_detector/poetry_semver/__init__.py +12 -5
- {requirements_detector-1.3.2 → requirements_detector-1.5.0}/requirements_detector/poetry_semver/patterns.py +6 -2
- {requirements_detector-1.3.2 → requirements_detector-1.5.0}/requirements_detector/poetry_semver/version.py +11 -3
- {requirements_detector-1.3.2 → requirements_detector-1.5.0}/requirements_detector/poetry_semver/version_range.py +32 -11
- {requirements_detector-1.3.2 → requirements_detector-1.5.0}/requirements_detector/poetry_semver/version_union.py +4 -1
- {requirements_detector-1.3.2 → requirements_detector-1.5.0}/requirements_detector/requirement.py +23 -6
- {requirements_detector-1.3.2 → requirements_detector-1.5.0}/requirements_detector/run.py +0 -1
- requirements_detector-1.3.2/pyproject.toml +0 -52
- {requirements_detector-1.3.2 → requirements_detector-1.5.0}/LICENSE +0 -0
- {requirements_detector-1.3.2 → requirements_detector-1.5.0}/requirements_detector/__main__.py +0 -0
- {requirements_detector-1.3.2 → requirements_detector-1.5.0}/requirements_detector/exceptions.py +0 -0
- {requirements_detector-1.3.2 → requirements_detector-1.5.0}/requirements_detector/formatters.py +0 -0
- {requirements_detector-1.3.2 → requirements_detector-1.5.0}/requirements_detector/handle_setup.py +0 -0
- {requirements_detector-1.3.2 → requirements_detector-1.5.0}/requirements_detector/poetry_semver/README.md +0 -0
- {requirements_detector-1.3.2 → requirements_detector-1.5.0}/requirements_detector/poetry_semver/empty_constraint.py +0 -0
- {requirements_detector-1.3.2 → requirements_detector-1.5.0}/requirements_detector/poetry_semver/exceptions.py +0 -0
- {requirements_detector-1.3.2 → requirements_detector-1.5.0}/requirements_detector/poetry_semver/version_constraint.py +0 -0
- {requirements_detector-1.3.2 → requirements_detector-1.5.0}/requirements_detector/py.typed +0 -0
|
@@ -1,29 +1,30 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: requirements-detector
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.0
|
|
4
4
|
Summary: Python tool to find and list requirements of a Python project
|
|
5
|
-
Home-page: https://github.com/landscapeio/requirements-detector
|
|
6
5
|
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
7
|
Keywords: python,requirements detector
|
|
8
8
|
Author: Landscape.io
|
|
9
9
|
Author-email: code@landscape.io
|
|
10
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.10,<4.0
|
|
11
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
12
|
Classifier: Environment :: Console
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
14
|
Classifier: License :: OSI Approved :: MIT License
|
|
15
15
|
Classifier: Operating System :: Unix
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
19
17
|
Classifier: Programming Language :: Python :: 3.10
|
|
20
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
21
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
22
|
Classifier: Topic :: Software Development :: Quality Assurance
|
|
23
|
-
Requires-Dist: astroid (>=
|
|
23
|
+
Requires-Dist: astroid (>=4.0,<5.0)
|
|
24
24
|
Requires-Dist: packaging (>=21.3)
|
|
25
25
|
Requires-Dist: semver (>=3.0.0,<4.0.0)
|
|
26
|
-
Requires-Dist:
|
|
26
|
+
Requires-Dist: tomli (>=2.2.1,<3.0.0) ; python_version < "3.11"
|
|
27
|
+
Project-URL: Homepage, https://github.com/landscapeio/requirements-detector
|
|
27
28
|
Description-Content-Type: text/markdown
|
|
28
29
|
|
|
29
30
|
# Requirements Detector
|
|
@@ -45,7 +46,7 @@ When run from the root of a Python project, it will try to ascertain which libra
|
|
|
45
46
|
It uses the following methods in order, in the root of the project:
|
|
46
47
|
|
|
47
48
|
1. Parse `setup.py` (if this is successful, the remaining steps are skipped)
|
|
48
|
-
2. Parse `pyproject.
|
|
49
|
+
2. Parse `pyproject.toml` (if a `tool.poetry.dependencies` section is found, the remaining steps are skipped)
|
|
49
50
|
3. Parse `requirements.txt` or `requirements.pip`
|
|
50
51
|
4. Parse all `*.txt` and `*.pip` files inside a folder called `requirements`
|
|
51
52
|
5. Parse all files in the root folder matching `*requirements*.txt` or `reqs.txt` (so for example, `pip_requirements.txt` would match, as would `requirements_common.txt`)
|
|
@@ -17,7 +17,7 @@ When run from the root of a Python project, it will try to ascertain which libra
|
|
|
17
17
|
It uses the following methods in order, in the root of the project:
|
|
18
18
|
|
|
19
19
|
1. Parse `setup.py` (if this is successful, the remaining steps are skipped)
|
|
20
|
-
2. Parse `pyproject.
|
|
20
|
+
2. Parse `pyproject.toml` (if a `tool.poetry.dependencies` section is found, the remaining steps are skipped)
|
|
21
21
|
3. Parse `requirements.txt` or `requirements.pip`
|
|
22
22
|
4. Parse all `*.txt` and `*.pip` files inside a folder called `requirements`
|
|
23
23
|
5. Parse all files in the root folder matching `*requirements*.txt` or `reqs.txt` (so for example, `pip_requirements.txt` would match, as would `requirements_common.txt`)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
build-backend = "poetry.core.masonry.api"
|
|
3
|
+
requires = [ "poetry-core>=1" ]
|
|
4
|
+
|
|
5
|
+
[tool.poetry]
|
|
6
|
+
name = "requirements-detector"
|
|
7
|
+
version = "1.5.0"
|
|
8
|
+
authors = [ "Landscape.io <code@landscape.io>" ]
|
|
9
|
+
classifiers = [
|
|
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.10',
|
|
16
|
+
'Programming Language :: Python :: 3.11',
|
|
17
|
+
'Programming Language :: Python :: 3.12',
|
|
18
|
+
'Programming Language :: Python :: 3.13',
|
|
19
|
+
'License :: OSI Approved :: MIT License',
|
|
20
|
+
]
|
|
21
|
+
license = "MIT"
|
|
22
|
+
keywords = [ "python", "requirements detector" ]
|
|
23
|
+
description = "Python tool to find and list requirements of a Python project"
|
|
24
|
+
readme = "README.md"
|
|
25
|
+
homepage = "https://github.com/landscapeio/requirements-detector"
|
|
26
|
+
packages = [
|
|
27
|
+
{ include = "requirements_detector/" },
|
|
28
|
+
]
|
|
29
|
+
include = [
|
|
30
|
+
"c2cgeoform/py.typed",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[tool.poetry.dependencies]
|
|
34
|
+
python = ">=3.10,<4.0"
|
|
35
|
+
astroid = "^4.0"
|
|
36
|
+
packaging = ">=21.3"
|
|
37
|
+
tomli = { version = "^2.2.1", python = "<3.11" }
|
|
38
|
+
semver = "^3.0.0"
|
|
39
|
+
|
|
40
|
+
[tool.poetry.dev-dependencies]
|
|
41
|
+
tox = "^3.24.5"
|
|
42
|
+
pre-commit = "^4.2.0"
|
|
43
|
+
pytest = "^6.2.4"
|
|
44
|
+
twine = "^6.1.0"
|
|
45
|
+
coverage = "^5.5"
|
|
46
|
+
pytest-benchmark = "^3.4.1"
|
|
47
|
+
pytest-cov = "^2.12.1"
|
|
48
|
+
[tool.poetry.scripts]
|
|
49
|
+
detect-requirements = 'requirements_detector.run:run'
|
{requirements_detector-1.3.2 → requirements_detector-1.5.0}/requirements_detector/__init__.py
RENAMED
|
@@ -8,3 +8,14 @@ from requirements_detector.detect import ( # from_setup_py,
|
|
|
8
8
|
from_requirements_txt,
|
|
9
9
|
from_setup_py,
|
|
10
10
|
)
|
|
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
|
+
]
|
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
import re
|
|
2
|
-
import sys
|
|
3
2
|
from pathlib import Path
|
|
4
3
|
from typing import List, Optional, Union
|
|
5
4
|
|
|
6
|
-
import toml
|
|
7
|
-
|
|
8
5
|
from .exceptions import CouldNotParseRequirements, RequirementsNotFound
|
|
9
6
|
from .handle_setup import from_setup_py
|
|
10
7
|
from .poetry_semver import parse_constraint
|
|
11
8
|
from .poetry_semver.version_constraint import VersionConstraint
|
|
12
9
|
from .requirement import DetectedRequirement
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
try:
|
|
12
|
+
# added in Python 3.11: https://docs.python.org/3/library/tomllib.html
|
|
16
13
|
import tomllib
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
except ImportError:
|
|
15
|
+
# for Python <= 3.10:
|
|
16
|
+
import tomli as tomllib
|
|
19
17
|
|
|
20
18
|
__all__ = [
|
|
21
19
|
"find_requirements",
|
|
@@ -89,7 +87,7 @@ def find_requirements(path: P) -> List[DetectedRequirement]:
|
|
|
89
87
|
if reqfile.exists and reqfile.is_file():
|
|
90
88
|
try:
|
|
91
89
|
requirements += from_requirements_txt(reqfile)
|
|
92
|
-
except CouldNotParseRequirements
|
|
90
|
+
except CouldNotParseRequirements:
|
|
93
91
|
pass
|
|
94
92
|
|
|
95
93
|
requirements_dir = path / "requirements"
|
|
@@ -135,11 +133,9 @@ def from_pyproject_toml(toml_file: P) -> List[DetectedRequirement]:
|
|
|
135
133
|
if isinstance(toml_file, str):
|
|
136
134
|
toml_file = Path(toml_file)
|
|
137
135
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
else:
|
|
142
|
-
parsed = toml.load(toml_file)
|
|
136
|
+
with open(toml_file, "rb") as toml_file_open:
|
|
137
|
+
parsed = tomllib.load(toml_file_open)
|
|
138
|
+
|
|
143
139
|
poetry_section = parsed.get("tool", {}).get("poetry", {})
|
|
144
140
|
dependencies = poetry_section.get("dependencies", {})
|
|
145
141
|
dependencies.update(poetry_section.get("dev-dependencies", {}))
|
|
@@ -156,7 +152,12 @@ def from_pyproject_toml(toml_file: P) -> List[DetectedRequirement]:
|
|
|
156
152
|
continue
|
|
157
153
|
assert parsed_spec_obj is not None
|
|
158
154
|
parsed_spec = str(parsed_spec_obj)
|
|
159
|
-
if
|
|
155
|
+
if (
|
|
156
|
+
"," not in parsed_spec
|
|
157
|
+
and "<" not in parsed_spec
|
|
158
|
+
and ">" not in parsed_spec
|
|
159
|
+
and "=" not in parsed_spec
|
|
160
|
+
):
|
|
160
161
|
parsed_spec = f"=={parsed_spec}"
|
|
161
162
|
|
|
162
163
|
req = DetectedRequirement.parse(f"{name}{parsed_spec}", toml_file)
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import re
|
|
2
2
|
|
|
3
|
-
from .empty_constraint import EmptyConstraint
|
|
4
3
|
from .patterns import (
|
|
5
4
|
BASIC_CONSTRAINT,
|
|
6
5
|
CARET_CONSTRAINT,
|
|
@@ -23,7 +22,9 @@ def parse_constraint(constraints: str) -> VersionConstraint:
|
|
|
23
22
|
or_constraints = re.split(r"\s*\|\|?\s*", constraints.strip())
|
|
24
23
|
or_groups = []
|
|
25
24
|
for constraints in or_constraints:
|
|
26
|
-
and_constraints = re.split(
|
|
25
|
+
and_constraints = re.split(
|
|
26
|
+
"(?<!^)(?<![=>< ,]) *(?<!-)[, ](?!-) *(?!,|$)", constraints
|
|
27
|
+
)
|
|
27
28
|
constraint_objects = []
|
|
28
29
|
|
|
29
30
|
if len(and_constraints) > 1:
|
|
@@ -61,7 +62,9 @@ def parse_single_constraint(constraint: str) -> VersionConstraint:
|
|
|
61
62
|
if len(m.group(1).split(".")) == 1:
|
|
62
63
|
high = version.stable.next_major
|
|
63
64
|
|
|
64
|
-
return VersionRange(
|
|
65
|
+
return VersionRange(
|
|
66
|
+
version, high, include_min=True, always_include_max_prerelease=True
|
|
67
|
+
)
|
|
65
68
|
|
|
66
69
|
# PEP 440 Tilde range (~=)
|
|
67
70
|
m = TILDE_PEP440_CONSTRAINT.match(constraint)
|
|
@@ -82,7 +85,9 @@ def parse_single_constraint(constraint: str) -> VersionConstraint:
|
|
|
82
85
|
low = Version(version.major, version.minor, version.patch)
|
|
83
86
|
high = version.stable.next_minor
|
|
84
87
|
|
|
85
|
-
return VersionRange(
|
|
88
|
+
return VersionRange(
|
|
89
|
+
low, high, include_min=True, always_include_max_prerelease=True
|
|
90
|
+
)
|
|
86
91
|
|
|
87
92
|
# Caret range
|
|
88
93
|
m = CARET_CONSTRAINT.match(constraint)
|
|
@@ -142,7 +147,9 @@ def parse_single_constraint(constraint: str) -> VersionConstraint:
|
|
|
142
147
|
try:
|
|
143
148
|
version = Version.parse(version)
|
|
144
149
|
except ValueError:
|
|
145
|
-
raise ValueError(
|
|
150
|
+
raise ValueError(
|
|
151
|
+
"Could not parse version constraint: {}".format(constraint)
|
|
152
|
+
)
|
|
146
153
|
|
|
147
154
|
if op == "<":
|
|
148
155
|
return VersionRange(max=version)
|
|
@@ -6,7 +6,9 @@ MODIFIERS = (
|
|
|
6
6
|
r"([+-]?([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?"
|
|
7
7
|
)
|
|
8
8
|
|
|
9
|
-
_COMPLETE_VERSION =
|
|
9
|
+
_COMPLETE_VERSION = (
|
|
10
|
+
r"v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?{}(?:\+[^\s]+)?".format(MODIFIERS)
|
|
11
|
+
)
|
|
10
12
|
|
|
11
13
|
COMPLETE_VERSION = re.compile("(?i)" + _COMPLETE_VERSION)
|
|
12
14
|
|
|
@@ -14,4 +16,6 @@ CARET_CONSTRAINT = re.compile(r"(?i)^\^({})$".format(_COMPLETE_VERSION))
|
|
|
14
16
|
TILDE_CONSTRAINT = re.compile("(?i)^~(?!=)({})$".format(_COMPLETE_VERSION))
|
|
15
17
|
TILDE_PEP440_CONSTRAINT = re.compile("(?i)^~=({})$".format(_COMPLETE_VERSION))
|
|
16
18
|
X_CONSTRAINT = re.compile(r"^(!=|==)?\s*v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.[xX*])+$")
|
|
17
|
-
BASIC_CONSTRAINT = re.compile(
|
|
19
|
+
BASIC_CONSTRAINT = re.compile(
|
|
20
|
+
r"(?i)^(<>|!=|>=?|<=?|==?)?\s*({}|dev)".format(_COMPLETE_VERSION)
|
|
21
|
+
)
|
|
@@ -168,7 +168,9 @@ class Version(VersionRange):
|
|
|
168
168
|
|
|
169
169
|
@property
|
|
170
170
|
def first_prerelease(self) -> "Version":
|
|
171
|
-
return Version.parse(
|
|
171
|
+
return Version.parse(
|
|
172
|
+
"{}.{}.{}-alpha.0".format(self.major, self.minor, self.patch)
|
|
173
|
+
)
|
|
172
174
|
|
|
173
175
|
@property
|
|
174
176
|
def min(self):
|
|
@@ -271,7 +273,11 @@ class Version(VersionRange):
|
|
|
271
273
|
return self
|
|
272
274
|
|
|
273
275
|
def equals_without_prerelease(self, other: "Version") -> bool:
|
|
274
|
-
return
|
|
276
|
+
return (
|
|
277
|
+
self.major == other.major
|
|
278
|
+
and self.minor == other.minor
|
|
279
|
+
and self.patch == other.patch
|
|
280
|
+
)
|
|
275
281
|
|
|
276
282
|
def _increment_major(self) -> "Version":
|
|
277
283
|
return Version(self.major + 1, 0, 0, precision=self._precision)
|
|
@@ -280,7 +286,9 @@ class Version(VersionRange):
|
|
|
280
286
|
return Version(self.major, self.minor + 1, 0, precision=self._precision)
|
|
281
287
|
|
|
282
288
|
def _increment_patch(self) -> "Version":
|
|
283
|
-
return Version(
|
|
289
|
+
return Version(
|
|
290
|
+
self.major, self.minor, self.patch + 1, precision=self._precision
|
|
291
|
+
)
|
|
284
292
|
|
|
285
293
|
def _normalize_prerelease(self, pre: str) -> str:
|
|
286
294
|
if not pre:
|
|
@@ -22,7 +22,11 @@ class VersionRange(VersionConstraint):
|
|
|
22
22
|
and not include_max
|
|
23
23
|
and not full_max.is_prerelease()
|
|
24
24
|
and not full_max.build
|
|
25
|
-
and (
|
|
25
|
+
and (
|
|
26
|
+
min is None
|
|
27
|
+
or not min.is_prerelease()
|
|
28
|
+
or not min.equals_without_prerelease(full_max)
|
|
29
|
+
)
|
|
26
30
|
):
|
|
27
31
|
full_max = full_max.first_prerelease
|
|
28
32
|
|
|
@@ -105,7 +109,9 @@ class VersionRange(VersionConstraint):
|
|
|
105
109
|
return any([self.allows_any(constraint) for constraint in other.ranges])
|
|
106
110
|
|
|
107
111
|
if isinstance(other, VersionRange):
|
|
108
|
-
return not other.is_strictly_lower(self) and not other.is_strictly_higher(
|
|
112
|
+
return not other.is_strictly_lower(self) and not other.is_strictly_higher(
|
|
113
|
+
self
|
|
114
|
+
)
|
|
109
115
|
|
|
110
116
|
raise ValueError("Unknown VersionConstraint type {}.".format(other))
|
|
111
117
|
|
|
@@ -160,7 +166,9 @@ class VersionRange(VersionConstraint):
|
|
|
160
166
|
return intersect_min
|
|
161
167
|
|
|
162
168
|
# If we got here, there is an actual range.
|
|
163
|
-
return VersionRange(
|
|
169
|
+
return VersionRange(
|
|
170
|
+
intersect_min, intersect_max, intersect_include_min, intersect_include_max
|
|
171
|
+
)
|
|
164
172
|
|
|
165
173
|
def union(self, other: VersionConstraint) -> VersionConstraint:
|
|
166
174
|
from .version import Version
|
|
@@ -170,19 +178,23 @@ class VersionRange(VersionConstraint):
|
|
|
170
178
|
return self
|
|
171
179
|
|
|
172
180
|
if other == self.min:
|
|
173
|
-
return VersionRange(
|
|
181
|
+
return VersionRange(
|
|
182
|
+
self.min, self.max, include_min=True, include_max=self.include_max
|
|
183
|
+
)
|
|
174
184
|
|
|
175
185
|
if other == self.max:
|
|
176
|
-
return VersionRange(
|
|
186
|
+
return VersionRange(
|
|
187
|
+
self.min, self.max, include_min=self.include_min, include_max=True
|
|
188
|
+
)
|
|
177
189
|
|
|
178
190
|
return VersionUnion.of(self, other)
|
|
179
191
|
|
|
180
192
|
if isinstance(other, VersionRange):
|
|
181
193
|
# If the two ranges don't overlap, we won't be able to create a single
|
|
182
194
|
# VersionRange for both of them.
|
|
183
|
-
edges_touch = (
|
|
184
|
-
self.
|
|
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))
|
|
186
198
|
|
|
187
199
|
if not edges_touch and not self.allows_any(other):
|
|
188
200
|
return VersionUnion.of(self, other)
|
|
@@ -245,14 +257,18 @@ class VersionRange(VersionConstraint):
|
|
|
245
257
|
elif self.min == other.min:
|
|
246
258
|
before = self.min
|
|
247
259
|
else:
|
|
248
|
-
before = VersionRange(
|
|
260
|
+
before = VersionRange(
|
|
261
|
+
self.min, other.min, self.include_min, not other.include_min
|
|
262
|
+
)
|
|
249
263
|
|
|
250
264
|
if not self.allows_higher(other):
|
|
251
265
|
after = None
|
|
252
266
|
elif self.max == other.max:
|
|
253
267
|
after = self.max
|
|
254
268
|
else:
|
|
255
|
-
after = VersionRange(
|
|
269
|
+
after = VersionRange(
|
|
270
|
+
other.max, self.max, not other.include_max, self.include_max
|
|
271
|
+
)
|
|
256
272
|
|
|
257
273
|
if before is None and after is None:
|
|
258
274
|
return EmptyConstraint()
|
|
@@ -345,7 +361,12 @@ class VersionRange(VersionConstraint):
|
|
|
345
361
|
if self.max != other.min:
|
|
346
362
|
return False
|
|
347
363
|
|
|
348
|
-
return
|
|
364
|
+
return (
|
|
365
|
+
self.include_max
|
|
366
|
+
and not other.include_min
|
|
367
|
+
or not self.include_max
|
|
368
|
+
and other.include_min
|
|
369
|
+
)
|
|
349
370
|
|
|
350
371
|
def __eq__(self, other):
|
|
351
372
|
if not isinstance(other, VersionRange):
|
|
@@ -60,7 +60,10 @@ class VersionUnion(VersionConstraint):
|
|
|
60
60
|
merged = []
|
|
61
61
|
for constraint in flattened:
|
|
62
62
|
# Merge this constraint with the previous one, but only if they touch.
|
|
63
|
-
if not merged or (
|
|
63
|
+
if not merged or (
|
|
64
|
+
not merged[-1].allows_any(constraint)
|
|
65
|
+
and not merged[-1].is_adjacent_to(constraint)
|
|
66
|
+
):
|
|
64
67
|
merged.append(constraint)
|
|
65
68
|
else:
|
|
66
69
|
merged[-1] = merged[-1].union(constraint)
|
{requirements_detector-1.3.2 → requirements_detector-1.5.0}/requirements_detector/requirement.py
RENAMED
|
@@ -8,6 +8,7 @@ we don't expect relative file paths to exist, for example. Note that the parsing
|
|
|
8
8
|
is also intentionally more lenient - it is not our job to validate the requirements
|
|
9
9
|
list.
|
|
10
10
|
"""
|
|
11
|
+
|
|
11
12
|
import os
|
|
12
13
|
import re
|
|
13
14
|
from pathlib import Path
|
|
@@ -51,12 +52,18 @@ def _strip_fragment(urlparts):
|
|
|
51
52
|
|
|
52
53
|
class DetectedRequirement:
|
|
53
54
|
def __init__(
|
|
54
|
-
self,
|
|
55
|
+
self,
|
|
56
|
+
name: str = None,
|
|
57
|
+
url: str = None,
|
|
58
|
+
requirement: Requirement = None,
|
|
59
|
+
location_defined: Path = None,
|
|
55
60
|
):
|
|
56
61
|
if requirement is not None:
|
|
57
62
|
self.name = requirement.name
|
|
58
63
|
self.requirement = requirement
|
|
59
|
-
self.version_specs = [
|
|
64
|
+
self.version_specs = [
|
|
65
|
+
(s.operator, s.version) for s in requirement.specifier
|
|
66
|
+
]
|
|
60
67
|
self.url = None
|
|
61
68
|
else:
|
|
62
69
|
self.name = name
|
|
@@ -66,7 +73,9 @@ class DetectedRequirement:
|
|
|
66
73
|
self.location_defined = location_defined
|
|
67
74
|
|
|
68
75
|
def _format_specs(self) -> str:
|
|
69
|
-
return ",".join(
|
|
76
|
+
return ",".join(
|
|
77
|
+
["%s%s" % (comp, version) for comp, version in self.version_specs]
|
|
78
|
+
)
|
|
70
79
|
|
|
71
80
|
def pip_format(self) -> str:
|
|
72
81
|
if self.url:
|
|
@@ -95,7 +104,11 @@ class DetectedRequirement:
|
|
|
95
104
|
return "<DetectedRequirement:%s>" % str(self)
|
|
96
105
|
|
|
97
106
|
def __eq__(self, other):
|
|
98
|
-
return
|
|
107
|
+
return (
|
|
108
|
+
self.name == other.name
|
|
109
|
+
and self.url == other.url
|
|
110
|
+
and self.version_specs == other.version_specs
|
|
111
|
+
)
|
|
99
112
|
|
|
100
113
|
def __gt__(self, other):
|
|
101
114
|
return (self.name or "") > (other.name or "")
|
|
@@ -149,7 +162,9 @@ class DetectedRequirement:
|
|
|
149
162
|
# this happens if the line is invalid
|
|
150
163
|
return None
|
|
151
164
|
else:
|
|
152
|
-
return DetectedRequirement(
|
|
165
|
+
return DetectedRequirement(
|
|
166
|
+
requirement=req, location_defined=location_defined
|
|
167
|
+
)
|
|
153
168
|
|
|
154
169
|
# otherwise, this is some kind of URL
|
|
155
170
|
name = _parse_egg_name(url.fragment)
|
|
@@ -158,4 +173,6 @@ class DetectedRequirement:
|
|
|
158
173
|
if vcs_scheme:
|
|
159
174
|
url = "%s://%s" % (vcs_scheme, url)
|
|
160
175
|
|
|
161
|
-
return DetectedRequirement(
|
|
176
|
+
return DetectedRequirement(
|
|
177
|
+
name=name, url=url, location_defined=location_defined
|
|
178
|
+
)
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
[tool.poetry]
|
|
2
|
-
name = "requirements-detector"
|
|
3
|
-
version = "1.3.2"
|
|
4
|
-
authors = ["Landscape.io <code@landscape.io>"]
|
|
5
|
-
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',
|
|
17
|
-
]
|
|
18
|
-
license = "MIT"
|
|
19
|
-
keywords = ["python","requirements detector"]
|
|
20
|
-
description = "Python tool to find and list requirements of a Python project"
|
|
21
|
-
readme = "README.md"
|
|
22
|
-
homepage = "https://github.com/landscapeio/requirements-detector"
|
|
23
|
-
packages = [
|
|
24
|
-
{ include = "requirements_detector/"}
|
|
25
|
-
]
|
|
26
|
-
include = [
|
|
27
|
-
"c2cgeoform/py.typed",
|
|
28
|
-
]
|
|
29
|
-
|
|
30
|
-
[tool.poetry.scripts]
|
|
31
|
-
detect-requirements = 'requirements_detector.run:run'
|
|
32
|
-
|
|
33
|
-
[tool.poetry.dependencies]
|
|
34
|
-
python = ">=3.8,<4.0"
|
|
35
|
-
astroid = "^3.0"
|
|
36
|
-
packaging = ">=21.3"
|
|
37
|
-
toml = {version = "^0.10.2", python = "<3.11"}
|
|
38
|
-
semver = "^3.0.0"
|
|
39
|
-
|
|
40
|
-
[tool.poetry.dev-dependencies]
|
|
41
|
-
tox = "^3.24.5"
|
|
42
|
-
pre-commit = "^2.17.0"
|
|
43
|
-
pytest = "^6.2.4"
|
|
44
|
-
coverage = "^5.5"
|
|
45
|
-
twine = "^3.8.0"
|
|
46
|
-
pytest-benchmark = "^3.4.1"
|
|
47
|
-
pytest-cov = "^2.12.1"
|
|
48
|
-
types-toml = "^0.10.4"
|
|
49
|
-
|
|
50
|
-
[build-system]
|
|
51
|
-
requires = ["poetry-core>=1.0.0"]
|
|
52
|
-
build-backend = "poetry.core.masonry.api"
|
|
File without changes
|
{requirements_detector-1.3.2 → requirements_detector-1.5.0}/requirements_detector/__main__.py
RENAMED
|
File without changes
|
{requirements_detector-1.3.2 → requirements_detector-1.5.0}/requirements_detector/exceptions.py
RENAMED
|
File without changes
|
{requirements_detector-1.3.2 → requirements_detector-1.5.0}/requirements_detector/formatters.py
RENAMED
|
File without changes
|
{requirements_detector-1.3.2 → requirements_detector-1.5.0}/requirements_detector/handle_setup.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|