Skip to main content

aiowebdav2

aiowebdav2 is a typed asyncio WebDAV client for Python 3.13+.

Installation

pip install aiowebdav2

Remote API

The Client API is remote-only. It exposes WebDAV resources as paths and does not read from or write to the local filesystem.

import asyncio

from aiowebdav2 import Client


async def main() -> None:
    async with Client(
        "https://webdav.example.com/remote.php/dav/files/me/",
        username="me",
        password="secret",
    ) as client:
        await client.mkdir("/docs/", parents=True, exist_ok=True)
        await client.write("/docs/readme.txt", b"hello")

        info = await client.stat("/docs/readme.txt")
        print(info.path, info.size)

        for child in await client.list("/docs/"):
            print(child.path, child.is_dir)

        data = await client.read("/docs/readme.txt")
        print(data)


asyncio.run(main())

Main remote operations:

  • exists(path) -> bool
  • stat(path) -> ResourceInfo
  • is_dir(path) -> bool
  • list(path="/", recursive=False) -> list[ResourceInfo]
  • mkdir(path, parents=False, exist_ok=False) -> None
  • delete(path) -> None
  • copy(source, target, depth=None, overwrite=True) -> None
  • move(source, target, overwrite=False) -> None
  • read(path) -> bytes
  • await iter_read(path) -> ResponseIterator (request errors raise on await; the response is released at EOF, on error, on aclose() and on GC)
  • write(path, data, content_length=None, progress=None) -> None (progress(current, total) needs data to be an async iterable)
  • quota() -> QuotaInfo
  • get_property(path, property_request) -> Property | None
  • get_properties(path, property_requests) -> list[Property]
  • list_properties(path, properties=...) -> dict[str, list[Property]]
  • set_property(path, property) -> None
  • set_properties(path, properties) -> None
  • lock(path) -> Lock
  • unlock(path, lock_token) -> None

Filesystem Helpers

Local file transfer helpers live in aiowebdav2.filesystem. They depend on a Client, but the client does not depend on local paths. Install the optional extra before using them:

pip install "aiowebdav2[filesystem]"
from pathlib import Path
import asyncio

from aiowebdav2 import Client
from aiowebdav2.filesystem import download_file, download_tree, upload_file, upload_tree


async def main() -> None:
    async with Client(
        "https://webdav.example.com",
        username="me",
        password="secret",
    ) as client:
        await upload_file(client, Path("readme.txt"), "/docs/readme.txt", parents=True)
        await download_file(client, "/docs/readme.txt", Path("readme.txt"))
        await upload_tree(client, Path("docs"), "/docs/", concurrency=4)
        await download_tree(client, "/docs/", Path("docs"), concurrency=4)


asyncio.run(main())

Filesystem helper operations:

  • download_file(client, remote_path, local_path, progress=None) -> None
  • upload_file(client, local_path, remote_path, parents=False, progress=None) -> None
  • download_tree(client, remote_path, local_path, overwrite=False, concurrency=1, progress=None) -> None
  • upload_tree(client, local_path, remote_path, concurrency=1, progress=None) -> None

Progress callbacks receive (current_bytes, total_bytes | None) and may be sync or async callables.

Authentication

Pass username and password, or a bearer token in ClientOptions.

import asyncio

from aiowebdav2 import Client, ClientOptions


async def main() -> None:
    async with Client(
        "https://webdav.example.com",
        username="me",
        password="secret",
    ) as client:
        await client.list("/")

    async with Client(
        "https://webdav.example.com",
        options=ClientOptions(token="token"),
    ) as client:
        await client.list("/")


asyncio.run(main())

Sessions and Options

Client owns its aiohttp session unless an external session is passed.

import asyncio

from aiohttp import ClientSession, ClientTimeout

from aiowebdav2 import Client, ClientOptions


async def main() -> None:
    async with ClientSession() as session:
        client = Client(
            "https://webdav.example.com",
            options=ClientOptions(
                session=session,
                timeout=ClientTimeout(total=30),
                verify_ssl=True,
                root="/",
                chunk_size=65536,
                proxy="http://127.0.0.1:8080",
                proxy_headers={"Proxy-Authorization": "Basic ..."},
            ),
        )


asyncio.run(main())

Models

from dataclasses import dataclass
from datetime import datetime


@dataclass(frozen=True, slots=True, kw_only=True)
class ResourceInfo:
    path: str
    name: str
    is_dir: bool
    size: int | None = None
    modified: datetime | None = None
    created: datetime | None = None
    etag: str | None = None
    content_type: str | None = None

Property, PropertyRequest, and QuotaInfo are also frozen, slotted dataclasses.

Development

uv sync --group dev
uv run pytest --cov-report=term-missing
uv run ruff check .
uv run ruff format --check .
uv run mypy aiowebdav2

Metadata

Release files for aiowebdav2 1.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for aiowebdav2 1.0.1
File Size Uploaded Provenance Actions
aiowebdav2-1.0.1.tar.gz 136.0 kB With provenance Details for aiowebdav2-1.0.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for aiowebdav2 1.0.1
File Interpreter ABI Platform Actions
aiowebdav2-1.0.1-py3-none-any.whl Python 3 none any Details for aiowebdav2-1.0.1-py3-none-any.whl

Total release size: 154.0 kB

Release files / aiowebdav2-1.0.1.tar.gz

Download URL aiowebdav2-1.0.1.tar.gz
Size 136.0 kB
Tags Source
SHA-256 checksum
How to use checksums
3f06d1b0bfe56afd4ba98eef011d6bb3dbc3c8410c0bff11c4b7f42508d15966
BLAKE2b-256 checksum
How to use checksums
2edf6ef0ce98f4b36d9b6720e048f146e50dc080faa8fac2074db5dc5945e40c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / aiowebdav2-1.0.1-py3-none-any.whl

Download URL aiowebdav2-1.0.1-py3-none-any.whl
Size 18.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c3359ac7d41f8c4cdab28b5ef8767dbae14d0e3dd69b7e57c7728c975df1a290
BLAKE2b-256 checksum
How to use checksums
94e0a0719f812970dec72bb7d19f20cf2e786c9a835d07c5945e75fb0bc5efc2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Security for aiowebdav2 1.0.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for aiowebdav2-1.0.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page