requirements-detector 1.3.1__tar.gz → 1.4.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.1 → requirements_detector-1.4.0}/PKG-INFO +7 -7
- {requirements_detector-1.3.1 → requirements_detector-1.4.0}/README.md +1 -1
- requirements_detector-1.4.0/pyproject.toml +52 -0
- {requirements_detector-1.3.1 → requirements_detector-1.4.0}/requirements_detector/__init__.py +11 -0
- {requirements_detector-1.3.1 → requirements_detector-1.4.0}/requirements_detector/detect.py +48 -15
- {requirements_detector-1.3.1 → requirements_detector-1.4.0}/requirements_detector/poetry_semver/__init__.py +14 -7
- {requirements_detector-1.3.1 → requirements_detector-1.4.0}/requirements_detector/poetry_semver/patterns.py +6 -2
- {requirements_detector-1.3.1 → requirements_detector-1.4.0}/requirements_detector/poetry_semver/version.py +49 -41
- requirements_detector-1.4.0/requirements_detector/poetry_semver/version_constraint.py +27 -0
- {requirements_detector-1.3.1 → requirements_detector-1.4.0}/requirements_detector/poetry_semver/version_range.py +46 -25
- {requirements_detector-1.3.1 → requirements_detector-1.4.0}/requirements_detector/poetry_semver/version_union.py +16 -10
- requirements_detector-1.4.0/requirements_detector/py.typed +0 -0
- {requirements_detector-1.3.1 → requirements_detector-1.4.0}/requirements_detector/requirement.py +23 -6
- {requirements_detector-1.3.1 → requirements_detector-1.4.0}/requirements_detector/run.py +0 -1
- requirements_detector-1.3.1/pyproject.toml +0 -49
- requirements_detector-1.3.1/requirements_detector/poetry_semver/version_constraint.py +0 -27
- {requirements_detector-1.3.1 → requirements_detector-1.4.0}/LICENSE +0 -0
- {requirements_detector-1.3.1 → requirements_detector-1.4.0}/requirements_detector/__main__.py +0 -0
- {requirements_detector-1.3.1 → requirements_detector-1.4.0}/requirements_detector/exceptions.py +0 -0
- {requirements_detector-1.3.1 → requirements_detector-1.4.0}/requirements_detector/formatters.py +0 -0
- {requirements_detector-1.3.1 → requirements_detector-1.4.0}/requirements_detector/handle_setup.py +0 -0
- {requirements_detector-1.3.1 → requirements_detector-1.4.0}/requirements_detector/poetry_semver/README.md +0 -0
- {requirements_detector-1.3.1 → requirements_detector-1.4.0}/requirements_detector/poetry_semver/empty_constraint.py +0 -0
- {requirements_detector-1.3.1 → requirements_detector-1.4.0}/requirements_detector/poetry_semver/exceptions.py +0 -0
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: requirements-detector
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.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
|
|
7
6
|
Keywords: python,requirements detector
|
|
8
7
|
Author: Landscape.io
|
|
9
8
|
Author-email: code@landscape.io
|
|
10
|
-
Requires-Python: >=3.
|
|
9
|
+
Requires-Python: >=3.9,<4.0
|
|
11
10
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
11
|
Classifier: Environment :: Console
|
|
13
12
|
Classifier: Intended Audience :: Developers
|
|
14
13
|
Classifier: License :: OSI Approved :: MIT License
|
|
15
14
|
Classifier: Operating System :: Unix
|
|
16
15
|
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
18
16
|
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
|
|
22
21
|
Classifier: Topic :: Software Development :: Quality Assurance
|
|
23
22
|
Requires-Dist: astroid (>=3.0,<4.0)
|
|
24
23
|
Requires-Dist: packaging (>=21.3)
|
|
25
24
|
Requires-Dist: semver (>=3.0.0,<4.0.0)
|
|
26
|
-
Requires-Dist:
|
|
25
|
+
Requires-Dist: tomli (>=2.2.1,<3.0.0) ; python_version < "3.11"
|
|
26
|
+
Project-URL: Homepage, https://github.com/landscapeio/requirements-detector
|
|
27
27
|
Description-Content-Type: text/markdown
|
|
28
28
|
|
|
29
29
|
# Requirements Detector
|
|
@@ -45,7 +45,7 @@ When run from the root of a Python project, it will try to ascertain which libra
|
|
|
45
45
|
It uses the following methods in order, in the root of the project:
|
|
46
46
|
|
|
47
47
|
1. Parse `setup.py` (if this is successful, the remaining steps are skipped)
|
|
48
|
-
2. Parse `pyproject.
|
|
48
|
+
2. Parse `pyproject.toml` (if a `tool.poetry.dependencies` section is found, the remaining steps are skipped)
|
|
49
49
|
3. Parse `requirements.txt` or `requirements.pip`
|
|
50
50
|
4. Parse all `*.txt` and `*.pip` files inside a folder called `requirements`
|
|
51
51
|
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,52 @@
|
|
|
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.4.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.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',
|
|
21
|
+
]
|
|
22
|
+
license = "MIT"
|
|
23
|
+
keywords = [ "python", "requirements detector" ]
|
|
24
|
+
description = "Python tool to find and list requirements of a Python project"
|
|
25
|
+
readme = "README.md"
|
|
26
|
+
homepage = "https://github.com/landscapeio/requirements-detector"
|
|
27
|
+
packages = [
|
|
28
|
+
{ include = "requirements_detector/" },
|
|
29
|
+
]
|
|
30
|
+
include = [
|
|
31
|
+
"c2cgeoform/py.typed",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[tool.poetry.scripts]
|
|
35
|
+
detect-requirements = 'requirements_detector.run:run'
|
|
36
|
+
|
|
37
|
+
[tool.poetry.dependencies]
|
|
38
|
+
python = ">=3.9,<4.0"
|
|
39
|
+
astroid = "^3.0"
|
|
40
|
+
packaging = ">=21.3"
|
|
41
|
+
tomli = { version = "^2.2.1", python = "<3.11" }
|
|
42
|
+
semver = "^3.0.0"
|
|
43
|
+
|
|
44
|
+
[tool.poetry.dev-dependencies]
|
|
45
|
+
tox = "^3.24.5"
|
|
46
|
+
pre-commit = "^4.2.0"
|
|
47
|
+
pytest = "^6.2.4"
|
|
48
|
+
twine = "^6.1.0"
|
|
49
|
+
coverage = "^5.5"
|
|
50
|
+
pytest-benchmark = "^3.4.1"
|
|
51
|
+
pytest-cov = "^2.12.1"
|
|
52
|
+
types-toml = "^0.10.4"
|
{requirements_detector-1.3.1 → requirements_detector-1.4.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,14 +1,21 @@
|
|
|
1
1
|
import re
|
|
2
|
+
import sys
|
|
2
3
|
from pathlib import Path
|
|
3
|
-
from typing import List, Union
|
|
4
|
-
|
|
5
|
-
import toml
|
|
4
|
+
from typing import List, Optional, Union
|
|
6
5
|
|
|
7
6
|
from .exceptions import CouldNotParseRequirements, RequirementsNotFound
|
|
8
7
|
from .handle_setup import from_setup_py
|
|
9
8
|
from .poetry_semver import parse_constraint
|
|
9
|
+
from .poetry_semver.version_constraint import VersionConstraint
|
|
10
10
|
from .requirement import DetectedRequirement
|
|
11
11
|
|
|
12
|
+
try:
|
|
13
|
+
# added in Python 3.11: https://docs.python.org/3/library/tomllib.html
|
|
14
|
+
import tomllib
|
|
15
|
+
except ImportError:
|
|
16
|
+
# for Python <= 3.10:
|
|
17
|
+
import tomli as tomllib
|
|
18
|
+
|
|
12
19
|
__all__ = [
|
|
13
20
|
"find_requirements",
|
|
14
21
|
"from_requirements_txt",
|
|
@@ -81,7 +88,7 @@ def find_requirements(path: P) -> List[DetectedRequirement]:
|
|
|
81
88
|
if reqfile.exists and reqfile.is_file():
|
|
82
89
|
try:
|
|
83
90
|
requirements += from_requirements_txt(reqfile)
|
|
84
|
-
except CouldNotParseRequirements
|
|
91
|
+
except CouldNotParseRequirements:
|
|
85
92
|
pass
|
|
86
93
|
|
|
87
94
|
requirements_dir = path / "requirements"
|
|
@@ -102,13 +109,34 @@ def find_requirements(path: P) -> List[DetectedRequirement]:
|
|
|
102
109
|
raise RequirementsNotFound
|
|
103
110
|
|
|
104
111
|
|
|
112
|
+
def _version_from_spec(spec: Union[list, dict, str]) -> Optional[VersionConstraint]:
|
|
113
|
+
if isinstance(spec, list):
|
|
114
|
+
constraint = None
|
|
115
|
+
for new_constraint in [_version_from_spec(s) for s in spec]:
|
|
116
|
+
if constraint is None:
|
|
117
|
+
constraint = new_constraint
|
|
118
|
+
elif new_constraint is not None:
|
|
119
|
+
constraint = constraint.union(new_constraint)
|
|
120
|
+
return constraint
|
|
121
|
+
|
|
122
|
+
if isinstance(spec, dict):
|
|
123
|
+
if "version" in spec:
|
|
124
|
+
spec = spec["version"]
|
|
125
|
+
else:
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
return parse_constraint(spec)
|
|
129
|
+
|
|
130
|
+
|
|
105
131
|
def from_pyproject_toml(toml_file: P) -> List[DetectedRequirement]:
|
|
106
132
|
requirements = []
|
|
107
133
|
|
|
108
134
|
if isinstance(toml_file, str):
|
|
109
135
|
toml_file = Path(toml_file)
|
|
110
136
|
|
|
111
|
-
|
|
137
|
+
with open(toml_file, "rb") as toml_file_open:
|
|
138
|
+
parsed = tomllib.load(toml_file_open)
|
|
139
|
+
|
|
112
140
|
poetry_section = parsed.get("tool", {}).get("poetry", {})
|
|
113
141
|
dependencies = poetry_section.get("dependencies", {})
|
|
114
142
|
dependencies.update(poetry_section.get("dev-dependencies", {}))
|
|
@@ -116,16 +144,21 @@ def from_pyproject_toml(toml_file: P) -> List[DetectedRequirement]:
|
|
|
116
144
|
for name, spec in dependencies.items():
|
|
117
145
|
if name.lower() == "python":
|
|
118
146
|
continue
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
parsed_spec = str(
|
|
128
|
-
if
|
|
147
|
+
|
|
148
|
+
parsed_spec_obj = _version_from_spec(spec)
|
|
149
|
+
if parsed_spec_obj is None and isinstance(spec, dict) and "version" not in spec:
|
|
150
|
+
req = DetectedRequirement.parse(f"{name}", toml_file)
|
|
151
|
+
if req is not None:
|
|
152
|
+
requirements.append(req)
|
|
153
|
+
continue
|
|
154
|
+
assert parsed_spec_obj is not None
|
|
155
|
+
parsed_spec = str(parsed_spec_obj)
|
|
156
|
+
if (
|
|
157
|
+
"," not in parsed_spec
|
|
158
|
+
and "<" not in parsed_spec
|
|
159
|
+
and ">" not in parsed_spec
|
|
160
|
+
and "=" not in parsed_spec
|
|
161
|
+
):
|
|
129
162
|
parsed_spec = f"=={parsed_spec}"
|
|
130
163
|
|
|
131
164
|
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,
|
|
@@ -16,14 +15,16 @@ from .version_union import VersionUnion
|
|
|
16
15
|
__version__ = "0.1.0"
|
|
17
16
|
|
|
18
17
|
|
|
19
|
-
def parse_constraint(constraints
|
|
18
|
+
def parse_constraint(constraints: str) -> VersionConstraint:
|
|
20
19
|
if constraints == "*":
|
|
21
20
|
return VersionRange()
|
|
22
21
|
|
|
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:
|
|
@@ -47,7 +48,7 @@ def parse_constraint(constraints): # type: (str) -> VersionConstraint
|
|
|
47
48
|
return VersionUnion.of(*or_groups)
|
|
48
49
|
|
|
49
50
|
|
|
50
|
-
def parse_single_constraint(constraint
|
|
51
|
+
def parse_single_constraint(constraint: str) -> VersionConstraint:
|
|
51
52
|
m = re.match(r"(?i)^v?[xX*](\.[xX*])*$", constraint)
|
|
52
53
|
if m:
|
|
53
54
|
return VersionRange()
|
|
@@ -61,7 +62,9 @@ def parse_single_constraint(constraint): # type: (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): # type: (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): # type: (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
|
+
)
|
|
@@ -16,15 +16,15 @@ class Version(VersionRange):
|
|
|
16
16
|
|
|
17
17
|
def __init__(
|
|
18
18
|
self,
|
|
19
|
-
major
|
|
20
|
-
minor
|
|
21
|
-
patch
|
|
22
|
-
rest
|
|
23
|
-
pre
|
|
24
|
-
build
|
|
25
|
-
text
|
|
26
|
-
precision
|
|
27
|
-
)
|
|
19
|
+
major: int,
|
|
20
|
+
minor: Optional[int] = None,
|
|
21
|
+
patch: Optional[int] = None,
|
|
22
|
+
rest: Optional[int] = None,
|
|
23
|
+
pre: Optional[str] = None,
|
|
24
|
+
build: Optional[str] = None,
|
|
25
|
+
text: Optional[str] = None,
|
|
26
|
+
precision: Optional[int] = None,
|
|
27
|
+
) -> None:
|
|
28
28
|
self._major = int(major)
|
|
29
29
|
self._precision = None
|
|
30
30
|
if precision is None:
|
|
@@ -92,27 +92,27 @@ class Version(VersionRange):
|
|
|
92
92
|
self._build = self._split_parts(build)
|
|
93
93
|
|
|
94
94
|
@property
|
|
95
|
-
def major(self)
|
|
95
|
+
def major(self) -> int:
|
|
96
96
|
return self._major
|
|
97
97
|
|
|
98
98
|
@property
|
|
99
|
-
def minor(self)
|
|
99
|
+
def minor(self) -> int:
|
|
100
100
|
return self._minor
|
|
101
101
|
|
|
102
102
|
@property
|
|
103
|
-
def patch(self)
|
|
103
|
+
def patch(self) -> int:
|
|
104
104
|
return self._patch
|
|
105
105
|
|
|
106
106
|
@property
|
|
107
|
-
def rest(self)
|
|
107
|
+
def rest(self) -> int:
|
|
108
108
|
return self._rest
|
|
109
109
|
|
|
110
110
|
@property
|
|
111
|
-
def prerelease(self)
|
|
111
|
+
def prerelease(self) -> List[str]:
|
|
112
112
|
return self._prerelease
|
|
113
113
|
|
|
114
114
|
@property
|
|
115
|
-
def build(self)
|
|
115
|
+
def build(self) -> List[str]:
|
|
116
116
|
return self._build
|
|
117
117
|
|
|
118
118
|
@property
|
|
@@ -120,7 +120,7 @@ class Version(VersionRange):
|
|
|
120
120
|
return self._text
|
|
121
121
|
|
|
122
122
|
@property
|
|
123
|
-
def precision(self)
|
|
123
|
+
def precision(self) -> int:
|
|
124
124
|
return self._precision
|
|
125
125
|
|
|
126
126
|
@property
|
|
@@ -131,28 +131,28 @@ class Version(VersionRange):
|
|
|
131
131
|
return self.next_patch
|
|
132
132
|
|
|
133
133
|
@property
|
|
134
|
-
def next_major(self)
|
|
134
|
+
def next_major(self) -> "Version":
|
|
135
135
|
if self.is_prerelease() and self.minor == 0 and self.patch == 0:
|
|
136
136
|
return Version(self.major, self.minor, self.patch)
|
|
137
137
|
|
|
138
138
|
return self._increment_major()
|
|
139
139
|
|
|
140
140
|
@property
|
|
141
|
-
def next_minor(self)
|
|
141
|
+
def next_minor(self) -> "Version":
|
|
142
142
|
if self.is_prerelease() and self.patch == 0:
|
|
143
143
|
return Version(self.major, self.minor, self.patch)
|
|
144
144
|
|
|
145
145
|
return self._increment_minor()
|
|
146
146
|
|
|
147
147
|
@property
|
|
148
|
-
def next_patch(self)
|
|
148
|
+
def next_patch(self) -> "Version":
|
|
149
149
|
if self.is_prerelease():
|
|
150
150
|
return Version(self.major, self.minor, self.patch)
|
|
151
151
|
|
|
152
152
|
return self._increment_patch()
|
|
153
153
|
|
|
154
154
|
@property
|
|
155
|
-
def next_breaking(self)
|
|
155
|
+
def next_breaking(self) -> "Version":
|
|
156
156
|
if self.major == 0:
|
|
157
157
|
if self.minor != 0:
|
|
158
158
|
return self._increment_minor()
|
|
@@ -167,8 +167,10 @@ class Version(VersionRange):
|
|
|
167
167
|
return self._increment_major()
|
|
168
168
|
|
|
169
169
|
@property
|
|
170
|
-
def first_prerelease(self)
|
|
171
|
-
return Version.parse(
|
|
170
|
+
def first_prerelease(self) -> "Version":
|
|
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):
|
|
@@ -191,7 +193,7 @@ class Version(VersionRange):
|
|
|
191
193
|
return True
|
|
192
194
|
|
|
193
195
|
@classmethod
|
|
194
|
-
def parse(cls, text
|
|
196
|
+
def parse(cls, text: str) -> "Version":
|
|
195
197
|
try:
|
|
196
198
|
match = COMPLETE_VERSION.match(text)
|
|
197
199
|
except TypeError:
|
|
@@ -221,25 +223,25 @@ class Version(VersionRange):
|
|
|
221
223
|
def is_empty(self):
|
|
222
224
|
return False
|
|
223
225
|
|
|
224
|
-
def is_prerelease(self)
|
|
226
|
+
def is_prerelease(self) -> bool:
|
|
225
227
|
return len(self._prerelease) > 0
|
|
226
228
|
|
|
227
|
-
def allows(self, version
|
|
229
|
+
def allows(self, version: "Version") -> bool:
|
|
228
230
|
return self == version
|
|
229
231
|
|
|
230
|
-
def allows_all(self, other
|
|
232
|
+
def allows_all(self, other: VersionConstraint) -> bool:
|
|
231
233
|
return other.is_empty() or other == self
|
|
232
234
|
|
|
233
|
-
def allows_any(self, other
|
|
235
|
+
def allows_any(self, other: VersionConstraint) -> bool:
|
|
234
236
|
return other.allows(self)
|
|
235
237
|
|
|
236
|
-
def intersect(self, other
|
|
238
|
+
def intersect(self, other: VersionConstraint) -> VersionConstraint:
|
|
237
239
|
if other.allows(self):
|
|
238
240
|
return self
|
|
239
241
|
|
|
240
242
|
return EmptyConstraint()
|
|
241
243
|
|
|
242
|
-
def union(self, other
|
|
244
|
+
def union(self, other: VersionConstraint) -> VersionConstraint:
|
|
243
245
|
from .version_range import VersionRange
|
|
244
246
|
|
|
245
247
|
if other.allows(self):
|
|
@@ -264,25 +266,31 @@ class Version(VersionRange):
|
|
|
264
266
|
|
|
265
267
|
return VersionUnion.of(self, other)
|
|
266
268
|
|
|
267
|
-
def difference(self, other
|
|
269
|
+
def difference(self, other: VersionConstraint) -> VersionConstraint:
|
|
268
270
|
if other.allows(self):
|
|
269
271
|
return EmptyConstraint()
|
|
270
272
|
|
|
271
273
|
return self
|
|
272
274
|
|
|
273
|
-
def equals_without_prerelease(self, other
|
|
274
|
-
return
|
|
275
|
+
def equals_without_prerelease(self, other: "Version") -> bool:
|
|
276
|
+
return (
|
|
277
|
+
self.major == other.major
|
|
278
|
+
and self.minor == other.minor
|
|
279
|
+
and self.patch == other.patch
|
|
280
|
+
)
|
|
275
281
|
|
|
276
|
-
def _increment_major(self)
|
|
282
|
+
def _increment_major(self) -> "Version":
|
|
277
283
|
return Version(self.major + 1, 0, 0, precision=self._precision)
|
|
278
284
|
|
|
279
|
-
def _increment_minor(self)
|
|
285
|
+
def _increment_minor(self) -> "Version":
|
|
280
286
|
return Version(self.major, self.minor + 1, 0, precision=self._precision)
|
|
281
287
|
|
|
282
|
-
def _increment_patch(self)
|
|
283
|
-
return Version(
|
|
288
|
+
def _increment_patch(self) -> "Version":
|
|
289
|
+
return Version(
|
|
290
|
+
self.major, self.minor, self.patch + 1, precision=self._precision
|
|
291
|
+
)
|
|
284
292
|
|
|
285
|
-
def _normalize_prerelease(self, pre
|
|
293
|
+
def _normalize_prerelease(self, pre: str) -> str:
|
|
286
294
|
if not pre:
|
|
287
295
|
return
|
|
288
296
|
|
|
@@ -307,7 +315,7 @@ class Version(VersionRange):
|
|
|
307
315
|
|
|
308
316
|
return "{}.{}".format(modifier, number)
|
|
309
317
|
|
|
310
|
-
def _normalize_build(self, build
|
|
318
|
+
def _normalize_build(self, build: str) -> str:
|
|
311
319
|
if not build:
|
|
312
320
|
return
|
|
313
321
|
|
|
@@ -319,7 +327,7 @@ class Version(VersionRange):
|
|
|
319
327
|
|
|
320
328
|
return build
|
|
321
329
|
|
|
322
|
-
def _split_parts(self, text
|
|
330
|
+
def _split_parts(self, text: str) -> List[Union[str, int]]:
|
|
323
331
|
parts = text.split(".")
|
|
324
332
|
|
|
325
333
|
for i, part in enumerate(parts):
|
|
@@ -389,7 +397,7 @@ class Version(VersionRange):
|
|
|
389
397
|
|
|
390
398
|
return 0
|
|
391
399
|
|
|
392
|
-
def _cmp_lists(self, a, b
|
|
400
|
+
def _cmp_lists(self, a: List, b: List) -> int:
|
|
393
401
|
for i in range(max(len(a), len(b))):
|
|
394
402
|
a_part = None
|
|
395
403
|
if i < len(a):
|
|
@@ -422,7 +430,7 @@ class Version(VersionRange):
|
|
|
422
430
|
|
|
423
431
|
return 0
|
|
424
432
|
|
|
425
|
-
def __eq__(self, other
|
|
433
|
+
def __eq__(self, other: "Version") -> bool:
|
|
426
434
|
if not isinstance(other, Version):
|
|
427
435
|
return NotImplemented
|
|
428
436
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import semver
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class VersionConstraint:
|
|
5
|
+
def is_empty(self) -> bool:
|
|
6
|
+
raise NotImplementedError()
|
|
7
|
+
|
|
8
|
+
def is_any(self) -> bool:
|
|
9
|
+
raise NotImplementedError()
|
|
10
|
+
|
|
11
|
+
def allows(self, version: semver.Version) -> bool:
|
|
12
|
+
raise NotImplementedError()
|
|
13
|
+
|
|
14
|
+
def allows_all(self, other: "VersionConstraint") -> bool:
|
|
15
|
+
raise NotImplementedError()
|
|
16
|
+
|
|
17
|
+
def allows_any(self, other: "VersionConstraint") -> bool:
|
|
18
|
+
raise NotImplementedError()
|
|
19
|
+
|
|
20
|
+
def intersect(self, other: "VersionConstraint") -> "VersionConstraint":
|
|
21
|
+
raise NotImplementedError()
|
|
22
|
+
|
|
23
|
+
def union(self, other: "VersionConstraint") -> "VersionConstraint":
|
|
24
|
+
raise NotImplementedError()
|
|
25
|
+
|
|
26
|
+
def difference(self, other: "VersionConstraint") -> "VersionConstraint":
|
|
27
|
+
raise NotImplementedError()
|
|
@@ -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
|
|
|
@@ -58,7 +62,7 @@ class VersionRange(VersionConstraint):
|
|
|
58
62
|
def is_any(self):
|
|
59
63
|
return self._min is None and self._max is None
|
|
60
64
|
|
|
61
|
-
def allows(self, other
|
|
65
|
+
def allows(self, other: semver.Version) -> bool:
|
|
62
66
|
if self._min is not None:
|
|
63
67
|
if other < self._min:
|
|
64
68
|
return False
|
|
@@ -75,7 +79,7 @@ class VersionRange(VersionConstraint):
|
|
|
75
79
|
|
|
76
80
|
return True
|
|
77
81
|
|
|
78
|
-
def allows_all(self, other
|
|
82
|
+
def allows_all(self, other: VersionConstraint) -> bool:
|
|
79
83
|
from .version import Version
|
|
80
84
|
|
|
81
85
|
if other.is_empty():
|
|
@@ -92,7 +96,7 @@ class VersionRange(VersionConstraint):
|
|
|
92
96
|
|
|
93
97
|
raise ValueError("Unknown VersionConstraint type {}.".format(other))
|
|
94
98
|
|
|
95
|
-
def allows_any(self, other
|
|
99
|
+
def allows_any(self, other: VersionConstraint) -> bool:
|
|
96
100
|
from .version import Version
|
|
97
101
|
|
|
98
102
|
if other.is_empty():
|
|
@@ -105,11 +109,13 @@ 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
|
|
|
112
|
-
def intersect(self, other
|
|
118
|
+
def intersect(self, other: VersionConstraint) -> VersionConstraint:
|
|
113
119
|
from .version import Version
|
|
114
120
|
|
|
115
121
|
if other.is_empty():
|
|
@@ -160,9 +166,11 @@ 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
|
-
def union(self, other
|
|
173
|
+
def union(self, other: VersionConstraint) -> VersionConstraint:
|
|
166
174
|
from .version import Version
|
|
167
175
|
|
|
168
176
|
if isinstance(other, 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)
|
|
@@ -210,7 +222,7 @@ class VersionRange(VersionConstraint):
|
|
|
210
222
|
|
|
211
223
|
return VersionUnion.of(self, other)
|
|
212
224
|
|
|
213
|
-
def difference(self, other
|
|
225
|
+
def difference(self, other: VersionConstraint) -> VersionConstraint:
|
|
214
226
|
from .version import Version
|
|
215
227
|
|
|
216
228
|
if other.is_empty():
|
|
@@ -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()
|
|
@@ -265,7 +281,7 @@ class VersionRange(VersionConstraint):
|
|
|
265
281
|
|
|
266
282
|
return VersionUnion.of(before, after)
|
|
267
283
|
elif isinstance(other, VersionUnion):
|
|
268
|
-
ranges
|
|
284
|
+
ranges: List[VersionRange] = []
|
|
269
285
|
current = self
|
|
270
286
|
|
|
271
287
|
for range in other.ranges:
|
|
@@ -296,7 +312,7 @@ class VersionRange(VersionConstraint):
|
|
|
296
312
|
|
|
297
313
|
raise ValueError("Unknown VersionConstraint type {}.".format(other))
|
|
298
314
|
|
|
299
|
-
def allows_lower(self, other
|
|
315
|
+
def allows_lower(self, other: "VersionRange") -> bool:
|
|
300
316
|
if self.min is None:
|
|
301
317
|
return other.min is not None
|
|
302
318
|
|
|
@@ -311,7 +327,7 @@ class VersionRange(VersionConstraint):
|
|
|
311
327
|
|
|
312
328
|
return self.include_min and not other.include_min
|
|
313
329
|
|
|
314
|
-
def allows_higher(self, other
|
|
330
|
+
def allows_higher(self, other: "VersionRange") -> bool:
|
|
315
331
|
if self.max is None:
|
|
316
332
|
return other.max is not None
|
|
317
333
|
|
|
@@ -326,7 +342,7 @@ class VersionRange(VersionConstraint):
|
|
|
326
342
|
|
|
327
343
|
return self.include_max and not other.include_max
|
|
328
344
|
|
|
329
|
-
def is_strictly_lower(self, other
|
|
345
|
+
def is_strictly_lower(self, other: "VersionRange") -> bool:
|
|
330
346
|
if self.max is None or other.min is None:
|
|
331
347
|
return False
|
|
332
348
|
|
|
@@ -338,14 +354,19 @@ class VersionRange(VersionConstraint):
|
|
|
338
354
|
|
|
339
355
|
return not self.include_max or not other.include_min
|
|
340
356
|
|
|
341
|
-
def is_strictly_higher(self, other
|
|
357
|
+
def is_strictly_higher(self, other: "VersionRange") -> bool:
|
|
342
358
|
return other.is_strictly_lower(self)
|
|
343
359
|
|
|
344
|
-
def is_adjacent_to(self, other
|
|
360
|
+
def is_adjacent_to(self, other: "VersionRange") -> bool:
|
|
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):
|
|
@@ -370,7 +391,7 @@ class VersionRange(VersionConstraint):
|
|
|
370
391
|
def __ge__(self, other):
|
|
371
392
|
return self._cmp(other) >= 0
|
|
372
393
|
|
|
373
|
-
def _cmp(self, other
|
|
394
|
+
def _cmp(self, other: "VersionRange") -> int:
|
|
374
395
|
if self.min is None:
|
|
375
396
|
if other.min is None:
|
|
376
397
|
return self._compare_max(other)
|
|
@@ -388,7 +409,7 @@ class VersionRange(VersionConstraint):
|
|
|
388
409
|
|
|
389
410
|
return self._compare_max(other)
|
|
390
411
|
|
|
391
|
-
def _compare_max(self, other
|
|
412
|
+
def _compare_max(self, other: "VersionRange") -> int:
|
|
392
413
|
if self.max is None:
|
|
393
414
|
if other.max is None:
|
|
394
415
|
return 0
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
from typing import List
|
|
1
|
+
from typing import TYPE_CHECKING, List
|
|
2
2
|
|
|
3
3
|
import semver
|
|
4
4
|
|
|
5
5
|
from .empty_constraint import EmptyConstraint
|
|
6
6
|
from .version_constraint import VersionConstraint
|
|
7
7
|
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from .version_range import VersionRange
|
|
10
|
+
|
|
8
11
|
|
|
9
12
|
class VersionUnion(VersionConstraint):
|
|
10
13
|
"""
|
|
@@ -57,7 +60,10 @@ class VersionUnion(VersionConstraint):
|
|
|
57
60
|
merged = []
|
|
58
61
|
for constraint in flattened:
|
|
59
62
|
# Merge this constraint with the previous one, but only if they touch.
|
|
60
|
-
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
|
+
):
|
|
61
67
|
merged.append(constraint)
|
|
62
68
|
else:
|
|
63
69
|
merged[-1] = merged[-1].union(constraint)
|
|
@@ -73,10 +79,10 @@ class VersionUnion(VersionConstraint):
|
|
|
73
79
|
def is_any(self):
|
|
74
80
|
return False
|
|
75
81
|
|
|
76
|
-
def allows(self, version
|
|
82
|
+
def allows(self, version: semver.Version) -> bool:
|
|
77
83
|
return any([constraint.allows(version) for constraint in self._ranges])
|
|
78
84
|
|
|
79
|
-
def allows_all(self, other
|
|
85
|
+
def allows_all(self, other: VersionConstraint) -> bool:
|
|
80
86
|
our_ranges = iter(self._ranges)
|
|
81
87
|
their_ranges = iter(self._ranges_for(other))
|
|
82
88
|
|
|
@@ -91,7 +97,7 @@ class VersionUnion(VersionConstraint):
|
|
|
91
97
|
|
|
92
98
|
return their_current_range is None
|
|
93
99
|
|
|
94
|
-
def allows_any(self, other
|
|
100
|
+
def allows_any(self, other: VersionConstraint) -> bool:
|
|
95
101
|
our_ranges = iter(self._ranges)
|
|
96
102
|
their_ranges = iter(self._ranges_for(other))
|
|
97
103
|
|
|
@@ -109,7 +115,7 @@ class VersionUnion(VersionConstraint):
|
|
|
109
115
|
|
|
110
116
|
return False
|
|
111
117
|
|
|
112
|
-
def intersect(self, other
|
|
118
|
+
def intersect(self, other: VersionConstraint) -> VersionConstraint:
|
|
113
119
|
our_ranges = iter(self._ranges)
|
|
114
120
|
their_ranges = iter(self._ranges_for(other))
|
|
115
121
|
new_ranges = []
|
|
@@ -130,10 +136,10 @@ class VersionUnion(VersionConstraint):
|
|
|
130
136
|
|
|
131
137
|
return VersionUnion.of(*new_ranges)
|
|
132
138
|
|
|
133
|
-
def union(self, other
|
|
139
|
+
def union(self, other: VersionConstraint) -> VersionConstraint:
|
|
134
140
|
return VersionUnion.of(self, other)
|
|
135
141
|
|
|
136
|
-
def difference(self, other
|
|
142
|
+
def difference(self, other: VersionConstraint) -> VersionConstraint:
|
|
137
143
|
our_ranges = iter(self._ranges)
|
|
138
144
|
their_ranges = iter(self._ranges_for(other))
|
|
139
145
|
new_ranges = []
|
|
@@ -213,7 +219,7 @@ class VersionUnion(VersionConstraint):
|
|
|
213
219
|
|
|
214
220
|
return VersionUnion.of(*new_ranges)
|
|
215
221
|
|
|
216
|
-
def _ranges_for(self, constraint
|
|
222
|
+
def _ranges_for(self, constraint: VersionConstraint) -> List["VersionRange"]:
|
|
217
223
|
from .version_range import VersionRange
|
|
218
224
|
|
|
219
225
|
if constraint.is_empty():
|
|
@@ -227,7 +233,7 @@ class VersionUnion(VersionConstraint):
|
|
|
227
233
|
|
|
228
234
|
raise ValueError("Unknown VersionConstraint type {}".format(constraint))
|
|
229
235
|
|
|
230
|
-
def _excludes_single_version(self)
|
|
236
|
+
def _excludes_single_version(self) -> bool:
|
|
231
237
|
from .version import Version
|
|
232
238
|
from .version_range import VersionRange
|
|
233
239
|
|
|
File without changes
|
{requirements_detector-1.3.1 → requirements_detector-1.4.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,49 +0,0 @@
|
|
|
1
|
-
[tool.poetry]
|
|
2
|
-
name = "requirements-detector"
|
|
3
|
-
version = "1.3.1"
|
|
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
|
-
|
|
27
|
-
[tool.poetry.scripts]
|
|
28
|
-
detect-requirements = 'requirements_detector.run:run'
|
|
29
|
-
|
|
30
|
-
[tool.poetry.dependencies]
|
|
31
|
-
python = ">=3.8,<4.0"
|
|
32
|
-
astroid = "^3.0"
|
|
33
|
-
packaging = ">=21.3"
|
|
34
|
-
toml = "^0.10.2"
|
|
35
|
-
semver = "^3.0.0"
|
|
36
|
-
|
|
37
|
-
[tool.poetry.dev-dependencies]
|
|
38
|
-
tox = "^3.24.5"
|
|
39
|
-
pre-commit = "^2.17.0"
|
|
40
|
-
pytest = "^6.2.4"
|
|
41
|
-
coverage = "^5.5"
|
|
42
|
-
twine = "^3.8.0"
|
|
43
|
-
pytest-benchmark = "^3.4.1"
|
|
44
|
-
pytest-cov = "^2.12.1"
|
|
45
|
-
types-toml = "^0.10.4"
|
|
46
|
-
|
|
47
|
-
[build-system]
|
|
48
|
-
requires = ["poetry-core>=1.0.0"]
|
|
49
|
-
build-backend = "poetry.core.masonry.api"
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import semver
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class VersionConstraint:
|
|
5
|
-
def is_empty(self): # type: () -> bool
|
|
6
|
-
raise NotImplementedError()
|
|
7
|
-
|
|
8
|
-
def is_any(self): # type: () -> bool
|
|
9
|
-
raise NotImplementedError()
|
|
10
|
-
|
|
11
|
-
def allows(self, version): # type: (semver.Version) -> bool
|
|
12
|
-
raise NotImplementedError()
|
|
13
|
-
|
|
14
|
-
def allows_all(self, other): # type: (VersionConstraint) -> bool
|
|
15
|
-
raise NotImplementedError()
|
|
16
|
-
|
|
17
|
-
def allows_any(self, other): # type: (VersionConstraint) -> bool
|
|
18
|
-
raise NotImplementedError()
|
|
19
|
-
|
|
20
|
-
def intersect(self, other): # type: (VersionConstraint) -> VersionConstraint
|
|
21
|
-
raise NotImplementedError()
|
|
22
|
-
|
|
23
|
-
def union(self, other): # type: (VersionConstraint) -> VersionConstraint
|
|
24
|
-
raise NotImplementedError()
|
|
25
|
-
|
|
26
|
-
def difference(self, other): # type: (VersionConstraint) -> VersionConstraint
|
|
27
|
-
raise NotImplementedError()
|
|
File without changes
|
{requirements_detector-1.3.1 → requirements_detector-1.4.0}/requirements_detector/__main__.py
RENAMED
|
File without changes
|
{requirements_detector-1.3.1 → requirements_detector-1.4.0}/requirements_detector/exceptions.py
RENAMED
|
File without changes
|
{requirements_detector-1.3.1 → requirements_detector-1.4.0}/requirements_detector/formatters.py
RENAMED
|
File without changes
|
{requirements_detector-1.3.1 → requirements_detector-1.4.0}/requirements_detector/handle_setup.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|