diff options
author | Anderson Bravalheri <andersonbravalheri@gmail.com> | 2023-05-03 13:18:46 +0100 |
---|---|---|
committer | Anderson Bravalheri <andersonbravalheri@gmail.com> | 2023-05-03 14:09:17 +0100 |
commit | ae8ec5c3f4cfc7f14783b4dd58c436a7ba168138 (patch) | |
tree | 092c426f4747d1451d58dc94775813ad4dbf61bf /setuptools/_core_metadata.py | |
parent | 81498f146f9eb01e04eecc53dc1147c6d21470be (diff) | |
download | python-setuptools-git-dev/core_metadata.tar.gz |
Improve atomicity when writing PKG-INFOdev/core_metadata
For the time being, when `setuptools.build_meta` is called,
`egg_info.egg_base` is accidentally set to the project root between the
several calls to the different build hooks.
This means that if the hooks are called, they will try to overwrite
`setuptools.egg-info/PKG-INFO`, and for a very short interval of time it
will be an empty file.
Another process may then try to simultaneously use `importlib.metadata`
to list entry-points. However to sort entry-points, `importlib.metadata`
will try to read the `Name` field in the `PKG-INFO/METADATA` files and
will raise an error/warning if that file is empty.
This commit tries to use `os.replace` to avoid having an empty PKG-INFO
while `importlib.metadata` is used.
Diffstat (limited to 'setuptools/_core_metadata.py')
-rw-r--r-- | setuptools/_core_metadata.py | 21 |
1 files changed, 21 insertions, 0 deletions
diff --git a/setuptools/_core_metadata.py b/setuptools/_core_metadata.py index 4b25b2f4..b8290f50 100644 --- a/setuptools/_core_metadata.py +++ b/setuptools/_core_metadata.py @@ -3,9 +3,12 @@ Handling of Core Metadata for Python packages (including reading and writing). See: https://packaging.python.org/en/latest/specifications/core-metadata/ """ +import os +import stat import textwrap from email import message_from_file from email.message import Message +from tempfile import NamedTemporaryFile from typing import Optional, List from distutils.util import rfc822_escape @@ -122,6 +125,24 @@ def single_line(val): return val +def write_pkg_info(self, base_dir): + """Write the PKG-INFO file into the release tree.""" + temp = "" + final = os.path.join(base_dir, 'PKG-INFO') + try: + # Use a temporary file while writing to avoid race conditions + # (e.g. `importlib.metadata` reading `.egg-info/PKG-INFO`): + with NamedTemporaryFile("w", encoding="utf-8", dir=base_dir, delete=False) as f: + temp = f.name + self.write_pkg_file(f) + permissions = stat.S_IMODE(os.lstat(temp).st_mode) + os.chmod(temp, permissions | stat.S_IRGRP | stat.S_IROTH) + os.replace(temp, final) # atomic operation. + finally: + if temp and os.path.exists(temp): + os.remove(temp) + + # Based on Python 3.5 version def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME """Write the PKG-INFO format data to a file object.""" |