@@ -32,20 +32,48 @@ def load_requirements(*requirements_paths):
3232 """
3333 # UPDATED VIA SEMGREP - if you need to remove/modify this method remove this line and add a comment specifying why.
3434
35+ # e.g. {"django": "Django", "confluent-kafka": "confluent_kafka[avro]"}
36+ by_canonical_name = {}
37+
38+ def check_name_consistent (package ):
39+ """
40+ Raise exception if package is named different ways.
41+
42+ This ensures that packages are named consistently so we can match
43+ constraints to packages. It also ensures that if we require a package
44+ with extras we don't constrain it without mentioning the extras (since
45+ that too would interfere with matching constraints.)
46+ """
47+ canonical = package .lower ().replace ('_' , '-' ).split ('[' )[0 ]
48+ seen_spelling = by_canonical_name .get (canonical )
49+ if seen_spelling is None :
50+ by_canonical_name [canonical ] = package
51+ elif seen_spelling != package :
52+ raise Exception (
53+ f'Encountered both "{ seen_spelling } " and "{ package } " in requirements '
54+ 'and constraints files; please use just one or the other.'
55+ )
56+
3557 requirements = {}
3658 constraint_files = set ()
3759
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]+)?" )
60+ # groups "pkg<=x.y.z,..." into ("pkg", "<=x.y.z,...")
61+ re_package_name_base_chars = r"a-zA-Z0-9\-_." # chars allowed in base package name
62+ # Two groups: name[maybe,extras], and optionally a constraint
63+ requirement_line_regex = re .compile (
64+ r"([%s]+(?:\[[%s,\s]+\])?)([<>=][^#\s]+)?"
65+ % (re_package_name_base_chars , re_package_name_base_chars )
66+ )
4067
4168 def add_version_constraint_or_raise (current_line , current_requirements , add_if_not_present ):
4269 regex_match = requirement_line_regex .match (current_line )
4370 if regex_match :
4471 package = regex_match .group (1 )
4572 version_constraints = regex_match .group (2 )
73+ check_name_consistent (package )
4674 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
75+ # It 's fine to add constraints to an unconstrained package,
76+ # but raise an error if there are already constraints in place.
4977 if existing_version_constraints and existing_version_constraints != version_constraints :
5078 raise BaseException (f'Multiple constraint definitions found for { package } :'
5179 f' "{ existing_version_constraints } " and "{ version_constraints } ".'
@@ -54,7 +82,8 @@ def add_version_constraint_or_raise(current_line, current_requirements, add_if_n
5482 if add_if_not_present or package in current_requirements :
5583 current_requirements [package ] = version_constraints
5684
57- # process .in files and store the path to any constraint files that are pulled in
85+ # Read requirements from .in files and store the path to any
86+ # constraint files that are pulled in.
5887 for path in requirements_paths :
5988 with open (path ) as reqs :
6089 for line in reqs :
@@ -63,7 +92,7 @@ def add_version_constraint_or_raise(current_line, current_requirements, add_if_n
6392 if line and line .startswith ('-c' ) and not line .startswith ('-c http' ):
6493 constraint_files .add (os .path .dirname (path ) + '/' + line .split ('#' )[0 ].replace ('-c' , '' ).strip ())
6594
66- # process constraint files and add any new constraints found to existing requirements
95+ # process constraint files: add constraints to existing requirements
6796 for constraint_file in constraint_files :
6897 with open (constraint_file ) as reader :
6998 for line in reader :
0 commit comments