Skip to content

Commit

Permalink
Standardize canonicalization of PyPI names
Browse files Browse the repository at this point in the history
  • Loading branch information
maresb committed Jul 21, 2023
1 parent 42e1e28 commit cad88fc
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 16 deletions.
2 changes: 1 addition & 1 deletion conda_lock/lockfile/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def dep_name(manager: str, dep: str) -> str:
# If we operate on lists of pip names and this is a conda dependency, we
# convert the name to a pip name.
if convert_to_pip_names and manager == "conda":
return conda_name_to_pypi_name(dep).lower()
return conda_name_to_pypi_name(dep)
return dep

for name, request in requested.items():
Expand Down
19 changes: 11 additions & 8 deletions conda_lock/lookup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
import requests
import yaml

from packaging.utils import NormalizedName, canonicalize_name
from typing_extensions import TypedDict


class MappingEntry(TypedDict):
conda_name: str
# legacy field, generally not used by anything anymore
conda_forge: str
pypi_name: str
pypi_name: NormalizedName


class _LookupLoader:
Expand All @@ -28,15 +29,15 @@ def mapping_url(self, value: str) -> None:
self._mapping_url = value

@cached_property
def pypi_lookup(self) -> Dict[str, MappingEntry]:
def pypi_lookup(self) -> Dict[NormalizedName, MappingEntry]:
res = requests.get(self._mapping_url)
res.raise_for_status()
lookup = yaml.safe_load(res.content)
# lowercase and kebabcase the pypi names
assert lookup is not None
lookup = {k.lower().replace("_", "-"): v for k, v in lookup.items()}
lookup = {canonicalize_name(k): v for k, v in lookup.items()}
for v in lookup.values():
v["pypi_name"] = v["pypi_name"].lower().replace("_", "-")
v["pypi_name"] = canonicalize_name(v["pypi_name"])
return lookup

@cached_property
Expand All @@ -47,7 +48,7 @@ def conda_lookup(self) -> Dict[str, MappingEntry]:
LOOKUP_OBJECT = _LookupLoader()


def get_forward_lookup() -> Dict[str, MappingEntry]:
def get_forward_lookup() -> Dict[NormalizedName, MappingEntry]:
global LOOKUP_OBJECT
return LOOKUP_OBJECT.pypi_lookup

Expand All @@ -65,12 +66,14 @@ def set_lookup_location(lookup_url: str) -> None:
LOOKUP_OBJECT.mapping_url = lookup_url


def conda_name_to_pypi_name(name: str) -> str:
def conda_name_to_pypi_name(name: str) -> NormalizedName:
"""return the pypi name for a conda package"""
lookup = get_lookup()
return lookup.get(name, {"pypi_name": name})["pypi_name"]
cname = canonicalize_name(name)
return lookup.get(cname, {"pypi_name": cname})["pypi_name"]


def pypi_name_to_conda_name(name: str) -> str:
"""return the conda name for a pypi package"""
return get_forward_lookup().get(name, {"conda_name": name})["conda_name"]
cname = canonicalize_name(name)
return get_forward_lookup().get(cname, {"conda_name": cname})["conda_name"]
2 changes: 1 addition & 1 deletion conda_lock/pypi_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ def solve_pypi(
# is essentially a dictionary of:
# - pip package name -> list of LockedDependency that are needed for this package
for conda_name, locked_dep in conda_locked.items():
pypi_name = conda_name_to_pypi_name(conda_name).lower()
pypi_name = conda_name_to_pypi_name(conda_name)
if pypi_name in planned:
planned[pypi_name].append(locked_dep)
else:
Expand Down
16 changes: 10 additions & 6 deletions conda_lock/src_parser/pyproject_toml.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,19 @@ def join_version_components(pieces: Sequence[Union[str, int]]) -> str:


def normalize_pypi_name(name: str) -> str:
name = name.replace("_", "-").lower()
if name in get_lookup():
lookup = get_lookup()[name]
cname = canonicalize_pypi_name(name)
if cname in get_lookup():
lookup = get_lookup()[cname]
res = lookup.get("conda_name") or lookup.get("conda_forge")
if res is not None:
return res
else:
logging.warning(f"Could not find conda name for {name}. Assuming identity.")
return name
logging.warning(
f"Could not find conda name for {cname}. Assuming identity."
)
return cname
else:
return name
return cname


def poetry_version_to_conda_version(version_string: Optional[str]) -> Optional[str]:
Expand Down Expand Up @@ -377,6 +379,8 @@ def parse_requirement_specifier(
# Handle the case where only the URL is specified without a package name
repo_name_and_maybe_tag = requirement.split("/")[-1]
repo_name = repo_name_and_maybe_tag.split("@")[0]
if repo_name.endswith(".git"):
repo_name = repo_name[:-4]
# Use the repo name as a placeholder for the package name
return Requirement(f"{repo_name} @ {requirement}")
else:
Expand Down

0 comments on commit cad88fc

Please sign in to comment.