webdav-rfc4918

A secure-by-default WebDAV client for Python, built on RFC 4918. It offers a filesystem-style API, the raw protocol when you need it, an fsspec backend for pandas and Dask, and a dav command.

PyPI Python versions License

Installation

$ pip install webdav-rfc4918

Requires Python 3.11+. Built on requests. MIT licensed.

First call

import webdav

for resource in webdav.ls("https://webdav.example.org/Photos", auth=("user", "password")):
    print(resource, resource.size)

One call, one connection. For several calls against the same server, open a FileSystem instead.

Works with pandas and Dask

Installing the package registers the webdav and webdavs schemes with fsspec. pandas, Dask and other fsspec-based tools then read a WebDAV server like any other storage:

import fsspec
import pandas as pd

auth = ("user", "password")

df = pd.read_csv("webdavs://webdav.example.org/data.csv", storage_options={"auth": auth})

with fsspec.open("webdavs://webdav.example.org/Photos/Gorilla.jpg", auth=auth) as f:
    f.read()

It passes fsspec’s own conformance suite (130+ tests). Install with pip install webdav-rfc4918[fsspec], details in fsspec.

Tested against four servers

Each server reads the RFC a little differently. Nextcloud, Apache and nginx each get their own CI job and their own page:

Server

Tests

Scope

Details

Nextcloud

40+

Core RFC 4918 only, no oc:/nc: properties

Nextcloud

Apache mod_dav

100+

The primary deployment target

Apache

nginx + dav-ext

25+

Includes the module’s known limitations

nginx

WsgiDAV

1200+

All other tests, on every commit, no system dependency

-

Safe by default

  • 1500+ tests on every commit, over 90% code coverage. 70+ of them cover the security defaults.

  • Follows RFC 4918, checked clause by clause in 130+ tests.

  • TLS verification is on by default. Turning it off always warns.

  • Redirects never carry credentials or cookies to another origin.

  • Buffered responses, including multistatus XML, are size-capped, and so are redirect chains. Exact limits in Session.

  • download_file writes only to the path you give, through a temporary file, and refuses a symlink at that path.

The full list is in the README.

Next steps

Help and feedback

Bugs and questions: GitHub Issues. To report a vulnerability, follow SECURITY.md. Please do not open a public issue for it.