Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-pdm-pep517 for 
openSUSE:Factory checked in at 2022-10-06 07:42:11
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pdm-pep517 (Old)
 and      /work/SRC/openSUSE:Factory/.python-pdm-pep517.new.2275 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-pdm-pep517"

Thu Oct  6 07:42:11 2022 rev:5 rq:1008164 version:1.0.4

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pdm-pep517/python-pdm-pep517.changes      
2022-08-03 21:17:19.735541420 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-pdm-pep517.new.2275/python-pdm-pep517.changes
    2022-10-06 07:42:21.268707392 +0200
@@ -1,0 +2,9 @@
+Tue Oct  4 22:57:46 UTC 2022 - Yogalakshmi Arunachalam <yarunacha...@suse.com>
+
+- Update to 1.0.4  
+  Bug Fixes
+  Fix a bug of editable install not reading run-setuptools = false config. #107
+  Put the long description in the body as specified by Metadata Version 2.1 
#109
+  Overwrite the existing files in the custom build stage of WheelBuilder. #114
+
+-------------------------------------------------------------------

Old:
----
  pdm-pep517-1.0.3.tar.gz

New:
----
  pdm-pep517-1.0.4.tar.gz

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

Other differences:
------------------
++++++ python-pdm-pep517.spec ++++++
--- /var/tmp/diff_new_pack.OhhB35/_old  2022-10-06 07:42:21.972708960 +0200
+++ /var/tmp/diff_new_pack.OhhB35/_new  2022-10-06 07:42:21.976708969 +0200
@@ -26,7 +26,7 @@
 %endif
 
 Name:           python-pdm-pep517%{psuffix}
-Version:        1.0.3
+Version:        1.0.4
 Release:        0
 Summary:        Python Development Master
 License:        MIT

++++++ pdm-pep517-1.0.3.tar.gz -> pdm-pep517-1.0.4.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-pep517-1.0.3/PKG-INFO 
new/pdm-pep517-1.0.4/PKG-INFO
--- old/pdm-pep517-1.0.3/PKG-INFO       2022-07-27 11:27:55.516363600 +0200
+++ new/pdm-pep517-1.0.4/PKG-INFO       2022-08-06 17:29:42.799394800 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: pdm-pep517
-Version: 1.0.3
+Version: 1.0.4
 Summary: A PEP 517 backend for PDM that supports PEP 621 metadata
 License: MIT
 Keywords: packaging,PEP 517,build
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-pep517-1.0.3/pdm/pep517/__init__.py 
new/pdm-pep517-1.0.4/pdm/pep517/__init__.py
--- old/pdm-pep517-1.0.3/pdm/pep517/__init__.py 2022-07-27 11:27:54.648356700 
+0200
+++ new/pdm-pep517-1.0.4/pdm/pep517/__init__.py 2022-08-06 17:29:41.923385100 
+0200
@@ -1 +1 @@
-__version__ = "1.0.3"
+__version__ = "1.0.4"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-pep517-1.0.3/pdm/pep517/api.py 
new/pdm-pep517-1.0.4/pdm/pep517/api.py
--- old/pdm-pep517-1.0.3/pdm/pep517/api.py      2022-07-27 11:27:54.656356800 
+0200
+++ new/pdm-pep517-1.0.4/pdm/pep517/api.py      2022-08-06 17:29:41.927385300 
+0200
@@ -1,8 +1,10 @@
 """
 PEP-517 compliant buildsystem API
 """
+from __future__ import annotations
+
 from pathlib import Path
-from typing import Any, List, Mapping, Optional
+from typing import Any, Mapping
 
 from pdm.pep517.editable import EditableBuilder
 from pdm.pep517.sdist import SdistBuilder
@@ -10,8 +12,8 @@
 
 
 def get_requires_for_build_wheel(
-    config_settings: Optional[Mapping[str, Any]] = None
-) -> List[str]:
+    config_settings: Mapping[str, Any] | None = None
+) -> list[str]:
     """
     Returns an additional list of requirements for building, as PEP508 strings,
     above and beyond those specified in the pyproject.toml file.
@@ -26,8 +28,8 @@
 
 
 def get_requires_for_build_sdist(
-    config_settings: Optional[Mapping[str, Any]] = None
-) -> List[str]:
+    config_settings: Mapping[str, Any] | None = None
+) -> list[str]:
     """There isn't any requirement for building a sdist at this point."""
     return []
 
@@ -55,7 +57,7 @@
 
 
 def prepare_metadata_for_build_wheel(
-    metadata_directory: str, config_settings: Optional[Mapping[str, Any]] = 
None
+    metadata_directory: str, config_settings: Mapping[str, Any] | None = None
 ) -> str:
     """Prepare the metadata, places it in metadata_directory"""
     with WheelBuilder(Path.cwd(), config_settings) as builder:
@@ -64,8 +66,8 @@
 
 def build_wheel(
     wheel_directory: str,
-    config_settings: Optional[Mapping[str, Any]] = None,
-    metadata_directory: Optional[str] = None,
+    config_settings: Mapping[str, Any] | None = None,
+    metadata_directory: str | None = None,
 ) -> str:
     """Builds a wheel, places it in wheel_directory"""
     with WheelBuilder(Path.cwd(), config_settings) as builder:
@@ -73,7 +75,7 @@
 
 
 def build_sdist(
-    sdist_directory: str, config_settings: Optional[Mapping[str, Any]] = None
+    sdist_directory: str, config_settings: Mapping[str, Any] | None = None
 ) -> str:
     """Builds an sdist, places it in sdist_directory"""
     with SdistBuilder(Path.cwd(), config_settings) as builder:
@@ -84,7 +86,7 @@
 
 
 def prepare_metadata_for_build_editable(
-    metadata_directory: str, config_settings: Optional[Mapping[str, Any]] = 
None
+    metadata_directory: str, config_settings: Mapping[str, Any] | None = None
 ) -> str:
     """Prepare the metadata, places it in metadata_directory"""
     with EditableBuilder(Path.cwd(), config_settings) as builder:
@@ -94,8 +96,8 @@
 
 def build_editable(
     wheel_directory: str,
-    config_settings: Optional[Mapping[str, Any]] = None,
-    metadata_directory: Optional[str] = None,
+    config_settings: Mapping[str, Any] | None = None,
+    metadata_directory: str | None = None,
 ) -> str:
     with EditableBuilder(Path.cwd(), config_settings) as builder:
         return Path(builder.build(wheel_directory)).name
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-pep517-1.0.3/pdm/pep517/base.py 
new/pdm-pep517-1.0.4/pdm/pep517/base.py
--- old/pdm-pep517-1.0.3/pdm/pep517/base.py     2022-07-27 11:27:54.656356800 
+0200
+++ new/pdm-pep517-1.0.4/pdm/pep517/base.py     2022-08-06 17:29:41.927385300 
+0200
@@ -1,20 +1,11 @@
+from __future__ import annotations
+
 import atexit
 import glob
 import os
 import warnings
 from pathlib import Path
-from typing import (
-    Any,
-    Dict,
-    Iterator,
-    List,
-    Mapping,
-    Optional,
-    Tuple,
-    TypeVar,
-    Union,
-    cast,
-)
+from typing import Any, Iterator, Mapping, TypeVar, cast
 
 from pdm.pep517._vendor import tomli
 from pdm.pep517.exceptions import MetadataError, PDMWarning, ProjectError
@@ -69,8 +60,8 @@
 
 
 def _merge_globs(
-    include_globs: Dict[str, str], excludes_globs: Dict[str, str]
-) -> Tuple[List[str], List[str]]:
+    include_globs: dict[str, str], excludes_globs: dict[str, str]
+) -> tuple[list[str], list[str]]:
     """Correctly merge includes and excludes.
     When a pattern exists in both includes and excludes,
     determine the priority in the following ways:
@@ -80,7 +71,7 @@
     3. If both have the same part number and concrete level, *excludes* wins
     """
 
-    def path_weight(pathname: str) -> Tuple[int, int]:
+    def path_weight(pathname: str) -> tuple[int, int]:
         """Return a two-element tuple [part_num, concrete_level]"""
         pathname_parts = Path(pathname).parts
         wildcard_count = 0
@@ -104,7 +95,7 @@
     return includes, list(excludes_globs)
 
 
-def _find_top_packages(root: str) -> List[str]:
+def _find_top_packages(root: str) -> list[str]:
     result = []
     for path in os.listdir(root):
         path = os.path.join(root, path)
@@ -113,7 +104,7 @@
     return result
 
 
-def _format_list(data: List[str], indent: int = 4) -> str:
+def _format_list(data: list[str], indent: int = 4) -> str:
     result = ["["]
     for row in data:
         result.append(" " * indent + repr(row) + ",")
@@ -121,7 +112,7 @@
     return "\n".join(result)
 
 
-def _format_dict_list(data: Dict[str, List[str]], indent: int = 4) -> str:
+def _format_dict_list(data: dict[str, list[str]], indent: int = 4) -> str:
     result = ["{"]
     for key, value in data.items():
         result.append(
@@ -138,15 +129,15 @@
 
     def __init__(
         self,
-        location: Union[str, Path],
-        config_settings: Optional[Mapping[str, Any]] = None,
+        location: str | Path,
+        config_settings: Mapping[str, Any] | None = None,
     ) -> None:
-        self._old_cwd: Optional[str] = None
+        self._old_cwd: str | None = None
         self.location = Path(location).absolute()
         self.config_settings = config_settings
-        self._meta: Optional[Metadata] = None
+        self._meta: Metadata | None = None
 
-    def _read_pyproject_toml(self) -> Dict[str, Any]:
+    def _read_pyproject_toml(self) -> dict[str, Any]:
         pyproject_toml = self.location / "pyproject.toml"
         if not pyproject_toml.exists():
             raise ProjectError("No pyproject.toml found")
@@ -184,7 +175,7 @@
 
     def _get_include_and_exclude_paths(
         self, for_sdist: bool = False
-    ) -> Tuple[List[str], List[str]]:
+    ) -> tuple[list[str], list[str]]:
         includes = set()
         excludes = set(self.DEFAULT_EXCLUDES)
 
@@ -221,7 +212,7 @@
         include_paths, exclude_paths = _merge_globs(include_globs, 
excludes_globs)
         return sorted(include_paths), sorted(exclude_paths)
 
-    def _is_excluded(self, path: str, exclude_paths: List[str]) -> bool:
+    def _is_excluded(self, path: str, exclude_paths: list[str]) -> bool:
         return any(
             is_same_or_descendant_path(path, exclude_path)
             for exclude_path in exclude_paths
@@ -259,7 +250,7 @@
         if os.path.isfile("pyproject.toml"):
             yield "pyproject.toml"
 
-    def find_files_to_add(self, for_sdist: bool = False) -> List[Path]:
+    def find_files_to_add(self, for_sdist: bool = False) -> list[Path]:
         """Traverse the project path and return a list of file names
         that should be included in a sdist distribution.
         If for_sdist is True, will include files like LICENSE, README and 
pyproject
@@ -267,7 +258,7 @@
         """
         return sorted({Path(p) for p in self._find_files_iter(for_sdist)})
 
-    def find_license_files(self) -> List[str]:
+    def find_license_files(self) -> list[str]:
         """Return a list of license files from the PEP 639 metadata."""
         license_files = self.meta.license_files
         if "paths" in license_files:
@@ -325,7 +316,7 @@
         if package_paths["packages"]:
             extra.append(
                 "    'packages': {},\n".format(
-                    _format_list(cast(List[str], package_paths["packages"]), 8)
+                    _format_list(cast("list[str]", package_paths["packages"]), 
8)
                 )
             )
         if package_paths["package_dir"]:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-pep517-1.0.3/pdm/pep517/editable.py 
new/pdm-pep517-1.0.4/pdm/pep517/editable.py
--- old/pdm-pep517-1.0.3/pdm/pep517/editable.py 2022-07-27 11:27:54.656356800 
+0200
+++ new/pdm-pep517-1.0.4/pdm/pep517/editable.py 2022-08-06 17:29:41.927385300 
+0200
@@ -1,13 +1,12 @@
-import hashlib
+from __future__ import annotations
+
 import os
 import subprocess
 import sys
 import tokenize
 import warnings
-import zipfile
-from base64 import urlsafe_b64encode
 from pathlib import Path
-from typing import Any, Dict, Iterable, List, Mapping, Optional, TextIO, 
Tuple, Union
+from typing import Any, Iterable, Mapping, TextIO
 
 from pdm.pep517.exceptions import BuildError, PDMWarning
 from pdm.pep517.utils import is_relative_path, show_warning, to_filename
@@ -20,8 +19,8 @@
     def __init__(self, project_name: str, project_dir: str) -> None:
         self.project_name = project_name
         self.project_dir = Path(project_dir)
-        self.redirections: Dict[str, str] = {}
-        self.path_entries: List[Path] = []
+        self.redirections: dict[str, str] = {}
+        self.path_entries: list[Path] = []
 
     def make_absolute(self, path: str) -> Path:
         return (self.project_dir / path).resolve()
@@ -40,7 +39,7 @@
     def add_to_path(self, dirname: str) -> None:
         self.path_entries.append(self.make_absolute(dirname))
 
-    def files(self) -> Iterable[Tuple[str, str]]:
+    def files(self) -> Iterable[tuple[str, str]]:
         yield f"{self.project_name}.pth", self.pth_file()
         if self.redirections:
             yield f"__editables_{self.project_name}.py", self.bootstrap_file()
@@ -50,7 +49,7 @@
             yield "editables"
 
     def pth_file(self) -> str:
-        lines: List[str] = []
+        lines: list[str] = []
         if self.redirections:
             lines.append(f"import __editables_{self.project_name}")
         for entry in self.path_entries:
@@ -69,7 +68,7 @@
 
 class EditableBuilder(WheelBuilder):
     def __init__(
-        self, location: Union[str, Path], config_settings: 
Optional[Mapping[str, Any]]
+        self, location: str | Path, config_settings: Mapping[str, Any] | None
     ) -> None:
         super().__init__(location, config_settings=config_settings)
         assert self.meta.project_name, "Project name is not specified"
@@ -77,7 +76,7 @@
             to_filename(self.meta.project_name), self.location.as_posix()
         )
 
-    def _build(self, wheel: zipfile.ZipFile) -> None:
+    def _build(self) -> None:
         if self.meta.config.setup_script:
             if self.meta.config.run_setuptools:
                 setup_py = self.ensure_setup_py()
@@ -95,7 +94,7 @@
                 build_dir = self.location / self.meta.config.package_dir
                 with tokenize.open(self.meta.config.setup_script) as f:
                     code = compile(f.read(), self.meta.config.setup_script, 
"exec")
-                global_dict: Dict[str, Any] = {}
+                global_dict: dict[str, Any] = {}
                 exec(code, global_dict)
                 if "build" not in global_dict:
                     show_warning(
@@ -107,7 +106,8 @@
 
         self._prepare_editable()
         for name, content in self.editables.files():
-            self._add_file_content(wheel, name, content)
+            with self._open_for_write(name) as fp:
+                fp.write(content)
 
     def _prepare_editable(self) -> None:
         package_paths = self.meta.convert_package_paths()
@@ -122,7 +122,7 @@
                 if "." in module:
                     continue
 
-                patterns: Tuple[str, ...] = (f"{module}.py",)
+                patterns: tuple[str, ...] = (f"{module}.py",)
                 if os.name == "nt":
                     patterns += (f"{module}.*.pyd",)
                 else:
@@ -144,7 +144,7 @@
                 )
             self.editables.add_to_path(package_dir)
 
-    def find_files_to_add(self, for_sdist: bool = False) -> List[Path]:
+    def find_files_to_add(self, for_sdist: bool = False) -> list[Path]:
         package_paths = self.meta.convert_package_paths()
         package_dir = self.meta.config.package_dir
         redirections = [
@@ -157,22 +157,6 @@
             and not any(is_relative_path(p, package) for package in 
redirections)
         ]
 
-    def _add_file_content(
-        self, wheel: zipfile.ZipFile, rel_path: str, content: str
-    ) -> None:
-        print(f" - Adding {rel_path}")
-        zinfo = zipfile.ZipInfo(rel_path)
-
-        hashsum = hashlib.sha256()
-        buf = content.encode("utf-8")
-        hashsum.update(buf)
-
-        wheel.writestr(zinfo, buf, compress_type=zipfile.ZIP_DEFLATED)
-        size = len(buf)
-        hash_digest = 
urlsafe_b64encode(hashsum.digest()).decode("ascii").rstrip("=")
-
-        self._records.append((rel_path, hash_digest, str(size)))
-
     def _write_metadata_file(self, fp: TextIO) -> None:
         self.meta.data.setdefault("dependencies", []).extend(
             self.editables.dependencies()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-pep517-1.0.3/pdm/pep517/exceptions.py 
new/pdm-pep517-1.0.4/pdm/pep517/exceptions.py
--- old/pdm-pep517-1.0.3/pdm/pep517/exceptions.py       2022-07-27 
11:27:54.656356800 +0200
+++ new/pdm-pep517-1.0.4/pdm/pep517/exceptions.py       2022-08-06 
17:29:41.927385300 +0200
@@ -1,4 +1,6 @@
-from typing import Any, List
+from __future__ import annotations
+
+from typing import Any
 
 
 class BuildError(RuntimeError):
@@ -20,6 +22,6 @@
 
 
 class PEP621ValidationError(ProjectError):
-    def __init__(self, errors: List[str]) -> None:
+    def __init__(self, errors: list[str]) -> None:
         super().__init__(errors)
         self.errors = errors
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-pep517-1.0.3/pdm/pep517/macosx_platform.py 
new/pdm-pep517-1.0.4/pdm/pep517/macosx_platform.py
--- old/pdm-pep517-1.0.3/pdm/pep517/macosx_platform.py  2022-07-27 
11:27:54.656356800 +0200
+++ new/pdm-pep517-1.0.4/pdm/pep517/macosx_platform.py  2022-08-06 
17:29:41.927385300 +0200
@@ -7,12 +7,13 @@
 Adapted from
 https://github.com/pypa/wheel/blob/6e86e6b886/src/wheel/macosx_libfile.py
 """
+from __future__ import annotations
 
 import ctypes
 import os
 import sys
 from pathlib import Path
-from typing import BinaryIO, List, Optional, Tuple, Type, TypeVar, Union, 
no_type_check
+from typing import TYPE_CHECKING, BinaryIO, TypeVar, no_type_check
 
 """here the needed const and struct from mach-o header files"""
 
@@ -206,8 +207,8 @@
     uint32_t   ntools;         /* number of tool entries following this */
 };
 """
-
-Version = Tuple[int, int, int]
+if TYPE_CHECKING:
+    Version = tuple[int, int, int]
 
 
 def swap32(x: int) -> int:
@@ -220,8 +221,8 @@
 
 
 def get_base_class_and_magic_number(
-    lib_file: BinaryIO, seek: Optional[int] = None
-) -> Tuple[Type[ctypes.Structure], int]:
+    lib_file: BinaryIO, seek: int | None = None
+) -> tuple[type[ctypes.Structure], int]:
     if seek is None:
         seek = lib_file.tell()
     else:
@@ -233,7 +234,7 @@
     # Handle wrong byte order
     if magic_number in [FAT_CIGAM, FAT_CIGAM_64, MH_CIGAM, MH_CIGAM_64]:
         if sys.byteorder == "little":
-            BaseClass: Type[ctypes.Structure] = ctypes.BigEndianStructure
+            BaseClass: type[ctypes.Structure] = ctypes.BigEndianStructure
         else:
             BaseClass = ctypes.LittleEndianStructure
 
@@ -248,12 +249,12 @@
 S = TypeVar("S", bound=ctypes.Structure)
 
 
-def read_data(struct_class: Type[S], lib_file: BinaryIO) -> S:
+def read_data(struct_class: type[S], lib_file: BinaryIO) -> S:
     return 
struct_class.from_buffer_copy(lib_file.read(ctypes.sizeof(struct_class)))
 
 
 @no_type_check
-def extract_macosx_min_system_version(path_to_lib: str) -> Optional[Version]:
+def extract_macosx_min_system_version(path_to_lib: str) -> Version | None:
     with open(path_to_lib, "rb") as lib_file:
         BaseClass, magic_number = get_base_class_and_magic_number(lib_file, 0)
         if magic_number not in [FAT_MAGIC, FAT_MAGIC_64, MH_MAGIC, 
MH_MAGIC_64]:
@@ -279,7 +280,7 @@
                 read_data(FatArch, lib_file) for _ in 
range(fat_header.nfat_arch)
             ]
 
-            versions_list: List[Version] = []
+            versions_list: list[Version] = []
             for el in fat_arch_list:
                 try:
                     version = read_mach_header(lib_file, el.offset)
@@ -312,9 +313,7 @@
 
 
 @no_type_check
-def read_mach_header(
-    lib_file: BinaryIO, seek: Optional[int] = None
-) -> Optional[Version]:
+def read_mach_header(lib_file: BinaryIO, seek: int | None = None) -> Version | 
None:
     """
     This funcition parse mach-O header and extract
     information about minimal system version
@@ -370,9 +369,7 @@
     return x, y, z
 
 
-def calculate_macosx_platform_tag(
-    archive_root: Union[str, Path], platform_tag: str
-) -> str:
+def calculate_macosx_platform_tag(archive_root: str | Path, platform_tag: str) 
-> str:
     """
     Calculate proper macosx platform tag basing on files which are included to 
wheel
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-pep517-1.0.3/pdm/pep517/metadata.py 
new/pdm-pep517-1.0.4/pdm/pep517/metadata.py
--- old/pdm-pep517-1.0.3/pdm/pep517/metadata.py 2022-07-27 11:27:54.656356800 
+0200
+++ new/pdm-pep517-1.0.4/pdm/pep517/metadata.py 2022-08-06 17:29:41.927385300 
+0200
@@ -1,20 +1,9 @@
+from __future__ import annotations
+
 import glob
 import os
 from pathlib import Path
-from typing import (
-    Any,
-    Callable,
-    Dict,
-    Generic,
-    Iterable,
-    List,
-    Mapping,
-    Optional,
-    Type,
-    TypeVar,
-    Union,
-    cast,
-)
+from typing import Any, Callable, Generic, Iterable, Mapping, TypeVar, cast
 
 from pdm.pep517._vendor.packaging.requirements import Requirement
 from pdm.pep517.exceptions import MetadataError, PDMWarning, ProjectError
@@ -36,12 +25,12 @@
 
 class MetaField(Generic[T]):
     def __init__(
-        self, name: str, fget: Optional[Callable[["Metadata", Any], T]] = None
+        self, name: str, fget: Callable[[Metadata, Any], T] | None = None
     ) -> None:
         self.name = name
         self.fget = fget
 
-    def __get__(self, instance: "Metadata", owner: Type["Metadata"]) -> 
Optional[T]:
+    def __get__(self, instance: Metadata, owner: type[Metadata]) -> T | None:
         if instance is None:
             return self
         try:
@@ -59,7 +48,7 @@
     DEFAULT_ENCODING = "utf-8"
     SUPPORTED_CONTENT_TYPES = ("text/markdown", "text/x-rst", "text/plain")
 
-    def __init__(self, root: Union[str, Path], pyproject: Dict[str, Any]) -> 
None:
+    def __init__(self, root: str | Path, pyproject: dict[str, Any]) -> None:
         self.root = Path(root).absolute()
         if "project" not in pyproject:
             raise ProjectError("No [project] config in pyproject.toml")
@@ -76,7 +65,7 @@
         return self.data["name"]
 
     @property
-    def version(self) -> Optional[str]:
+    def version(self) -> str | None:
         static_version = self.data.get("version")
         if isinstance(static_version, str):
             return static_version
@@ -91,12 +80,12 @@
 
     description: MetaField[str] = MetaField("description")
 
-    def _get_readme_file(self, value: Union[Mapping[str, str], str]) -> str:
+    def _get_readme_file(self, value: Mapping[str, str] | str) -> str:
         if isinstance(value, str):
             return value
         return value.get("file", "")
 
-    def _get_readme_content(self, value: Union[Mapping[str, str], str]) -> str:
+    def _get_readme_content(self, value: Mapping[str, str] | str) -> str:
         if isinstance(value, str):
             return Path(value).read_text(encoding=self.DEFAULT_ENCODING)
         if "file" in value and "text" in value:
@@ -111,7 +100,7 @@
         encoding = value.get("charset", self.DEFAULT_ENCODING)
         return Path(file_path).read_text(encoding=encoding)
 
-    def _get_content_type(self, value: Union[Mapping[str, str], str]) -> str:
+    def _get_content_type(self, value: Mapping[str, str] | str) -> str:
         if isinstance(value, str):
             if value.lower().endswith(".md"):
                 return "text/markdown"
@@ -161,7 +150,7 @@
     maintainer_email: MetaField[str] = MetaField("maintainers", _get_email)
 
     @property
-    def classifiers(self) -> List[str]:
+    def classifiers(self) -> list[str]:
         classifers = set(self.data.get("classifiers", []))
 
         if self.dynamic and "classifiers" in self.dynamic:
@@ -183,10 +172,10 @@
         return sorted(classifers)
 
     keywords: MetaField[str] = MetaField("keywords")
-    project_urls: MetaField[Dict[str, str]] = MetaField("urls")
+    project_urls: MetaField[dict[str, str]] = MetaField("urls")
 
     @property
-    def license_expression(self) -> Optional[str]:
+    def license_expression(self) -> str | None:
         if "license-expression" in self.data:
             if "license" in self.data:
                 raise MetadataError(
@@ -208,7 +197,7 @@
         return None
 
     @property
-    def license_files(self) -> Dict[str, List[str]]:
+    def license_files(self) -> dict[str, list[str]]:
         if "license-files" not in self.data:
             if self.data.get("license", {}).get("file"):
                 # show_warning(
@@ -242,28 +231,28 @@
         return rv
 
     def _convert_dependencies(
-        self, deps: List[str], field: str = "dependencies"
-    ) -> List[str]:
+        self, deps: list[str], field: str = "dependencies"
+    ) -> list[str]:
         return list(filter(None, (ensure_pep440_req(dep, field) for dep in 
deps)))
 
     def _convert_optional_dependencies(
-        self, deps: Mapping[str, List[str]]
-    ) -> Dict[str, List[str]]:
+        self, deps: Mapping[str, list[str]]
+    ) -> dict[str, list[str]]:
         return {
             k: self._convert_dependencies(deps[k], "optional-dependencies")
             for k in deps
         }
 
-    dependencies: MetaField[List[str]] = MetaField(
+    dependencies: MetaField[list[str]] = MetaField(
         "dependencies", _convert_dependencies
     )
-    optional_dependencies: MetaField[Dict[str, List[str]]] = MetaField(
+    optional_dependencies: MetaField[dict[str, list[str]]] = MetaField(
         "optional-dependencies", _convert_optional_dependencies
     )
-    dynamic: MetaField[List[str]] = MetaField("dynamic")
+    dynamic: MetaField[list[str]] = MetaField("dynamic")
 
     @property
-    def project_name(self) -> Optional[str]:
+    def project_name(self) -> str | None:
         if self.name is None:
             return None
         return safe_name(self.name)
@@ -275,11 +264,11 @@
         return to_filename(self.project_name)
 
     @property
-    def requires_extra(self) -> Dict[str, List[str]]:
+    def requires_extra(self) -> dict[str, list[str]]:
         """For PKG-INFO metadata"""
         if not self.optional_dependencies:
             return {}
-        result: Dict[str, List[str]] = {}
+        result: dict[str, list[str]] = {}
         for name, reqs in self.optional_dependencies.items():
             current = result[name] = []
             for r in reqs:
@@ -294,7 +283,7 @@
         return "" if result == "*" else result
 
     @property
-    def entry_points(self) -> Dict[str, List[str]]:
+    def entry_points(self) -> dict[str, list[str]]:
         result = {}
         settings = self.data
         if "scripts" in settings:
@@ -319,12 +308,12 @@
                 result[plugin] = [f"{k} = {v}" for k, v in value.items()]
         return result
 
-    def convert_package_paths(self) -> Dict[str, Union[List, Dict]]:
+    def convert_package_paths(self) -> dict[str, list | dict]:
         """Return a {package_dir, packages, package_data, 
exclude_package_data} dict."""
         packages = []
         py_modules = []
         package_data = {"": ["*"]}
-        exclude_package_data: Dict[str, List[str]] = {}
+        exclude_package_data: dict[str, list[str]] = {}
         package_dir = self.config.package_dir
         includes = self.config.includes
         excludes = self.config.excludes
@@ -389,12 +378,12 @@
 class Config:
     """The [tool.pdm] table"""
 
-    def __init__(self, root: Path, data: Dict[str, Any]) -> None:
+    def __init__(self, root: Path, data: dict[str, Any]) -> None:
         self.root = root
         self.data = data
 
     def _compatible_get(
-        self, name: str, default: Any = None, old_name: Optional[str] = None
+        self, name: str, default: Any = None, old_name: str | None = None
     ) -> Any:
         if name in self.data.get("build", {}):
             return self.data["build"][name]
@@ -410,19 +399,19 @@
         return default
 
     @property
-    def includes(self) -> List[str]:
+    def includes(self) -> list[str]:
         return self._compatible_get("includes", [])
 
     @property
-    def source_includes(self) -> List[str]:
+    def source_includes(self) -> list[str]:
         return self._compatible_get("source-includes", [])
 
     @property
-    def excludes(self) -> List[str]:
+    def excludes(self) -> list[str]:
         return self._compatible_get("excludes", [])
 
     @property
-    def setup_script(self) -> Optional[str]:
+    def setup_script(self) -> str | None:
         build_table = self.data.get("build", {})
         if "setup-script" in build_table:
             return build_table["setup-script"]
@@ -463,7 +452,7 @@
         return self._compatible_get("editable-backend", "path")
 
     @property
-    def dynamic_version(self) -> Optional[DynamicVersion]:
+    def dynamic_version(self) -> DynamicVersion | None:
         dynamic_version = self.data.get("version")
         if not dynamic_version:
             return None
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-pep517-1.0.3/pdm/pep517/scm.py 
new/pdm-pep517-1.0.4/pdm/pep517/scm.py
--- old/pdm-pep517-1.0.3/pdm/pep517/scm.py      2022-07-27 11:27:54.656356800 
+0200
+++ new/pdm-pep517-1.0.4/pdm/pep517/scm.py      2022-08-06 17:29:41.931385300 
+0200
@@ -2,6 +2,8 @@
 module to get version from tag of SCM repository.
 Adapted from setuptools-scm. Currently only support git and hg.
 """
+from __future__ import annotations
+
 import os
 import re
 import shlex
@@ -10,7 +12,7 @@
 import warnings
 from datetime import datetime
 from pathlib import Path
-from typing import Any, Dict, Iterable, List, NamedTuple, Optional, Tuple, 
Union
+from typing import Any, Iterable, NamedTuple
 
 from pdm.pep517._vendor.packaging.version import LegacyVersion, Version
 from pdm.pep517._vendor.packaging.version import parse as parse_version
@@ -21,10 +23,10 @@
 
 
 def _subprocess_call(
-    cmd: Union[str, List[str]],
-    cwd: Optional[os.PathLike] = None,
-    extra_env: Optional[Dict[str, str]] = None,
-) -> Tuple[int, str, str]:
+    cmd: str | list[str],
+    cwd: os.PathLike | None = None,
+    extra_env: dict[str, str] | None = None,
+) -> tuple[int, str, str]:
     # adapted from pre-commit
     # Too many bugs dealing with environment variables and GIT:
     # https://github.com/pre-commit/pre-commit/issues/300
@@ -51,50 +53,50 @@
 
 
 class VersionInfo(NamedTuple):
-    version: Union[Version, LegacyVersion]
-    distance: Optional[int]
+    version: Version | LegacyVersion
+    distance: int | None
     dirty: bool
-    node: Optional[str]
-    branch: Optional[str]
+    node: str | None
+    branch: str | None
 
 
 def meta(
-    tag: Union[str, Version, LegacyVersion],
-    distance: Optional[int] = None,
+    tag: str | Version | LegacyVersion,
+    distance: int | None = None,
     dirty: bool = False,
-    node: Optional[str] = None,
-    branch: Optional[str] = None,
+    node: str | None = None,
+    branch: str | None = None,
 ) -> VersionInfo:
     if isinstance(tag, str):
         tag = tag_to_version(tag)
     return VersionInfo(tag, distance, dirty, node, branch)
 
 
-def _git_get_branch(root: "os.PathLike[Any]") -> Optional[str]:
+def _git_get_branch(root: os.PathLike[Any]) -> str | None:
     ret, out, _ = _subprocess_call("git rev-parse --abbrev-ref HEAD", root)
     if not ret:
         return out
     return None
 
 
-def _git_is_dirty(root: "os.PathLike[Any]") -> bool:
+def _git_is_dirty(root: os.PathLike[Any]) -> bool:
     _, out, _ = _subprocess_call("git status --porcelain 
--untracked-files=no", root)
     return bool(out)
 
 
-def _git_get_node(root: "os.PathLike[Any]") -> Optional[str]:
+def _git_get_node(root: os.PathLike[Any]) -> str | None:
     ret, out, _ = _subprocess_call("git rev-parse --verify --quiet HEAD", root)
     if not ret:
         return out[:7]
     return None
 
 
-def _git_count_all_nodes(root: "os.PathLike[Any]") -> int:
+def _git_count_all_nodes(root: os.PathLike[Any]) -> int:
     _, out, _ = _subprocess_call("git rev-list HEAD", root)
     return out.count("\n") + 1
 
 
-def _git_parse_describe(describe_output: str) -> Tuple[str, int, str, bool]:
+def _git_parse_describe(describe_output: str) -> tuple[str, int, str, bool]:
     # 'describe_output' looks e.g. like 'v1.5.0-0-g4060507' or
     # 'v1.15.1rc1-37-g9bd1298-dirty'.
 
@@ -114,14 +116,14 @@
     suffix: str
 
 
-def _parse_version_tag(tag: str) -> Optional[_ParseResult]:
+def _parse_version_tag(tag: str) -> _ParseResult | None:
     tagstring = tag if not isinstance(tag, str) else str(tag)
     match = DEFAULT_TAG_REGEX.match(tagstring)
 
     result = None
     if match:
         if len(match.groups()) == 1:
-            key: Union[int, str] = 1
+            key: int | str = 1
         else:
             key = "version"
 
@@ -134,7 +136,7 @@
     return result
 
 
-def tag_to_version(tag: str) -> Union[Version, LegacyVersion]:
+def tag_to_version(tag: str) -> Version | LegacyVersion:
     """
     take a tag that might be prefixed with a keyword and return only the 
version part
     :param config: optional configuration object
@@ -152,7 +154,7 @@
     return parse_version(version)
 
 
-def tags_to_versions(tags: Iterable[str]) -> List[Union[Version, 
LegacyVersion]]:
+def tags_to_versions(tags: Iterable[str]) -> list[Version | LegacyVersion]:
     """
     take tags that might be prefixed with a keyword and return only the 
version part
     :param tags: an iterable of tags
@@ -161,7 +163,7 @@
     return [tag_to_version(tag) for tag in tags if tag]
 
 
-def git_parse_version(root: "os.PathLike[Any]") -> Optional[VersionInfo]:
+def git_parse_version(root: os.PathLike[Any]) -> VersionInfo | None:
     GIT = shutil.which("git")
     if not GIT:
         return None
@@ -187,7 +189,7 @@
         return meta(tag, number or None, dirty, node, branch)
 
 
-def get_latest_normalizable_tag(root: "os.PathLike[Any]") -> str:
+def get_latest_normalizable_tag(root: os.PathLike[Any]) -> str:
     # Gets all tags containing a '.' from oldest to newest
     cmd = [
         "hg",
@@ -205,14 +207,14 @@
     return tag
 
 
-def hg_get_graph_distance(root: "os.PathLike[Any]", rev1: str, rev2: str = 
".") -> int:
+def hg_get_graph_distance(root: os.PathLike[Any], rev1: str, rev2: str = ".") 
-> int:
     cmd = ["hg", "log", "-q", "-r", f"{rev1}::{rev2}"]
     _, out, _ = _subprocess_call(cmd, root)
     return len(out.strip().splitlines()) - 1
 
 
 def _hg_tagdist_normalize_tagcommit(
-    root: "os.PathLike[Any]", tag: str, dist: int, node: str, branch: str
+    root: os.PathLike[Any], tag: str, dist: int, node: str, branch: str
 ) -> VersionInfo:
     dirty = node.endswith("+")
     node = "h" + node.strip("+")
@@ -239,7 +241,7 @@
         return meta(tag)
 
 
-def guess_next_version(tag_version: Union[Version, LegacyVersion]) -> str:
+def guess_next_version(tag_version: Version | LegacyVersion) -> str:
     version = _strip_local(str(tag_version))
     return _bump_dev(version) or _bump_regex(version)
 
@@ -265,7 +267,7 @@
     return "%s%d" % (prefix, int(tail) + 1)
 
 
-def hg_parse_version(root: "os.PathLike[Any]") -> Optional[VersionInfo]:
+def hg_parse_version(root: os.PathLike[Any]) -> VersionInfo | None:
     if not shutil.which("hg"):
         return None
     _, output, _ = _subprocess_call("hg id -i -b -t", root)
@@ -314,7 +316,7 @@
     return main_version + local_version
 
 
-def get_version_from_scm(root: Union[str, Path]) -> str:
+def get_version_from_scm(root: str | Path) -> str:
     if "PDM_PEP517_SCM_VERSION" in os.environ:
         version = meta(os.getenv("PDM_PEP517_SCM_VERSION", ""))
     else:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-pep517-1.0.3/pdm/pep517/utils.py 
new/pdm-pep517-1.0.4/pdm/pep517/utils.py
--- old/pdm-pep517-1.0.3/pdm/pep517/utils.py    2022-07-27 11:27:54.656356800 
+0200
+++ new/pdm-pep517-1.0.4/pdm/pep517/utils.py    2022-08-06 17:29:41.931385300 
+0200
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import functools
 import os
 import re
@@ -7,7 +9,7 @@
 from contextlib import contextmanager
 from fnmatch import fnmatchcase
 from pathlib import Path
-from typing import Callable, Generator, Iterable, Optional, Type, Union
+from typing import Callable, Generator, Iterable
 
 from pdm.pep517._vendor.packaging import tags
 from pdm.pep517._vendor.packaging.markers import Marker
@@ -121,13 +123,13 @@
         os.chdir(_old_cwd)
 
 
-def normalize_path(filename: Union[str, Path]) -> str:
+def normalize_path(filename: str | Path) -> str:
     """Normalize a file/dir name for comparison purposes"""
     filename = os.path.abspath(filename) if sys.platform == "cygwin" else 
filename
     return os.path.normcase(os.path.realpath(os.path.normpath(filename)))
 
 
-def get_platform(build_dir: Union[str, Path]) -> str:
+def get_platform(build_dir: str | Path) -> str:
     """Return our platform name 'win32', 'linux_x86_64'"""
     result = sysconfig.get_platform()
     if result.startswith("macosx") and os.path.exists(build_dir):
@@ -156,7 +158,7 @@
     return val == expected
 
 
-def get_abi_tag() -> Optional[str]:
+def get_abi_tag() -> str | None:
     """Return the ABI tag based on SOABI (if available) or emulate SOABI
     (CPython 2, PyPy)."""
     soabi = sysconfig.get_config_var("SOABI")
@@ -185,7 +187,7 @@
         return None
 
 
-def ensure_pep440_req(req: str, field: str) -> Optional[str]:
+def ensure_pep440_req(req: str, field: str) -> str | None:
     """Discard all non-PEP 440 requirements, e.g. editable VCS requirements."""
 
     if req.strip().startswith("-e"):
@@ -207,6 +209,6 @@
 
 
 @functools.lru_cache(maxsize=None)
-def show_warning(message: str, category: Type[Warning], stacklevel: int = 1) 
-> None:
+def show_warning(message: str, category: type[Warning], stacklevel: int = 1) 
-> None:
     """A cached version of warnings.warn to avoid repeated warnings."""
     warnings.warn(message, category, stacklevel + 1)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-pep517-1.0.3/pdm/pep517/version.py 
new/pdm-pep517-1.0.4/pdm/pep517/version.py
--- old/pdm-pep517-1.0.3/pdm/pep517/version.py  2022-07-27 11:27:54.656356800 
+0200
+++ new/pdm-pep517-1.0.4/pdm/pep517/version.py  2022-08-06 17:29:41.931385300 
+0200
@@ -1,8 +1,10 @@
+from __future__ import annotations
+
 import os
 import re
 import warnings
 from pathlib import Path
-from typing import Any, Dict, Union
+from typing import Any
 
 from pdm.pep517.exceptions import MetadataError
 from pdm.pep517.scm import get_version_from_scm
@@ -21,7 +23,7 @@
         self.options = options
 
     @classmethod
-    def from_toml(cls, toml: Dict[str, Any]) -> "DynamicVersion":
+    def from_toml(cls, toml: dict[str, Any]) -> DynamicVersion:
         """Create a DynamicVersion from a TOML dictionary."""
         options = toml.copy()
         if "from" in options:
@@ -63,7 +65,7 @@
             )
         return cls(source, **options)
 
-    def evaluate_in_project(self, root: Union[str, Path]) -> str:
+    def evaluate_in_project(self, root: str | Path) -> str:
         """Evaluate the dynamic version."""
         if self.source == "file":
             version_source = os.path.join(root, self.options["path"])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-pep517-1.0.3/pdm/pep517/wheel.py 
new/pdm-pep517-1.0.4/pdm/pep517/wheel.py
--- old/pdm-pep517-1.0.3/pdm/pep517/wheel.py    2022-07-27 11:27:54.656356800 
+0200
+++ new/pdm-pep517-1.0.4/pdm/pep517/wheel.py    2022-08-06 17:29:41.931385300 
+0200
@@ -1,6 +1,10 @@
+from __future__ import annotations
+
+import abc
 import contextlib
 import csv
 import hashlib
+import io
 import os
 import re
 import shutil
@@ -11,9 +15,8 @@
 import tokenize
 import zipfile
 from base64 import urlsafe_b64encode
-from io import StringIO
 from pathlib import Path
-from typing import Any, Dict, Generator, List, Mapping, Optional, TextIO, 
Tuple, Union
+from typing import Any, BinaryIO, Generator, Mapping, NamedTuple, TextIO
 
 from pdm.pep517 import __version__
 from pdm.pep517._vendor.packaging import tags
@@ -35,14 +38,87 @@
 PY_LIMITED_API_PATTERN = r"cp3\d"
 
 
+class RecordEntry(NamedTuple):
+    relpath: str
+    hash_digest: str
+    size: str
+
+
+class WheelEntry(metaclass=abc.ABCMeta):
+    # Fix the date time for reproducible builds
+    date_time = (2016, 1, 1, 0, 0, 0)
+
+    def __init__(self, rel_path: str) -> None:
+        self.rel_path = rel_path
+
+    @abc.abstractmethod
+    def open(self) -> BinaryIO:
+        pass
+
+    def build_zipinfo(self) -> zipfile.ZipInfo:
+        return zipfile.ZipInfo(self.rel_path, self.date_time)
+
+    def write_to_zip(self, zf: zipfile.ZipFile) -> RecordEntry:
+        zi = self.build_zipinfo()
+
+        hashsum = hashlib.sha256()
+        with self.open() as src:
+            while True:
+                buf = src.read(1024 * 8)
+                if not buf:
+                    break
+                hashsum.update(buf)
+
+            src.seek(0)
+            zf.writestr(zi, src.read(), compress_type=zipfile.ZIP_DEFLATED)
+
+        size = zi.file_size
+        hash_digest = 
urlsafe_b64encode(hashsum.digest()).decode("ascii").rstrip("=")
+        return RecordEntry(self.rel_path, f"sha256={hash_digest}", str(size))
+
+
+class WheelFileEntry(WheelEntry):
+    def __init__(self, rel_path: str, full_path: Path) -> None:
+        super().__init__(rel_path)
+        self.full_path = full_path
+
+    def open(self) -> BinaryIO:
+        return self.full_path.open("rb")
+
+    def build_zipinfo(self) -> zipfile.ZipInfo:
+        zi = super().build_zipinfo()
+        st_mode = os.stat(self.full_path).st_mode
+        zi.external_attr = (st_mode & 0xFFFF) << 16  # Unix attributes
+
+        if stat.S_ISDIR(st_mode):
+            zi.external_attr |= 0x10  # MS-DOS directory flag
+        return zi
+
+
+class WheelStringEntry(WheelEntry):
+    def __init__(self, rel_path: str) -> None:
+        super().__init__(rel_path)
+        self.buffer = io.BytesIO()
+
+    def open(self) -> BinaryIO:
+        self.buffer.seek(0)
+        return self.buffer
+
+    @contextlib.contextmanager
+    def text_open(self) -> Generator[TextIO, None, None]:
+        text_buffer = io.TextIOWrapper(self.open(), encoding="utf-8", 
newline="")
+        yield text_buffer
+        text_buffer.detach()
+
+
 class WheelBuilder(Builder):
     def __init__(
         self,
-        location: Union[str, Path],
-        config_settings: Optional[Mapping[str, Any]] = None,
+        location: str | Path,
+        config_settings: Mapping[str, Any] | None = None,
     ) -> None:
         super().__init__(location, config_settings)
-        self._records = []  # type: List[Tuple[str, str, str]]
+        self._entries: dict[str, WheelEntry] = {}
         self._parse_config_settings()
 
     def _parse_config_settings(self) -> None:
@@ -66,16 +142,17 @@
         if not os.path.exists(build_dir):
             os.makedirs(build_dir, exist_ok=True)
 
-        self._records.clear()
+        self._entries.clear()
         fd, temp_path = tempfile.mkstemp(suffix=".whl")
         os.close(fd)
 
+        self._copy_module()
+        self._build()
+        self._write_metadata()
         with zipfile.ZipFile(
             temp_path, mode="w", compression=zipfile.ZIP_DEFLATED
         ) as zip_file:
-            self._copy_module(zip_file)
-            self._build(zip_file)
-            self._write_metadata(zip_file)
+            self._write_to_zip(zip_file)
 
         target = os.path.join(build_dir, self.wheel_filename)
         if os.path.exists(target):
@@ -131,60 +208,45 @@
         version = self.meta_version
         return f"{name}-{version}.dist-info"
 
-    def _write_record(self, fp: TextIO) -> None:
-        writer = csv.writer(fp, lineterminator="\n")
-        writer.writerows(
-            [(path, f"sha256={hash}", size) for path, hash, size in 
self._records]
-        )
-        writer.writerow([self.dist_info_name + "/RECORD", "", ""])
+    def _write_record(self, records: list[RecordEntry]) -> WheelEntry:
+        entry = WheelStringEntry(self.dist_info_name + "/RECORD")
+        with entry.text_open() as fp:
+            writer = csv.writer(fp, lineterminator="\n")
+            writer.writerows(records)
+            writer.writerow(RecordEntry(entry.rel_path, "", ""))
+        return entry
 
-    def _write_metadata(self, wheel: zipfile.ZipFile) -> None:
+    def _write_metadata(self) -> None:
         dist_info = self.dist_info_name
         if self.meta.entry_points:
-            with self._write_to_zip(wheel, dist_info + "/entry_points.txt") as 
f:
+            with self._open_for_write(dist_info + "/entry_points.txt") as f:
                 self._write_entry_points(f)
 
-        with self._write_to_zip(wheel, dist_info + "/WHEEL") as f:
+        with self._open_for_write(dist_info + "/WHEEL") as f:
             self._write_wheel_file(f)
 
-        with self._write_to_zip(wheel, dist_info + "/METADATA") as f:
+        with self._open_for_write(dist_info + "/METADATA") as f:
             self._write_metadata_file(f)
 
         for license_file in self.find_license_files():
             self._add_file(
-                wheel,
-                os.path.join(self.location, license_file),
                 f"{dist_info}/license_files/{license_file}",
+                self.location / license_file,
             )
 
-        with self._write_to_zip(wheel, dist_info + "/RECORD") as f:
-            self._write_record(f)
-
     @contextlib.contextmanager
-    def _write_to_zip(
-        self, wheel: zipfile.ZipFile, rel_path: str
-    ) -> Generator[StringIO, None, None]:
-        sio = StringIO()
-        yield sio
-
-        # The default is a fixed timestamp rather than the current time, so
-        # that building a wheel twice on the same computer can automatically
-        # give you the exact same result.
-        date_time = (2016, 1, 1, 0, 0, 0)
-        zi = zipfile.ZipInfo(rel_path, date_time)
-        b = sio.getvalue().encode("utf-8")
-        hashsum = hashlib.sha256(b)
-        hash_digest = 
urlsafe_b64encode(hashsum.digest()).decode("ascii").rstrip("=")
-
-        wheel.writestr(zi, b, compress_type=zipfile.ZIP_DEFLATED)
+    def _open_for_write(self, rel_path: str) -> Generator[TextIO, None, None]:
+        entry = WheelStringEntry(rel_path)
+        with entry.text_open() as fp:
+            yield fp
         print(f" - Adding {rel_path}")
-        self._records.append((rel_path, hash_digest, str(len(b))))
+        self._entries[rel_path] = entry
 
-    def _build(self, wheel: zipfile.ZipFile) -> None:
+    def _build(self) -> None:
         build_dir = self.location / "build"
         if build_dir.exists():
             shutil.rmtree(str(build_dir))
-        lib_dir: Optional[Path] = None
+        lib_dir: Path | None = None
         if self.meta.config.setup_script:
             if self.meta.config.run_setuptools:
                 setup_py = self.ensure_setup_py()
@@ -204,7 +266,7 @@
                 build_dir.mkdir(exist_ok=True)
                 with tokenize.open(self.meta.config.setup_script) as f:
                     code = compile(f.read(), self.meta.config.setup_script, 
"exec")
-                global_dict: Dict[str, Any] = {}
+                global_dict: dict[str, Any] = {}
                 exec(code, global_dict)
                 if "build" not in global_dict:
                     show_warning(
@@ -233,16 +295,13 @@
             if self._is_excluded(rel_path, excludes):
                 continue
 
-            if whl_path in wheel.namelist():
-                continue
-
-            self._add_file(wheel, pkg.as_posix(), whl_path)
+            self._add_file(whl_path, pkg)
 
     def _write_version(self, destination: Path) -> None:
         dynamic_version = self.meta.config.dynamic_version
         if (
             not dynamic_version
-            or dynamic_version.source != "scm"
+            or dynamic_version.source == "file"
             or "write_to" not in dynamic_version.options
         ):
             return
@@ -253,48 +312,21 @@
         with write_path.open("w") as f:
             f.write(write_template.format(self.meta_version))
 
-    def _copy_module(self, wheel: zipfile.ZipFile) -> None:
+    def _copy_module(self) -> None:
+        root = self.meta.config.package_dir or self.location
         for path in self.find_files_to_add():
-            rel_path = None
-            if self.meta.config.package_dir:
-                try:
-                    rel_path = 
path.relative_to(self.meta.config.package_dir).as_posix()
-                except ValueError:
-                    pass
-            self._add_file(wheel, str(path), rel_path)
+            try:
+                rel_path = path.relative_to(root).as_posix()
+            except ValueError:
+                rel_path = path.as_posix()
+            self._add_file(rel_path, path)
 
-    def _add_file(
-        self, wheel: zipfile.ZipFile, full_path: str, rel_path: Optional[str] 
= None
-    ) -> None:
-        if not rel_path:
-            rel_path = full_path
+    def _add_file(self, rel_path: str, full_path: Path) -> None:
         if os.sep != "/":
             # We always want to have /-separated paths in the zip file and in 
RECORD
             rel_path = rel_path.replace(os.sep, "/")
         print(f" - Adding {rel_path}")
-        zinfo = zipfile.ZipInfo.from_file(full_path, rel_path)
-
-        # Normalize permission bits to either 755 (executable) or 644
-        st_mode = os.stat(full_path).st_mode
-
-        if stat.S_ISDIR(st_mode):
-            zinfo.external_attr |= 0x10  # MS-DOS directory flag
-
-        hashsum = hashlib.sha256()
-        with open(full_path, "rb") as src:
-            while True:
-                buf = src.read(1024 * 8)
-                if not buf:
-                    break
-                hashsum.update(buf)
-
-            src.seek(0)
-            wheel.writestr(zinfo, src.read(), 
compress_type=zipfile.ZIP_DEFLATED)
-
-        size = os.stat(full_path).st_size
-        hash_digest = 
urlsafe_b64encode(hashsum.digest()).decode("ascii").rstrip("=")
-
-        self._records.append((rel_path, hash_digest, str(size)))
+        self._entries[rel_path] = WheelFileEntry(rel_path, full_path)
 
     def _write_metadata_file(self, fp: TextIO) -> None:
         fp.write(self.format_pkginfo())
@@ -314,3 +346,11 @@
                 fp.write(ep.replace(" ", "") + "\n")
 
             fp.write("\n")
+
+    def _write_to_zip(self, zf: zipfile.ZipFile) -> None:
+        records: list[RecordEntry] = []
+        for entry in self._entries.values():
+            records.append(entry.write_to_zip(zf))
+
+        record_entry = self._write_record(records)
+        record_entry.write_to_zip(zf)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-pep517-1.0.3/pyproject.toml 
new/pdm-pep517-1.0.4/pyproject.toml
--- old/pdm-pep517-1.0.3/pyproject.toml 2022-07-27 11:27:54.656356800 +0200
+++ new/pdm-pep517-1.0.4/pyproject.toml 2022-08-06 17:29:41.931385300 +0200
@@ -22,7 +22,7 @@
     "Programming Language :: Python :: 3.10",
 ]
 dependencies = []
-version = "1.0.3"
+version = "1.0.4"
 
 [project.license]
 text = "MIT"

Reply via email to