Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Changelog
-----
released ...

- tag wheels for their build platform so pip does not install an incompatible shared library on ARM64
- breaking: drop support for python 3.9 and 3.10
- add support for python 3.14
- upgrade vendored libfaketime to 0.9.13 without modifications, fixing build failures with recent glibc versions and compilers defaulting to ISO C23
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ Installation
$ pip install libfaketime
```

Wheels contain a native library and are platform-specific. If a compatible wheel
is not available, installation builds the library from source and requires `make`
and a C compiler.

Usage
-----

Expand Down
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
[build-system]
requires = ["setuptools>=70.1.0", "wheel"]
build-backend = "setuptools.build_meta"

[tool.pytest.ini_options]
filterwarnings = [
# utcnow() is still covered on purpose: it remains supported and used
Expand Down
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ mock==5.1.0
pytest==8.2.0
python-dateutil==2.9.0post0
pytz==2024.1
setuptools>=70.1.0
tox==4.15.0
wheel
24 changes: 22 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
import subprocess
import sys

from setuptools import Distribution
from setuptools import find_packages
from setuptools import setup
from setuptools.command.bdist_wheel import bdist_wheel
from setuptools.command.install import install

# This hack is from http://stackoverflow.com/a/7071358/1231454;
Expand Down Expand Up @@ -36,6 +38,23 @@
faketime_lib = os.path.join(_vendor_path, "src", libname)


class BinaryDistribution(Distribution):
"""A distribution containing a platform-specific shared library."""

def has_ext_modules(self) -> bool:
"""Use the platform library directory for the bundled native library."""
return True


class PlatformWheel(bdist_wheel):
"""A platform-specific wheel without a Python ABI dependency."""

def get_tag(self) -> tuple[str, str, str]:
"""Keep Python compatibility while selecting the build platform."""
_, _, platform_tag = super().get_tag()
return "py3", "none", platform_tag


class CustomInstall(install):
def run(self):
self.my_outputs = []
Expand All @@ -55,7 +74,7 @@ def run(self):
elif sys.platform == "darwin":
subprocess.check_call(["make", "-C", _vendor_path])

dest = os.path.join(self.install_purelib, os.path.dirname(faketime_lib))
dest = os.path.join(self.install_lib, os.path.dirname(faketime_lib))
try:
os.makedirs(dest)
except OSError as e:
Expand Down Expand Up @@ -106,7 +125,8 @@ def get_outputs(self):
],
include_package_data=True,
zip_safe=False,
cmdclass={"install": CustomInstall},
distclass=BinaryDistribution,
cmdclass={"bdist_wheel": PlatformWheel, "install": CustomInstall},
entry_points={
"console_scripts": [
"python-libfaketime = libfaketime:main",
Expand Down
47 changes: 47 additions & 0 deletions test/test_wheel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import subprocess
import sys
from email.parser import BytesParser
from pathlib import Path
from zipfile import ZipFile


def test_wheel_is_platform_specific(tmp_path: Path) -> None:
"""Check wheel compatibility tags and the bundled library's location.

Args:
tmp_path: Directory for the built wheel.

"""
root = Path(__file__).resolve().parents[1]
subprocess.run(
[
sys.executable,
"-m",
"pip",
"wheel",
"--no-deps",
"--no-build-isolation",
"--wheel-dir",
str(tmp_path),
str(root),
],
check=True,
timeout=180,
)
[wheel] = tmp_path.glob("libfaketime-*.whl")
_, python_tag, abi_tag, platform_tag = wheel.stem.rsplit("-", 3)
assert python_tag == "py3"
assert abi_tag == "none"
assert platform_tag != "any"

library_name = (
"libfaketime.1.dylib" if sys.platform == "darwin" else "libfaketime.so.1"
)
with ZipFile(wheel) as archive:
[metadata_path] = (
name for name in archive.namelist() if name.endswith(".dist-info/WHEEL")
)
metadata = BytesParser().parsebytes(archive.read(metadata_path))
assert metadata["Root-Is-Purelib"] == "false"
library = archive.getinfo(f"libfaketime/vendor/libfaketime/src/{library_name}")
assert library.file_size > 0