Thanks @sakshi-sharma for your informative tips! Here's my fully working solution.
Eventually, I've decided that it's better to create a new file containing build timestamp (and add it to .gitignore) rather than update an existing one. Also, I prefer having it as a data file rather than .py (in case I want to ignore it being missing - like, when running my project from the IDE).
So, in the end, I am creating a .dotenv-like file src/myproject/.build_info containing key like TIMESTAMP=2025-10-11 21:37:57 each time I execute build --wheel.
Changes to pyproject.toml:
dependencies = [
...more stuff...
"python-dotenv>=1.1.0",
]
[build-system]
requires = ["setuptools"] # no "wheel" needed
build-backend = "setuptools_build_hook"
backend-path = ["."] # important!
[tool.setuptools.package-data]
"*" = [
...more stuff...,
".build_info",
]
New file setuptools_build_hook.py in project's root:
"""
Setuptools build hook wrapper that writes file `src/myproject/.build_info`
containing build timestamp when building WHL files with `build --wheel`.
"""
from datetime import datetime
from os import PathLike
from pathlib import Path
from setuptools import build_meta
def build_wheel(
wheel_directory: str | PathLike[str],
config_settings: dict[str, str | list[str] | None] | None = None,
metadata_directory: str | PathLike[str] | None = None,
) -> str:
"""Creates file `src/myproject/.build_info` with key TIMESTAMP, then proceeds normally."""
Path("src/myproject/.build_info").write_text(f"TIMESTAMP={datetime.now():%Y-%m-%d %H:%M:%S}\n", encoding="utf-8")
print("* Written .build_info.")
return build_meta.build_wheel(wheel_directory, config_settings, metadata_directory)
# Proxy (wrappers) for setuptools.build_meta
get_requires_for_build_wheel = build_meta.get_requires_for_build_wheel
get_requires_for_build_sdist = build_meta.get_requires_for_build_sdist
prepare_metadata_for_build_wheel = build_meta.prepare_metadata_for_build_wheel
build_sdist = build_meta.build_sdist
get_requires_for_build_editable = build_meta.get_requires_for_build_editable
prepare_metadata_for_build_editable = build_meta.prepare_metadata_for_build_editable
build_editable = build_meta.build_editable
And now, how to read this value in runtime:
import myproject as this_package
from io import StringIO
build_timestamp: str | None = None
# noinspection PyBroadException
try:
build_timestamp = dotenv_values(stream=StringIO(resources.files(this_package).joinpath(".build_info")
.read_text(encoding="utf-8")))["TIMESTAMP"]
except Exception:
pass