Spaces:
Running
Running
""" | |
Helpers for normalization as expected in wheel/sdist/module file names | |
and core metadata | |
""" | |
import re | |
from pathlib import Path | |
from typing import Union | |
from .extern import packaging | |
from .warnings import SetuptoolsDeprecationWarning | |
_Path = Union[str, Path] | |
# https://packaging.python.org/en/latest/specifications/core-metadata/#name | |
_VALID_NAME = re.compile(r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$", re.I) | |
_UNSAFE_NAME_CHARS = re.compile(r"[^A-Z0-9.]+", re.I) | |
def safe_identifier(name: str) -> str: | |
"""Make a string safe to be used as Python identifier. | |
>>> safe_identifier("12abc") | |
'_12abc' | |
>>> safe_identifier("__editable__.myns.pkg-78.9.3_local") | |
'__editable___myns_pkg_78_9_3_local' | |
""" | |
safe = re.sub(r'\W|^(?=\d)', '_', name) | |
assert safe.isidentifier() | |
return safe | |
def safe_name(component: str) -> str: | |
"""Escape a component used as a project name according to Core Metadata. | |
>>> safe_name("hello world") | |
'hello-world' | |
>>> safe_name("hello?world") | |
'hello-world' | |
""" | |
# See pkg_resources.safe_name | |
return _UNSAFE_NAME_CHARS.sub("-", component) | |
def safe_version(version: str) -> str: | |
"""Convert an arbitrary string into a valid version string. | |
>>> safe_version("1988 12 25") | |
'1988.12.25' | |
>>> safe_version("v0.2.1") | |
'0.2.1' | |
>>> safe_version("v0.2?beta") | |
'0.2b0' | |
>>> safe_version("v0.2 beta") | |
'0.2b0' | |
>>> safe_version("ubuntu lts") | |
Traceback (most recent call last): | |
... | |
setuptools.extern.packaging.version.InvalidVersion: Invalid version: 'ubuntu.lts' | |
""" | |
v = version.replace(' ', '.') | |
try: | |
return str(packaging.version.Version(v)) | |
except packaging.version.InvalidVersion: | |
attempt = _UNSAFE_NAME_CHARS.sub("-", v) | |
return str(packaging.version.Version(attempt)) | |
def best_effort_version(version: str) -> str: | |
"""Convert an arbitrary string into a version-like string. | |
>>> best_effort_version("v0.2 beta") | |
'0.2b0' | |
>>> import warnings | |
>>> warnings.simplefilter("ignore", category=SetuptoolsDeprecationWarning) | |
>>> best_effort_version("ubuntu lts") | |
'ubuntu.lts' | |
""" | |
# See pkg_resources.safe_version | |
try: | |
return safe_version(version) | |
except packaging.version.InvalidVersion: | |
SetuptoolsDeprecationWarning.emit( | |
f"Invalid version: {version!r}.", | |
f""" | |
Version {version!r} is not valid according to PEP 440. | |
Please make sure to specify a valid version for your package. | |
Also note that future releases of setuptools may halt the build process | |
if an invalid version is given. | |
""", | |
see_url="https://peps.python.org/pep-0440/", | |
due_date=(2023, 9, 26), # See setuptools/dist _validate_version | |
) | |
v = version.replace(' ', '.') | |
return safe_name(v) | |
def filename_component(value: str) -> str: | |
"""Normalize each component of a filename (e.g. distribution/version part of wheel) | |
Note: ``value`` needs to be already normalized. | |
>>> filename_component("my-pkg") | |
'my_pkg' | |
""" | |
return value.replace("-", "_").strip("_") | |
def safer_name(value: str) -> str: | |
"""Like ``safe_name`` but can be used as filename component for wheel""" | |
# See bdist_wheel.safer_name | |
return filename_component(safe_name(value)) | |
def safer_best_effort_version(value: str) -> str: | |
"""Like ``best_effort_version`` but can be used as filename component for wheel""" | |
# See bdist_wheel.safer_verion | |
# TODO: Replace with only safe_version in the future (no need for best effort) | |
return filename_component(best_effort_version(value)) | |