@@ -26,26 +26,66 @@ def load_requirements(*requirements_paths):
26
26
"""
27
27
Load all requirements from the specified requirements files.
28
28
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.
31
32
"""
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
33
58
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
39
76
40
77
41
78
def is_requirement (line ):
42
79
"""
43
80
Return True if the requirement line is a package requirement.
44
81
45
82
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
47
85
"""
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' ))
49
89
50
90
51
91
VERSION = get_version ('user_tasks' , '__init__.py' )
0 commit comments