Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-hatchling for 
openSUSE:Factory checked in at 2026-06-04 18:52:57
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-hatchling (Old)
 and      /work/SRC/openSUSE:Factory/.python-hatchling.new.2375 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-hatchling"

Thu Jun  4 18:52:57 2026 rev:35 rq:1357010 version:1.30.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-hatchling/python-hatchling.changes        
2026-03-01 22:13:47.623420746 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-hatchling.new.2375/python-hatchling.changes  
    2026-06-04 18:53:50.895718639 +0200
@@ -1,0 +2,11 @@
+Tue Jun  2 19:18:51 UTC 2026 - BenoĆ®t Monin <[email protected]>
+
+- update to version 1.30.1:
+  * Fixed: Default core metadata version kept at 2.4 until more
+    tools support 2.5
+- additional changes from version 1.30.0:
+  * Added: Support PEP 794 (core metadata Import-Name and
+    Import-Namespace fields and version 2.5)
+  * Fixed: Exclude Git worktree metadata files from sdists
+
+-------------------------------------------------------------------

Old:
----
  hatchling-1.29.0.tar.gz

New:
----
  hatchling-1.30.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-hatchling.spec ++++++
--- /var/tmp/diff_new_pack.Xgkle2/_old  2026-06-04 18:53:52.563787443 +0200
+++ /var/tmp/diff_new_pack.Xgkle2/_new  2026-06-04 18:53:52.567787608 +0200
@@ -24,7 +24,7 @@
 %{?pythons_for_pypi}
 %{?sle15_python_module_pythons}
 Name:           python-hatchling
-Version:        1.29.0
+Version:        1.30.1
 Release:        0
 Summary:        Build backend used by Hatch
 License:        MIT

++++++ hatchling-1.29.0.tar.gz -> hatchling-1.30.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hatchling-1.29.0/PKG-INFO 
new/hatchling-1.30.1/PKG-INFO
--- old/hatchling-1.29.0/PKG-INFO       2020-02-02 01:00:00.000000000 +0100
+++ new/hatchling-1.30.1/PKG-INFO       2020-02-02 01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: hatchling
-Version: 1.29.0
+Version: 1.30.1
 Summary: Modern, extensible Python build backend
 Project-URL: Homepage, https://hatch.pypa.io/latest/
 Project-URL: Sponsor, https://github.com/sponsors/ofek
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hatchling-1.29.0/src/hatchling/__about__.py 
new/hatchling-1.30.1/src/hatchling/__about__.py
--- old/hatchling-1.29.0/src/hatchling/__about__.py     2020-02-02 
01:00:00.000000000 +0100
+++ new/hatchling-1.30.1/src/hatchling/__about__.py     2020-02-02 
01:00:00.000000000 +0100
@@ -1 +1 @@
-__version__ = "1.29.0"
+__version__ = "1.30.1"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hatchling-1.29.0/src/hatchling/builders/constants.py 
new/hatchling-1.30.1/src/hatchling/builders/constants.py
--- old/hatchling-1.29.0/src/hatchling/builders/constants.py    2020-02-02 
01:00:00.000000000 +0100
+++ new/hatchling-1.30.1/src/hatchling/builders/constants.py    2020-02-02 
01:00:00.000000000 +0100
@@ -27,6 +27,8 @@
 EXCLUDED_FILES = frozenset((
     # https://en.wikipedia.org/wiki/.DS_Store
     ".DS_Store",
+    # Git worktree metadata file
+    ".git",
 ))
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hatchling-1.29.0/src/hatchling/builders/wheel.py 
new/hatchling-1.30.1/src/hatchling/builders/wheel.py
--- old/hatchling-1.29.0/src/hatchling/builders/wheel.py        2020-02-02 
01:00:00.000000000 +0100
+++ new/hatchling-1.30.1/src/hatchling/builders/wheel.py        2020-02-02 
01:00:00.000000000 +0100
@@ -65,6 +65,21 @@
         self.__file_obj.close()
 
 
+class _WheelZipFile(zipfile.ZipFile):
+    def open(self, name, mode="r", pwd=None, *, force_zip64=False):
+        filename = name.filename if isinstance(name, zipfile.ZipInfo) else name
+        if mode == "w" and filename in self.NameToInfo:
+            message = (
+                f"A second file is being added to the wheel archive at the 
same path: `{filename}`.\n\n"
+                f"The most likely cause of this is an entry in the "
+                f"`tool.hatch.build.targets.wheel.force-include` table. See: "
+                f"https://hatch.pypa.io/1.8/config/build/#forced-inclusion\n\n";
+            )
+            raise ValueError(message)
+
+        return super().open(name, mode, pwd, force_zip64=force_zip64)
+
+
 class WheelArchive:
     def __init__(self, project_id: str, *, reproducible: bool) -> None:
         """
@@ -82,7 +97,7 @@
 
         raw_fd, self.path = tempfile.mkstemp(suffix=".whl")
         self.fd = os.fdopen(raw_fd, "w+b")
-        self.zf = zipfile.ZipFile(self.fd, "w", 
compression=zipfile.ZIP_DEFLATED)
+        self.zf = _WheelZipFile(self.fd, "w", compression=zipfile.ZIP_DEFLATED)
 
     @staticmethod
     def get_reproducible_time_tuple() -> TIME_TUPLE:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hatchling-1.29.0/src/hatchling/metadata/core.py 
new/hatchling-1.30.1/src/hatchling/metadata/core.py
--- old/hatchling-1.29.0/src/hatchling/metadata/core.py 2020-02-02 
01:00:00.000000000 +0100
+++ new/hatchling-1.30.1/src/hatchling/metadata/core.py 2020-02-02 
01:00:00.000000000 +0100
@@ -389,6 +389,8 @@
         self._optional_dependencies_complex: dict[str, dict[str, Requirement]] 
| None = None
         self._optional_dependencies: dict[str, list[str]] | None = None
         self._dynamic: list[str] | None = None
+        self._import_names: list[str] | None = None
+        self._import_namespaces: list[str] | None = None
 
         # Indicates that the version has been successfully set dynamically
         self._version_set: bool = False
@@ -1338,6 +1340,75 @@
         return self._optional_dependencies
 
     @property
+    def import_names(self) -> list[str] | None:
+        """
+        https://peps.python.org/pep-0794/
+        """
+        if self._import_names is None:
+            if "import-names" not in self.config:
+                return None
+
+            import_names = self.config["import-names"]
+            if "import-names" in self.dynamic:
+                message = (
+                    "Metadata field `import-names` cannot be both statically 
defined and "
+                    "listed in field `project.dynamic`"
+                )
+                raise ValueError(message)
+
+            if not isinstance(import_names, list):
+                message = "Field `project.import-names` must be an array"
+                raise TypeError(message)
+
+            for i, import_name in enumerate(import_names, 1):
+                if not isinstance(import_name, str) or not 
self.__import_name_is_valid(import_name):
+                    message = f"Import name #{i} of field 
`project.import-names` must be a valid import name"
+                    raise TypeError(message)
+
+            self._import_names = sorted(import_names)
+
+            if set(self._import_names) & set(self.import_namespaces):
+                message = "Fields `project.import-names` and 
`project.import-namespaces` cannot contain the same name"
+                raise ValueError(message)
+
+        return self._import_names
+
+    @property
+    def import_namespaces(self) -> list[str]:
+        """
+        
https://packaging.python.org/en/latest/specifications/pyproject-toml/#import-namespaces
+        """
+        if self._import_namespaces is None:
+            if "import-namespaces" in self.config:
+                import_namespaces = self.config["import-namespaces"]
+                if "import-namespaces" in self.dynamic:
+                    message = (
+                        "Metadata field `import-namespaces` cannot be both 
statically defined and "
+                        "listed in field `project.dynamic`"
+                    )
+                    raise ValueError(message)
+            else:
+                import_namespaces = []
+
+            if not isinstance(import_namespaces, list):
+                message = "Field `project.import-namespaces` must be an array"
+                raise TypeError(message)
+
+            for i, import_namespace in enumerate(import_namespaces, 1):
+                if not isinstance(import_namespace, str) or not 
self.__import_name_is_valid(import_namespace):
+                    message = f"Import namespace #{i} of field 
`project.import-namespaces` must be a valid import name"
+                    raise TypeError(message)
+
+            self._import_namespaces = sorted(import_namespaces)
+
+            import_names = self.import_names
+            if import_names is not None and set(import_names) & 
set(self._import_namespaces):
+                message = "Fields `project.import-names` and 
`project.import-namespaces` cannot contain the same name"
+                raise ValueError(message)
+
+        return self._import_namespaces
+
+    @property
     def dynamic(self) -> list[str]:
         """
         https://peps.python.org/pep-0621/#dynamic
@@ -1369,6 +1440,10 @@
     def __classifier_is_private(classifier: str) -> bool:
         return classifier.lower().startswith("private ::")
 
+    @staticmethod
+    def __import_name_is_valid(import_name: str) -> bool:
+        return all(module.isidentifier() for module in import_name.split("."))
+
 
 class HatchMetadata(Generic[PluginManagerBound]):
     def __init__(self, root: str, config: dict[str, dict[str, Any]], 
plugin_manager: PluginManagerBound) -> None:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hatchling-1.29.0/src/hatchling/metadata/spec.py 
new/hatchling-1.30.1/src/hatchling/metadata/spec.py
--- old/hatchling-1.29.0/src/hatchling/metadata/spec.py 2020-02-02 
01:00:00.000000000 +0100
+++ new/hatchling-1.30.1/src/hatchling/metadata/spec.py 2020-02-02 
01:00:00.000000000 +0100
@@ -8,7 +8,7 @@
     from hatchling.metadata.core import ProjectMetadata
 
 DEFAULT_METADATA_VERSION = "2.4"
-LATEST_METADATA_VERSION = "2.4"
+LATEST_METADATA_VERSION = "2.5"
 CORE_METADATA_PROJECT_FIELDS = {
     "Author": ("authors",),
     "Author-email": ("authors",),
@@ -29,6 +29,8 @@
     "Summary": ("description",),
     "Project-URL": ("urls",),
     "Version": ("version",),
+    "Import-Name": ("import-names",),
+    "Import-Namespace": ("import-namespaces",),
 }
 PROJECT_CORE_METADATA_FIELDS = {
     "authors": ("Author", "Author-email"),
@@ -46,6 +48,8 @@
     "description": ("Summary",),
     "urls": ("Project-URL",),
     "version": ("Version",),
+    "import-names": ("Import-Name",),
+    "import-namespaces": ("Import-Namespace",),
 }
 
 
@@ -59,6 +63,7 @@
         "2.2": construct_metadata_file_2_2,
         "2.3": construct_metadata_file_2_3,
         "2.4": construct_metadata_file_2_4,
+        "2.5": construct_metadata_file_2_5,
     }
 
 
@@ -196,6 +201,16 @@
     if optional_dependencies:
         metadata["optional-dependencies"] = optional_dependencies
 
+    if (import_names := message.get_all("Import-Name")) is not None:
+        metadata["import-names"] = import_names
+
+    if (import_namespaces := message.get_all("Import-Namespace")) is not None:
+        metadata["import-namespaces"] = import_namespaces
+
+    if set(metadata.get("import-names", [])) & 
set(metadata.get("import-namespaces", [])):
+        error_message = "Import-Name and Import-Namespace fields cannot 
contain the same name"
+        raise ValueError(error_message)
+
     return metadata
 
 
@@ -519,6 +534,115 @@
 
     if metadata.core.dynamic:
         # Ordered set
+        for field in {
+            core_metadata_field: None
+            for project_field in metadata.core.dynamic
+            for core_metadata_field in 
PROJECT_CORE_METADATA_FIELDS.get(project_field, ())
+        }:
+            metadata_file += f"Dynamic: {field}\n"
+
+    if metadata.core.description:
+        metadata_file += f"Summary: {metadata.core.description}\n"
+
+    if metadata.core.urls:
+        for label, url in metadata.core.urls.items():
+            metadata_file += f"Project-URL: {label}, {url}\n"
+
+    authors_data = metadata.core.authors_data
+    if authors_data["name"]:
+        metadata_file += f"Author: {', '.join(authors_data['name'])}\n"
+    if authors_data["email"]:
+        metadata_file += f"Author-email: {', '.join(authors_data['email'])}\n"
+
+    maintainers_data = metadata.core.maintainers_data
+    if maintainers_data["name"]:
+        metadata_file += f"Maintainer: {', '.join(maintainers_data['name'])}\n"
+    if maintainers_data["email"]:
+        metadata_file += f"Maintainer-email: {', 
'.join(maintainers_data['email'])}\n"
+
+    if metadata.core.license:
+        license_start = "License: "
+        indent = " " * (len(license_start) - 1)
+        metadata_file += license_start
+
+        for i, line in enumerate(metadata.core.license.splitlines()):
+            if i == 0:
+                metadata_file += f"{line}\n"
+            else:
+                metadata_file += f"{indent}{line}\n"
+
+    if metadata.core.license_expression:
+        metadata_file += f"License-Expression: 
{metadata.core.license_expression}\n"
+
+    if metadata.core.license_files:
+        for license_file in metadata.core.license_files:
+            metadata_file += f"License-File: {license_file}\n"
+
+    if metadata.core.keywords:
+        metadata_file += f"Keywords: {','.join(metadata.core.keywords)}\n"
+
+    if metadata.core.classifiers:
+        for classifier in metadata.core.classifiers:
+            metadata_file += f"Classifier: {classifier}\n"
+
+    if metadata.core.requires_python:
+        metadata_file += f"Requires-Python: {metadata.core.requires_python}\n"
+
+    if metadata.core.dependencies:
+        for dependency in metadata.core.dependencies:
+            metadata_file += f"Requires-Dist: {dependency}\n"
+
+    if extra_dependencies:
+        for dependency in extra_dependencies:
+            metadata_file += f"Requires-Dist: {dependency}\n"
+
+    if metadata.core.optional_dependencies:
+        for option, dependencies in 
metadata.core.optional_dependencies.items():
+            metadata_file += f"Provides-Extra: {option}\n"
+            for dependency in dependencies:
+                if ";" in dependency:
+                    dep_name, dep_env_marker = dependency.split(";", 
maxsplit=1)
+                    metadata_file += f"Requires-Dist: {dep_name}; 
({dep_env_marker.strip()}) and extra == {option!r}\n"
+                elif "@ " in dependency:
+                    metadata_file += f"Requires-Dist: {dependency} ; extra == 
{option!r}\n"
+                else:
+                    metadata_file += f"Requires-Dist: {dependency}; extra == 
{option!r}\n"
+
+    if metadata.core.readme:
+        metadata_file += f"Description-Content-Type: 
{metadata.core.readme_content_type}\n"
+        metadata_file += f"\n{metadata.core.readme}"
+
+    return metadata_file
+
+
+def construct_metadata_file_2_5(metadata: ProjectMetadata, extra_dependencies: 
tuple[str] | None = None) -> str:
+    """
+    https://peps.python.org/pep-0794/
+    """
+    metadata_file = "Metadata-Version: 2.5\n"
+    metadata_file += f"Name: {metadata.core.raw_name}\n"
+    metadata_file += f"Version: {metadata.version}\n"
+
+    if metadata.core.import_names is not None:
+        if not metadata.core.import_names and not 
metadata.core.import_namespaces:
+            # Projects MAY set `import-names` an empty array and not set 
`import-namespaces`
+            # at all in a `pyproject.toml` file (e.g. `import-names = []`). To 
match this,
+            # projects MAY have an empty `Import-Name` field in their 
metadata. This represents
+            # a project with NO import names, public or private (i.e. there 
are no Python modules
+            # of any kind in the distribution file).
+            metadata_file += "Import-Name\n"
+
+        for import_name in metadata.core.import_names:
+            _name = f"{import_name}; private" if import_name.startswith("_") 
else import_name
+            metadata_file += f"Import-Name: {_name}\n"
+
+    if metadata.core.import_namespaces:
+        for import_namespace in metadata.core.import_namespaces:
+            _name = f"{import_namespace}; private" if 
import_namespace.startswith("_") else import_namespace
+            metadata_file += f"Import-Namespace: {_name}\n"
+
+    if metadata.core.dynamic:
+        # Ordered set
         for field in {
             core_metadata_field: None
             for project_field in metadata.core.dynamic
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hatchling-1.29.0/tests/downstream/requirements.txt 
new/hatchling-1.30.1/tests/downstream/requirements.txt
--- old/hatchling-1.29.0/tests/downstream/requirements.txt      2020-02-02 
01:00:00.000000000 +0100
+++ new/hatchling-1.30.1/tests/downstream/requirements.txt      2020-02-02 
01:00:00.000000000 +0100
@@ -2,4 +2,4 @@
 packaging
 requests
 tomli
-virtualenv>=20.13.1
+virtualenv>=21

Reply via email to