diff --git a/changelog/745.bugfix.rst b/changelog/745.bugfix.rst new file mode 100644 index 00000000..a06ed28c --- /dev/null +++ b/changelog/745.bugfix.rst @@ -0,0 +1 @@ +Fixed a regression that was causing some namespace packages with dots in them fail to upload to PyPI. diff --git a/tests/test_package.py b/tests/test_package.py index c6de1976..467e8758 100644 --- a/tests/test_package.py +++ b/tests/test_package.py @@ -11,6 +11,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +import string + import pretend import pytest @@ -110,6 +112,28 @@ def test_package_signed_name_is_correct(): assert package.signed_filename == (filename + ".asc") +@pytest.mark.parametrize( + "pkg_name,expected_name", + [ + (string.ascii_letters, string.ascii_letters), + (string.digits, string.digits), + (string.punctuation, "-.-"), + ("mosaik.SimConfig", "mosaik.SimConfig"), + ("mosaik$$$$.SimConfig", "mosaik-.SimConfig"), + ], +) +def test_package_safe_name_is_correct(pkg_name, expected_name): + package = package_file.PackageFile( + filename="tests/fixtures/deprecated-pypirc", + comment=None, + metadata=pretend.stub(name=pkg_name), + python_version=None, + filetype=None, + ) + + assert package.safe_name == expected_name + + @pytest.mark.parametrize("gpg_signature", [(None), (pretend.stub())]) def test_metadata_dictionary(gpg_signature): meta = pretend.stub( diff --git a/twine/package.py b/twine/package.py index 0b68bc9c..745752ff 100644 --- a/twine/package.py +++ b/twine/package.py @@ -14,11 +14,11 @@ import hashlib import io import os +import re import subprocess from typing import Dict, NamedTuple, Optional, Sequence, Tuple, Union import importlib_metadata -import packaging.utils import pkginfo from twine import exceptions @@ -44,6 +44,17 @@ MetadataValue = Union[str, Sequence[str]] +def _safe_name(name: str) -> str: + """Convert an arbitrary string to a standard distribution name. + + Any runs of non-alphanumeric/. characters are replaced with a single '-'. + + Copied from pkg_resources.safe_name for compatibility with warehouse. + See https://github.com/pypa/twine/issues/743. + """ + return re.sub("[^A-Za-z0-9.]+", "-", name) + + class PackageFile: def __init__( self, @@ -59,7 +70,7 @@ def __init__( self.metadata = metadata self.python_version = python_version self.filetype = filetype - self.safe_name = packaging.utils.canonicalize_name(metadata.name) + self.safe_name = _safe_name(metadata.name) self.signed_filename = self.filename + ".asc" self.signed_basefilename = self.basefilename + ".asc" self.gpg_signature: Optional[Tuple[str, bytes]] = None