如何构建包含HTML Tidy的自包含PyTidyLib Python Wheel
Great question! Creating a self-contained PyTidyLib wheel means bundling the HTML Tidy library directly into the package so users don’t need to install it via apt/rpm or other system package managers. Here’s a practical, step-by-step guide to make this happen:
1. Get or Build HTML Tidy Binaries
First, you need platform-specific Tidy library files to bundle. You can either:
- Pre-build binaries: Download pre-compiled
libtidy.so(Linux),libtidy.dylib(macOS), ortidy.dll(Windows) from trusted sources like the official Tidy repository. - Build from source: Clone the Tidy repo, then compile with flags to create a position-independent shared library (critical for bundling):
- For Linux/macOS: Run
./configure --enable-shared --with-pic && maketo generate the shared library. - For Windows: Use MinGW or MSVC to compile a DLL, ensuring it’s compatible with your target Python versions.
- For Linux/macOS: Run
Store these binaries in a subfolder within the PyTidyLib package (e.g., pytidylib/lib/).
2. Modify PyTidyLib to Load the Bundled Library
The original PyTidyLib relies on ctypes.util.find_library() to locate the system-installed Tidy. We need to override this to load our bundled version first.
Open the pytidylib/tidy.py file and replace the library loading logic with something like this:
import os import sys from ctypes import cdll, util def load_tidy_library(): # Get the path to our bundled library folder package_dir = os.path.dirname(os.path.abspath(__file__)) lib_dir = os.path.join(package_dir, "lib") # Platform-specific library names if os.name == "nt": lib_path = os.path.join(lib_dir, "tidy.dll") elif sys.platform == "darwin": lib_path = os.path.join(lib_dir, "libtidy.dylib") else: lib_path = os.path.join(lib_dir, "libtidy.so") # Try loading bundled library first, fall back to system if missing try: return cdll.LoadLibrary(lib_path) except OSError: return cdll.LoadLibrary(util.find_library("tidy")) # Replace the original library reference tidy = load_tidy_library()
3. Configure Packaging to Include Binaries
Update the package’s build configuration to include the bundled library files in the wheel.
Option A: Using setup.py
If the project uses setup.py, add a package_data entry:
from setuptools import setup, find_packages setup( name="pytidylib-selfcontained", # Custom name to distinguish from upstream version="0.3.2", # Match upstream version plus your suffix if needed packages=find_packages(), package_data={ "pytidylib": ["lib/*"], # Include all files in the lib subfolder }, # Add other metadata (author, license, etc.) as needed )
Option B: Using pyproject.toml (Modern Approach)
For projects using pyproject.toml, add this section:
[project] name = "pytidylib-selfcontained" version = "0.3.2" packages = ["pytidylib"] [tool.setuptools.package-data] pytidylib = ["lib/*"]
4. Build the Wheel
Run this command in the project root directory to generate the self-contained wheel:
pip wheel .
This will create a .whl file that includes both the PyTidyLib code and your bundled Tidy binaries.
For cross-platform wheels (supporting Linux, macOS, Windows), use tools like cibuildwheel to automate building across different environments.
5. Test the Wheel
Install the wheel on a system with no system-wide Tidy installed to verify it works:
pip install pytidylib-selfcontained-0.3.2-py3-none-any.whl
Then run a quick test script:
from pytidylib import tidy_document raw_html = "<html><body><p>Hello, unformatted HTML!</p></body>" cleaned_html, errors = tidy_document(raw_html) print("Cleaned HTML:", cleaned_html)
Key Notes
- License Compliance: Ensure you adhere to the licenses of both PyTidyLib (MIT) and HTML Tidy (BSD-like) when distributing your bundled wheel.
- Binary Compatibility: For Linux, build on an older distro (e.g., CentOS 7) to maximize compatibility with newer systems (thanks to glibc backward compatibility).
- Fallback Logic: The modified code includes a fallback to the system library if the bundled version is missing, which adds robustness.
内容的提问来源于stack exchange,提问作者guettli

