Skip to content

Commit 1bcf22d

Browse files
feat: advertise constraints in setup.py (#182)
1 parent 6611908 commit 1bcf22d

File tree

2 files changed

+51
-10
lines changed

2 files changed

+51
-10
lines changed

MANIFEST.in

+1
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ include LICENSE.txt
44
include README.rst
55
include requirements/base.in
66
recursive-include user_tasks *.html *.png *.gif *js *.css *jpg *jpeg *svg *py
7+
include requirements/constraints.txt

setup.py

100755100644
+50-10
Original file line numberDiff line numberDiff line change
@@ -26,26 +26,66 @@ def load_requirements(*requirements_paths):
2626
"""
2727
Load all requirements from the specified requirements files.
2828
29-
Returns:
30-
list: Requirements file relative path strings
29+
Requirements will include any constraints from files specified
30+
with -c in the requirements files.
31+
Returns a list of requirement strings.
3132
"""
32-
requirements = set()
33+
# UPDATED VIA SEMGREP - if you need to remove/modify this method remove this line and add a comment specifying why.
34+
35+
requirements = {}
36+
constraint_files = set()
37+
38+
# groups "my-package-name<=x.y.z,..." into ("my-package-name", "<=x.y.z,...")
39+
requirement_line_regex = re.compile(r"([a-zA-Z0-9-_.]+)([<>=][^#\s]+)?")
40+
41+
def add_version_constraint_or_raise(current_line, current_requirements, add_if_not_present):
42+
regex_match = requirement_line_regex.match(current_line)
43+
if regex_match:
44+
package = regex_match.group(1)
45+
version_constraints = regex_match.group(2)
46+
existing_version_constraints = current_requirements.get(package, None)
47+
# it's fine to add constraints to an unconstrained package, but raise an error if there are already
48+
# constraints in place
49+
if existing_version_constraints and existing_version_constraints != version_constraints:
50+
raise BaseException(f'Multiple constraint definitions found for {package}:'
51+
f' "{existing_version_constraints}" and "{version_constraints}".'
52+
f'Combine constraints into one location with {package}'
53+
f'{existing_version_constraints},{version_constraints}.')
54+
if add_if_not_present or package in current_requirements:
55+
current_requirements[package] = version_constraints
56+
57+
# process .in files and store the path to any constraint files that are pulled in
3358
for path in requirements_paths:
34-
requirements.update(
35-
line.split('#')[0].strip() for line in open(path).readlines()
36-
if is_requirement(line.strip())
37-
)
38-
return list(requirements)
59+
with open(path) as reqs:
60+
for line in reqs:
61+
if is_requirement(line):
62+
add_version_constraint_or_raise(line, requirements, True)
63+
if line and line.startswith('-c') and not line.startswith('-c http'):
64+
constraint_files.add(os.path.dirname(path) + '/' + line.split('#')[0].replace('-c', '').strip())
65+
66+
# process constraint files and add any new constraints found to existing requirements
67+
for constraint_file in constraint_files:
68+
with open(constraint_file) as reader:
69+
for line in reader:
70+
if is_requirement(line):
71+
add_version_constraint_or_raise(line, requirements, False)
72+
73+
# process back into list of pkg><=constraints strings
74+
constrained_requirements = [f'{pkg}{version or ""}' for (pkg, version) in sorted(requirements.items())]
75+
return constrained_requirements
3976

4077

4178
def is_requirement(line):
4279
"""
4380
Return True if the requirement line is a package requirement.
4481
4582
Returns:
46-
bool: True if the line is not blank, a comment, a URL, or an included file
83+
bool: True if the line is not blank, a comment,
84+
a URL, or an included file
4785
"""
48-
return line and not line.startswith(('-r', '#', '-e', 'git+', '-c'))
86+
# UPDATED VIA SEMGREP - if you need to remove/modify this method remove this line and add a comment specifying why
87+
88+
return line and line.strip() and not line.startswith(('-r', '#', '-e', 'git+', '-c'))
4989

5090

5191
VERSION = get_version('user_tasks', '__init__.py')

0 commit comments

Comments
 (0)