Skip to main content

Async-native Python library for interacting with Docker

Project description

🐳 aiowhales

The async Docker client for Python.

Talk to Docker the way Python was meant to — with async and await.

CI PyPI version Python versions Downloads License Ruff Coverage Typed


Built on aiohttp, aiowhales talks directly to the Docker Engine API over Unix sockets or TCP. No subprocess shells. No sync wrappers. No blocking threads. Just pure async Python.

async with AsyncDockerClient() as docker:
    container = await docker.containers.run("python:3.13-slim", "echo hello!", detach=True)
    async for line in docker.containers.logs(container.id):
        print(line)  # hello!

Why aiowhales?

aiowhales docker-py subprocess
Async native
Direct API (no CLI)
Typed models
Streaming (async for)
Built-in test mocks
Cross-platform

Install

pip install aiowhales
uv add aiowhales

Quick Start

import asyncio
from aiowhales import AsyncDockerClient

async def main():
    async with AsyncDockerClient() as docker:
        # Pull an image with streaming progress
        async for progress in docker.images.pull("python:3.12-slim"):
            print(progress.status)

        # Run a container
        container = await docker.containers.run(
            "python:3.12-slim",
            "python -c 'print(\"hello from aiowhales!\")'",
            detach=True,
        )

        # Stream logs
        await docker.containers.wait(container.id)
        async for line in docker.containers.logs(container.id):
            print(line)

        # Clean up
        await docker.containers.remove(container.id)

asyncio.run(main())

Features

🔌 Connecting

from aiowhales import AsyncDockerClient, from_env

# Unix socket (default on Linux/macOS)
async with AsyncDockerClient() as docker: ...

# TCP (default on Windows, or remote hosts)
async with AsyncDockerClient("tcp://192.168.1.100:2375") as docker: ...

# From DOCKER_HOST env var
docker = from_env()

📦 Containers

# List
containers = await docker.containers.list(all=True)

# Run
container = await docker.containers.run("nginx:latest", name="web", ports={"80/tcp": 8080})

# Lifecycle
await docker.containers.stop(container.id)
await docker.containers.start(container.id)
await docker.containers.restart(container.id)

# Inspect
info = await docker.containers.get(container.id)
print(info.status, info.image)

# Exec into a running container
result = await docker.containers.exec_run(container.id, ["ls", "-la"])

# Stream logs in real time
async for line in docker.containers.logs(container.id, follow=True):
    print(line)

# Live resource stats
stats = await docker.containers.stats(container.id)
print(f"CPU: {stats.cpu_percent}%  Memory: {stats.memory_usage}")

# Remove
await docker.containers.remove(container.id, force=True)

🖼️ Images

# List
images = await docker.images.list()

# Pull with progress
async for progress in docker.images.pull("ubuntu:latest"):
    print(f"{progress.status} {progress.progress or ''}")

# Build from Dockerfile
async for output in docker.images.build(".", tags=["myapp:latest"]):
    print(output.stream)

# Tag and push
await docker.images.tag("myapp:latest", "registry.example.com/myapp:v1")
async for progress in docker.images.push("registry.example.com/myapp:v1"):
    print(progress.status)

# Remove
await docker.images.remove("myapp:latest")

💾 Volumes

volumes = await docker.volumes.list()
vol = await docker.volumes.create("my-data", labels={"env": "dev"})
await docker.volumes.remove("my-data")
pruned = await docker.volumes.prune()

🌐 Networks

networks = await docker.networks.list()
net = await docker.networks.create("my-net", driver="bridge")
await docker.networks.connect(net.id, container.id, aliases=["web"])
await docker.networks.disconnect(net.id, container.id)
await docker.networks.remove(net.id)

⚡ Exec

# Run a command inside a container
result = await docker.exec.run(container.id, ["echo", "hello"])
print(result.exit_code, result.output)

# Stream output
async for line in docker.exec.stream(container.id, ["tail", "-f", "/var/log/app.log"]):
    print(line)

🎼 Compose

await docker.compose.up("./my-project", detach=True, build=True)
services = await docker.compose.ps("./my-project")

async for line in docker.compose.logs("./my-project", service="web", follow=True):
    print(line)

await docker.compose.down("./my-project", volumes=True)

📡 Events

async for event in docker.events(filters={"type": ["container"]}):
    print(f"{event.action} {event.actor_id}")

Testing

aiowhales ships with MockTransport — test your Docker code without a running daemon:

from aiowhales import AsyncDockerClient
from aiowhales.testing import MockTransport

transport = MockTransport()
transport.register("GET", "/containers/json", [
    {"Id": "abc123", "Names": ["/myapp"], "State": "running",
     "Image": "nginx", "Created": 0, "Labels": {}, "Ports": []}
])

async with AsyncDockerClient(transport=transport) as docker:
    containers = await docker.containers.list()
    assert containers[0].id == "abc123"

Models

All models are frozen dataclasses — immutable snapshots of Docker state:

Model What it represents
Container Container state — id, name, status, image, ports, labels
Image Image metadata — id, tags, size, created
Volume Volume info — name, driver, mountpoint, labels
Network Network info — id, name, driver, connected containers
ContainerStats Live CPU / memory / network usage
ExecResult Command result — exit code + output
DockerEvent Engine event — action, type, actor, timestamp
PullProgress / PushProgress Image transfer progress
BuildOutput Build step output

Platform Support

Platform Transport Status
Linux Unix socket ✅ Fully supported
macOS Unix socket ✅ Fully supported
Windows TCP (Docker Desktop) ✅ Fully supported

Requirements

  • Python 3.11+
  • aiohttp >= 3.9
  • Docker Engine API v1.43+

License

Apache License 2.0 — see LICENSE for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aiowhales-0.1.1.tar.gz (122.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aiowhales-0.1.1-py3-none-any.whl (27.3 kB view details)

Uploaded Python 3

File details

Details for the file aiowhales-0.1.1.tar.gz.

File metadata

  • Download URL: aiowhales-0.1.1.tar.gz
  • Upload date:
  • Size: 122.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for aiowhales-0.1.1.tar.gz
Algorithm Hash digest
SHA256 45df2ac9e0dc33dc48980678588ebdb5dfb3e242eadadbe17ca0fd6c7248c596
MD5 7acff13357772f8497b720b92b40c627
BLAKE2b-256 ddb744a690ef9dd76f4de735dde312689313ab5486b9d1819bd8d822f3680a1f

See more details on using hashes here.

Provenance

The following attestation bundles were made for aiowhales-0.1.1.tar.gz:

Publisher: publish.yml on GuySharir/aiowhales

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file aiowhales-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: aiowhales-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 27.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for aiowhales-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a4ef05a27baf4f27938f6d0100d5a6f60090115b5f365ca79324c1bc79c7cad1
MD5 296308f35a77f64b6f8bee3ebf438dfc
BLAKE2b-256 a07fe3ffd27ad9d403f6c67fe7e47f85120bd1278b4e22d41faa03b44a3ab28a

See more details on using hashes here.

Provenance

The following attestation bundles were made for aiowhales-0.1.1-py3-none-any.whl:

Publisher: publish.yml on GuySharir/aiowhales

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page