66from dataclasses import dataclass
77from typing import Iterable , Optional , Tuple
88
9+ from packaging .utils import canonicalize_name as canonicalize_project_name
910from pkg_resources import Requirement , parse_requirements
1011
1112from pants .backend .python .rules .pex import (
@@ -188,11 +189,13 @@ async def pex_from_targets(request: PexFromTargetsRequest, python_setup: PythonS
188189 description = request .description
189190
190191 if python_setup .requirement_constraints :
191- # In requirement strings Foo_Bar and foo-bar refer to the same project.
192- def canonical ( name : str ) -> str :
193- return name . lower (). replace ( "_" , "-" )
192+ # In requirement strings Foo_-Bar.BAZ and foo-bar-baz refer to the same project. We let
193+ # packaging canonicalize for us.
194+ # See: https://www.python.org/dev/peps/pep-0503/#normalized-names
194195
195- exact_req_projects = {canonical (Requirement .parse (req ).project_name ) for req in exact_reqs }
196+ exact_req_projects = {
197+ canonicalize_project_name (Requirement .parse (req ).project_name ) for req in exact_reqs
198+ }
196199 constraints_file_contents = await Get (
197200 DigestContents ,
198201 PathGlobs (
@@ -205,7 +208,9 @@ def canonical(name: str) -> str:
205208 constraints_file_reqs = set (
206209 parse_requirements (next (iter (constraints_file_contents )).content .decode ())
207210 )
208- constraint_file_projects = {canonical (req .project_name ) for req in constraints_file_reqs }
211+ constraint_file_projects = {
212+ canonicalize_project_name (req .project_name ) for req in constraints_file_reqs
213+ }
209214 unconstrained_projects = exact_req_projects - constraint_file_projects
210215 if unconstrained_projects :
211216 logger .warning (
0 commit comments