CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-versioningit

Versioning It with your Version In Git - automatic package versioning based on VCS tags

Pending
Overview
Eval results
Files

build-integration.mddocs/

Build Integration

Integration with build systems including setuptools command classes, onbuild hooks, and file providers for inserting version information into build artifacts. This system allows versioningit to modify files during the build process.

Capabilities

Setuptools Command Classes

Custom setuptools command classes that run the onbuild step during sdist and wheel building.

def get_cmdclasses(
    bases: Optional[dict[str, type[Command]]] = None
) -> dict[str, type[Command]]:
    """
    Return a dict of custom setuptools Command classes that run the onbuild
    step when building an sdist or wheel.

    Parameters:
    - bases: Optional dict of alternative base classes for customization

    Returns:
    dict[str, type[Command]]: Dict containing "sdist" and "build_py" command classes
    """

Onbuild Execution

Run the onbuild step for inserting version information into build artifacts.

def run_onbuild(
    *,
    build_dir: str | Path,
    is_source: bool,
    template_fields: dict[str, Any],
    project_dir: str | Path = os.curdir,
    config: Optional[dict] = None,
) -> None:
    """
    Run the onbuild step for the given setuptools project.

    Parameters:
    - build_dir: Directory containing the in-progress build
    - is_source: True for sdist (preserves source paths), False for wheel (uses install paths)
    - template_fields: Dict of fields for template substitution
    - project_dir: Path to project root (default: current directory)
    - config: Configuration dict or None to read from configuration file

    Raises:
    - NoConfigFileError: if config is None and no configuration file exists
    - NoConfigSectionError: if configuration file has no versioningit section
    - ConfigError: if configuration values are invalid
    - MethodError: if a method returns wrong type
    """

def get_template_fields_from_distribution(
    dist: Distribution,
) -> Optional[dict[str, Any]]:
    """
    Extract template fields stashed on setuptools Distribution by versioningit's
    setuptools hook, for passing to the onbuild step.

    Parameters:
    - dist: setuptools Distribution object

    Returns:
    Optional[dict[str, Any]]: Template fields or None if building from sdist
    """

File Provider System

Abstract interfaces for accessing and modifying files during the build process.

class OnbuildFileProvider(ABC):
    """
    Abstract base class for accessing files that are about to be included
    in an sdist or wheel currently being built.
    """
    
    @abstractmethod
    def get_file(
        self, source_path: str | PurePath, install_path: str | PurePath, is_source: bool
    ) -> OnbuildFile:
        """
        Get an object for reading & writing a file in the project being built.

        Parameters:
        - source_path: Path relative to project root
        - install_path: Path when installed (for wheels)
        - is_source: True for sdist, False for wheel

        Returns:
        OnbuildFile: File access object
        """

class OnbuildFile(ABC):
    """
    Abstract base class for opening a file in a project currently being built.
    """
    
    @abstractmethod
    def open(
        self,
        mode: str = "r",
        encoding: str | None = None,
        errors: str | None = None,
        newline: str | None = None,
    ) -> IO:
        """
        Open the associated file. Mode must be "r", "w", "a", "rb", "br", 
        "wb", "bw", "ab", or "ba".
        """

Setuptools File Provider

Implementation for setuptools builds that operates on temporary build directories.

@dataclass
class SetuptoolsFileProvider(OnbuildFileProvider):
    """
    OnbuildFileProvider implementation for setuptools builds.
    Operates directly on the temporary directory containing build files.
    """
    
    build_dir: Path
    """The setuptools-managed temporary directory containing build files."""
    
    modified: set[PurePath]
    """Set of file paths that have been opened for writing or appending."""
    
    def get_file(
        self, source_path: str | PurePath, install_path: str | PurePath, is_source: bool
    ) -> SetuptoolsOnbuildFile: ...

@dataclass
class SetuptoolsOnbuildFile(OnbuildFile):
    """File access implementation for setuptools builds."""
    
    provider: SetuptoolsFileProvider
    source_path: PurePath
    install_path: PurePath
    is_source: bool

Hatch File Provider

Implementation for Hatch builds that uses temporary directories and forced inclusion.

@dataclass
class HatchFileProvider(OnbuildFileProvider):
    """
    OnbuildFileProvider implementation for Hatch builds.
    Creates modified files in temporary directory for forced inclusion.
    """
    
    src_dir: Path
    """The root of the project directory."""
    
    tmp_dir: Path
    """Temporary directory for creating modified files."""
    
    modified: set[PurePath]
    """Set of file paths created under the temporary directory."""
    
    def get_file(
        self, source_path: str | PurePath, install_path: str | PurePath, is_source: bool
    ) -> HatchOnbuildFile: ...
    
    def get_force_include(self) -> dict[str, str]:
        """
        Get mapping of temporary files to their target paths for forced inclusion.
        """

@dataclass
class HatchOnbuildFile(OnbuildFile):
    """File access implementation for Hatch builds."""
    
    provider: HatchFileProvider
    source_path: PurePath
    install_path: PurePath
    is_source: bool

Usage Examples

Setuptools Integration

# setup.py
from setuptools import setup
from versioningit import get_cmdclasses

setup(
    name="mypackage",
    cmdclass=get_cmdclasses(),
    # ... other setup parameters
)
# pyproject.toml
[build-system]
requires = ["setuptools", "versioningit"]
build-backend = "setuptools.build_meta"

[project]
dynamic = ["version"]

[tool.setuptools.dynamic]
version = {attr = "versioningit.get_version"}

[tool.versioningit]
# ... configuration

[tool.versioningit.onbuild]
method = "replace-version"
source-file = "src/mypackage/__init__.py"
build-file = "mypackage/__init__.py"

Custom Command Classes

from setuptools import setup
from setuptools.command.build_py import build_py
from versioningit import get_cmdclasses

class CustomBuildPy(build_py):
    def run(self):
        # Custom build logic
        super().run()

# Use custom base classes
cmdclasses = get_cmdclasses({"build_py": CustomBuildPy})

setup(
    name="mypackage",
    cmdclass=cmdclasses,
)

Manual Onbuild Execution

from pathlib import Path
from versioningit import run_onbuild, get_template_fields_from_distribution

def custom_build_command(self):
    # Get template fields from distribution
    template_fields = get_template_fields_from_distribution(self.distribution)
    
    if template_fields is not None:
        # Run onbuild step
        run_onbuild(
            build_dir=self.build_lib,
            is_source=False,  # Building wheel
            template_fields=template_fields,
            project_dir=Path().resolve()
        )

Custom File Provider

from versioningit.onbuild import OnbuildFileProvider, OnbuildFile
from pathlib import Path

class CustomFileProvider(OnbuildFileProvider):
    def __init__(self, build_dir: Path):
        self.build_dir = build_dir
    
    def get_file(self, source_path, install_path, is_source):
        return CustomOnbuildFile(
            self, source_path, install_path, is_source
        )

class CustomOnbuildFile(OnbuildFile):
    def __init__(self, provider, source_path, install_path, is_source):
        self.provider = provider
        self.source_path = source_path
        self.install_path = install_path
        self.is_source = is_source
    
    def open(self, mode="r", encoding=None, errors=None, newline=None):
        path = self.source_path if self.is_source else self.install_path
        full_path = self.provider.build_dir / path
        return full_path.open(mode=mode, encoding=encoding, errors=errors, newline=newline)

Onbuild Method Implementation

def custom_onbuild_method(
    *,
    file_provider: OnbuildFileProvider,
    is_source: bool,
    template_fields: dict[str, Any],
    params: dict[str, Any],
) -> None:
    """Custom onbuild method example."""
    
    source_file = params["source-file"]
    build_file = params["build-file"]
    
    # Get file handle
    file = file_provider.get_file(source_file, build_file, is_source)
    
    # Read current content
    with file.open("r", encoding="utf-8") as fp:
        content = fp.read()
    
    # Modify content with template fields
    new_content = content.format(**template_fields)
    
    # Write back
    with file.open("w", encoding="utf-8") as fp:
        fp.write(new_content)

Template Fields Usage

from versioningit import Versioningit

vgit = Versioningit.from_project_dir()
report = vgit.run()

# Template fields available for onbuild
fields = report.template_fields

# Common substitutions in onbuild
version_line = '__version__ = "{version}"'.format(**fields)
build_info = """
__version__ = "{version}"
__version_tuple__ = {version_tuple}
__build_date__ = "{build_date}"
__git_revision__ = "{rev}"
""".format(**fields)

Error Handling in Build Integration

from versioningit import get_cmdclasses, run_onbuild
from versioningit.errors import ConfigError, MethodError

try:
    cmdclasses = get_cmdclasses()
except Exception as e:
    print(f"Failed to get command classes: {e}")
    cmdclasses = {}

# In build command
try:
    run_onbuild(
        build_dir=build_dir,
        is_source=False,
        template_fields=template_fields
    )
except ConfigError as e:
    print(f"Configuration error in onbuild: {e}")
except MethodError as e:
    print(f"Method error in onbuild: {e}")

Install with Tessl CLI

npx tessl i tessl/pypi-versioningit

docs

build-integration.md

builtin-methods.md

core-operations.md

data-models.md

exceptions.md

index.md

method-system.md

versioningit-class.md

tile.json