A thin Python wrapper over Ansible's executor: typed AnsibleHost / AnsibleHosts classes that let you run Ansible modules from Python without pytest-ansible or ansible-runner.
Project description
ansible-host
A thin Python wrapper over Ansible's executor. Lets you run Ansible modules from Python with structured results, batch execution, and a host-object-first API — without going through pytest-ansible or ansible-runner.
Status: alpha (0.1.0a0). Built from a working internal implementation; API is stable in shape but may shift in details before 1.0.
Why this exists
Ansible has a great executor and a huge module ecosystem, but the existing Python access paths are awkward for in-test or in-tool use:
pytest-ansible |
ansible-runner |
ansible-host |
|
|---|---|---|---|
| Use case | Pytest fixtures | AWX-style managed jobs | In-process programmatic |
| API surface | ~20% of Ansible's runtime | Subprocess + event stream | Typed Python objects |
| Forking, custom callbacks | Limited | Yes (in subprocess) | Yes (native) |
| Per-call overhead | Low | High (subprocess + JSON parsing) | Low (in-process) |
| Returns | Strings | Event stream | Structured Python dicts |
ansible-host sits where the other two don't: in-process, low-overhead, structured-result execution that you can drop into any Python codebase that wants Ansible underneath.
Install
pip install ansible-host
Requires Python 3.10+ and ansible-core>=2.16,<2.22. Like Ansible itself, the library runs on POSIX systems (Linux, macOS, WSL) — it is not supported on native Windows.
Development
The recommended dev workflow uses uv — it manages the virtualenv directly, sidestepping the python3-venv split on Ubuntu and installing dependencies an order of magnitude faster than pip.
# One-time: install uv (https://docs.astral.sh/uv/getting-started/installation/)
curl -LsSf https://astral.sh/uv/install.sh | sh
# In the repo
uv venv # creates .venv with the default Python
uv pip install -e ".[dev]" # editable install + dev extras
uv run pytest # run the test suite
uv run ruff check src tests # lint
To target a specific Python or ansible-core version (matches the CI matrix):
uv venv --python 3.12
uv pip install -e ".[dev]"
uv pip install "ansible-core==2.19.*"
uv run pytest
pip and a manually-managed venv still work — uv is just the convenience.
Quickstart
30-second try — no SSH, no inventory
AnsibleLocalhost runs modules in-process on the current machine via Ansible's local connection plugin. No inventory file, no SSH, no setup.
from ansible_host import AnsibleLocalhost
host = AnsibleLocalhost()
result = host.ping()
assert result["ping"] == "pong"
result = host.command("uname -a")
print(result["stdout"])
Running against a real host
Drop an inventory file alongside your script:
# inventory.ini
[switches]
sw-01 ansible_host=10.0.0.1 ansible_user=admin
from ansible_host import AnsibleHost
host = AnsibleHost(inventory="inventory.ini", pattern="sw-01")
result = host.shell("show version")
print(result["stdout"])
Multi-host fanout
from ansible_host import AnsibleHosts
hosts = AnsibleHosts(inventory="inventory.ini", pattern="switches")
results = hosts.ping() # parallelism via forks=
for hostname, r in results.items():
print(hostname, r["ping"])
# Container API: index, iterate, len
print(len(hosts), hosts.hostnames)
first = hosts[0] # -> AnsibleHost
by_name = hosts["sw-01"] # -> AnsibleHost
for h in hosts:
print(h.hostname)
Dynamic dispatch and task directives
Any Ansible module is callable as a method via __getattr__ (host.<module_name>(...)):
host.copy(src="/etc/hosts", dest="/tmp/hosts.bak")
host.command("rm /tmp/maybe-missing", task_directives={"ignore_errors": True})
host.shell("echo $TOKEN", task_directives={"no_log": True})
Common task_directives: ignore_errors, no_log, when, failed_when, changed_when, become.
Batch mode — queue tasks, run them in a single play
with host:
host.shell("uptime")
host.shell("df -h")
host.shell("free -m")
results = host.results
Building a queue across functions
with host: is lexically scoped. If you need to assemble a batch across multiple functions, use the explicit form — same machinery, no scope limit:
host.load_module("ansible.builtin.command", args=["uptime"])
host.load_module("ansible.builtin.command", args=["df -h"])
results = host.run_loaded_modules()
Result shapes
| single task | batch (with block) |
|
|---|---|---|
| single host | dict |
list[dict] |
| multi host | {hostname: dict} |
{hostname: list[dict]} |
Failures
A failing module raises AnsibleModuleFailed:
from ansible_host import AnsibleModuleFailed
try:
host.command("false")
except AnsibleModuleFailed as e:
print("module failed:", e)
# Or suppress and inspect the result:
result = host.command("false", task_directives={"ignore_errors": True})
assert result["failed"] is True
More examples
See tests/test_local_integration.py for 30 runnable examples covering ping, command, shell, batch mode, dynamic dispatch, no_log, forks, multi-host fanout, per-host failure aggregation, and the container protocol.
Compatibility
This library uses Ansible's internal Python API (TaskQueueManager, InventoryManager, VariableManager, Play, DataLoader). Those APIs are not officially stable across ansible-core releases — expect occasional updates when ansible-core introduces breaking internal changes. The current support range is declared in pyproject.toml and in the matrix CI.
Concurrency
ansible-host is designed for sequential use at the instance level. For parallelism within a single call, use Ansible's native forking via the forks= option. Running multiple AnsibleHost / AnsibleHosts instances concurrently in different threads will race on ansible.context's process-global state.
Reference implementation
A real-world use of this pattern lives in sonic-mgmt's tbng branch — ansible-host is the cleaned-up, packaged version of that code.
License
Apache License 2.0. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ansible_host-0.1.0a0.tar.gz.
File metadata
- Download URL: ansible_host-0.1.0a0.tar.gz
- Upload date:
- Size: 25.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e3eb46cc8cbb068dd1eab6462584aa4cef06ae4ac863d6246874dd31af731d0b
|
|
| MD5 |
ff91c86bf328f494e4fd60895eb94b42
|
|
| BLAKE2b-256 |
3611805750a007ccc09d3ec8ab08fbde8b2fcf7a35b23d903e76ccc6c3b61938
|
Provenance
The following attestation bundles were made for ansible_host-0.1.0a0.tar.gz:
Publisher:
publish.yml on wangxin/ansible-host
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ansible_host-0.1.0a0.tar.gz -
Subject digest:
e3eb46cc8cbb068dd1eab6462584aa4cef06ae4ac863d6246874dd31af731d0b - Sigstore transparency entry: 1796924010
- Sigstore integration time:
-
Permalink:
wangxin/ansible-host@f3e6a1434f1366d22e8bfc00f33cdb68e4a65125 -
Branch / Tag:
refs/tags/v0.1.0a0 - Owner: https://github.com/wangxin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f3e6a1434f1366d22e8bfc00f33cdb68e4a65125 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ansible_host-0.1.0a0-py3-none-any.whl.
File metadata
- Download URL: ansible_host-0.1.0a0-py3-none-any.whl
- Upload date:
- Size: 22.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0fb70a4871d4f730b6b6bfd7d4c659c0ef1ca2661345855392ad0a569a7bb5fe
|
|
| MD5 |
132a13588f31d7dff58c19304ac03043
|
|
| BLAKE2b-256 |
6ec0b1c9cb4bc1fdf21a649e17552f27f253270a2184a74b8940cf6f0eeb8148
|
Provenance
The following attestation bundles were made for ansible_host-0.1.0a0-py3-none-any.whl:
Publisher:
publish.yml on wangxin/ansible-host
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ansible_host-0.1.0a0-py3-none-any.whl -
Subject digest:
0fb70a4871d4f730b6b6bfd7d4c659c0ef1ca2661345855392ad0a569a7bb5fe - Sigstore transparency entry: 1796924252
- Sigstore integration time:
-
Permalink:
wangxin/ansible-host@f3e6a1434f1366d22e8bfc00f33cdb68e4a65125 -
Branch / Tag:
refs/tags/v0.1.0a0 - Owner: https://github.com/wangxin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f3e6a1434f1366d22e8bfc00f33cdb68e4a65125 -
Trigger Event:
push
-
Statement type: