From d173e3b2fc5ad02d1b27ba5633fa2f2735a135bc Mon Sep 17 00:00:00 2001 From: Xin Wang Date: Thu, 11 Jun 2026 15:08:01 +0800 Subject: [PATCH] Prepare metadata for PyPI publishing - __version__ is now derived from installed package metadata via importlib.metadata.version, replacing a hardcoded string that duplicated pyproject.toml. The smoke test asserts the two match to catch any future drift. - Operating System classifier widened from POSIX :: Linux to POSIX, matching the documented support range (Linux, macOS, WSL). - Added Changelog URL to [project.urls] so PyPI sidebar links it. - Added CHANGELOG.md (Keep a Changelog format, SemVer) with an initial 0.1.0a0 entry and Unreleased section. - Added CHANGELOG.md to the sdist include manifest. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Xin Wang --- CHANGELOG.md | 47 ++++++++++++++++++++++++++++++++++++ pyproject.toml | 4 ++- src/ansible_host/__init__.py | 11 +++++++-- tests/test_smoke.py | 8 ++++++ 4 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f3a85aa --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,47 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Changed +- `__version__` is now derived from installed package metadata via + `importlib.metadata` instead of a hardcoded string, so it stays in sync + with `pyproject.toml` automatically. +- `Operating System` classifier widened from `POSIX :: Linux` to `POSIX` + to match the documented support range (Linux, macOS, WSL). +- Added `Changelog` URL to `[project.urls]` (linked from PyPI sidebar). + +## [0.1.0a0] — 2026-06-11 + +Initial alpha release. + +### Added +- `AnsibleHost`, `AnsibleHosts`, `AnsibleLocalhost` classes for in-process + Ansible module execution with structured results. +- Dynamic dispatch: any Ansible module is callable as a method + (e.g. `host.ping()`, `host.command("uname -a")`). +- Batch execution via context manager (`with host:`) and via explicit + `load_module` / `run_loaded_modules`. +- Container protocol on `AnsibleHosts` (`len`, `[]` by int or hostname, + iteration). +- Per-host failure aggregation via `task_directives={"ignore_errors": True}`. +- Verbosity-controlled debug logging via the `ansible_host` logger and the + `ANSIBLE_HOST_VERBOSITY` environment variable. +- Inline `_JsonResultsCallback` for result collection without a separate + plugin file. +- Tier-1 integration test suite (30 tests) using Ansible's `local` + connection — no SSH or Docker required. +- Tier-A logging/verbosity tests guarding `display.verbosity` save/restore + and the verbosity ladder contract. + +### Supported +- Python 3.10, 3.11, 3.12, 3.13 +- ansible-core 2.16, 2.17, 2.18, 2.19, 2.20, 2.21 +- POSIX systems (Linux, macOS, WSL); not supported on native Windows. + +[Unreleased]: https://github.com/wangxin/ansible-host/compare/v0.1.0a0...HEAD +[0.1.0a0]: https://github.com/wangxin/ansible-host/releases/tag/v0.1.0a0 diff --git a/pyproject.toml b/pyproject.toml index 204cd65..5ed77c5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ classifiers = [ "Intended Audience :: Developers", "Intended Audience :: System Administrators", "License :: OSI Approved :: Apache Software License", - "Operating System :: POSIX :: Linux", + "Operating System :: POSIX", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -47,6 +47,7 @@ dev = [ Homepage = "https://github.com/wangxin/ansible-host" Source = "https://github.com/wangxin/ansible-host" Issues = "https://github.com/wangxin/ansible-host/issues" +Changelog = "https://github.com/wangxin/ansible-host/blob/main/CHANGELOG.md" [tool.hatch.build.targets.wheel] packages = ["src/ansible_host"] @@ -56,6 +57,7 @@ include = [ "src/ansible_host", "tests", "README.md", + "CHANGELOG.md", "LICENSE", "pyproject.toml", ] diff --git a/src/ansible_host/__init__.py b/src/ansible_host/__init__.py index 14066a1..954956f 100644 --- a/src/ansible_host/__init__.py +++ b/src/ansible_host/__init__.py @@ -19,6 +19,8 @@ import json import logging import os +from importlib.metadata import PackageNotFoundError +from importlib.metadata import version as _pkg_version from typing import Any import ansible @@ -39,6 +41,13 @@ display = Display() init_plugin_loader() +try: + __version__ = _pkg_version("ansible-host") +except PackageNotFoundError: + # Source checkout without installed metadata (e.g., running from a tarball + # without ``pip install -e .``). Falls back to an inert marker. + __version__ = "0.0.0+unknown" + # Logger for the ansible-host library. Users can configure verbosity by # attaching handlers to this logger (logging.getLogger("ansible_host")). logger = logging.getLogger("ansible_host") @@ -1091,8 +1100,6 @@ def __repr__(self) -> str: return f"AnsibleLocalhost(inventory={inv_str}, connection='local')" -__version__ = "0.1.0a0" - __all__ = [ "AnsibleHost", "AnsibleHosts", diff --git a/tests/test_smoke.py b/tests/test_smoke.py index f707218..41ed383 100644 --- a/tests/test_smoke.py +++ b/tests/test_smoke.py @@ -21,6 +21,14 @@ def test_version_present(): assert isinstance(ansible_host.__version__, str) assert ansible_host.__version__ + # Must match installed package metadata (i.e., pyproject.toml [project].version), + # not be the inert fallback. Catches a stale hardcoded version regression. + from importlib.metadata import version as _pkg_version + + assert ansible_host.__version__ == _pkg_version("ansible-host"), ( + f"__version__ ({ansible_host.__version__}) does not match installed " + f"metadata ({_pkg_version('ansible-host')})" + ) def test_dunder_lookup_does_not_dispatch_as_module():