Conversation
Stalwart (and RFC-compliant servers in general) may return the same iCal object with different line folding depending on the fetch method (get_journal_by_uid vs get_journals). The old test relied on a side effect of property access on icalendar_instance to force re-serialization through the icalendar library (which normalizes folding), then compared .data. After the ruff B018 cleanup replaced that with the explicit get_icalendar_instance() call (which has no side effects on .data), the raw differently-folded bytes were compared and the assertion failed. Fix: compare to_ical() output directly, which is both side-effect-free and normalizes line folding. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When PYTHON_CALDAV_USE_TEST_SERVER=1 is set (or testconfig=True), and no
testing_allowed config-file section is found, get_davclient() now falls
back to spinning up the first enabled server from the test-server registry.
Registry changes:
- Xandikos is now discovered before Radicale in _discover_embedded_servers(),
giving it higher default priority
- all_servers() and enabled_servers() now return servers sorted by priority
(lower number = first); defaults: embedded=10, docker=20, external=30
- Per-server priority can be overridden via priority: <int> in either
caldav_test_servers.yaml or a calendar config file section
- Three new env vars filter which server types are included:
PYTHON_CALDAV_TEST_EMBEDDED (default: enabled)
PYTHON_CALDAV_TEST_DOCKER (default: enabled)
PYTHON_CALDAV_TEST_EXTERNAL (default: enabled)
Set any to 0/false/no/off to exclude that category.
caldav/config.py changes:
- _get_test_server_config() falls back to _try_start_registry_server()
when no testing_allowed config section is found
- _try_start_registry_server() iterates enabled_servers() in priority
order, starts the first one that succeeds, and returns conn params
Tests added:
- testAutoEmbeddedServer: verifies that testconfig=True auto-starts
xandikos when no config file server is found (skipped if xandikos absent)
- test_get_davclient_returns_none_without_env_or_config: verifies that
get_davclient() returns None when no env var and no config file are present
- Add caldav/testing.py (shipped with package): EmbeddedServer base class,
XandikosServer, RadicaleServer — so pip-installed users can use
PYTHON_CALDAV_USE_TEST_SERVER=1 without the source tree.
- Rewrite caldav/config.py _get_test_server_config() to use the registry as
the primary authority (not a fallback): priority-ordered servers win over
config-file entries unless those entries have an explicit better priority.
Adds _collect_test_servers(), _get_pip_test_servers(), _ConfiguredServer,
_test_server_to_params(). Removes _try_start_registry_server() and
_registry_server_to_params() which duplicated client_context() logic.
- Rewrite tests/test_servers/helpers.py client_context(): no longer writes a
temporary config file; just starts the server directly and sets
PYTHON_CALDAV_USE_TEST_SERVER=1 for the duration.
- Rewrite tests/test_servers/embedded.py: XandikosTestServer and
RadicaleTestServer now delegate to caldav.testing.XandikosServer /
RadicaleServer, eliminating duplicated startup/teardown code.
- tests/test_caldav.py: add testAutoEmbeddedServer and
test_get_davclient_returns_none_without_env_or_config; fix
caldav_servers[-1] → caldav_servers[0] (embedded servers now sort first).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ndar_name/url filtering
Two bugs fixed, one new feature:
Bug 1: _extract_conn_params_from_section silently dropped calendar_name and
calendar_url keys from config sections, so get_calendars(config_section="foo")
where foo had calendar_name: "My Cal" would return all calendars instead of
filtering. Fixed by extracting those keys alongside the caldav_* ones.
Bug 2: expand_config_section was never called when reading the config file, so
meta-sections ({"contains": ["work", "personal"]}) had no effect.
New feature: get_calendars() now uses expand_config_section on the config file
path, expanding a single config_section value ("*", "all", "work_*", ...) to
multiple leaf sections. Each expanded section gets its own DAVClient; all
calendars are aggregated into one CalendarCollection. CalendarCollection now
holds a list of clients and closes all of them on exit.
The single-server path (explicit url=, env vars, testconfig=True) is unchanged.
Per-section calendar_name / calendar_url are used as filters; function-level
arguments override them.
New helper _fetch_calendars_for_client() eliminates the duplicate calendar-
fetching logic that was inline in get_calendars().
New config.py export: get_all_file_connection_params(config_file, section).
Tests added in test_caldav_unit.py (TestExpandConfigSection,
TestGetAllFileConnectionParams) and test_caldav.py (TestGetCalendarsConfig).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
get_icalendar_component() returns a deepcopy of the inner VEVENT/VTODO/VJOURNAL subcomponent for read-only access, consistent with the get_icalendar_instance() naming convention. edit_icalendar_component() is a context manager that yields the inner component directly for editing, delegating to edit_icalendar_instance() so all borrow/state/save machinery is reused. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…arning
RFC 4791 §9.9 requires UTC datetime values in time-range queries. When
plain date objects were passed to calendar.search() or calendar.searcher(),
they flowed into icalendar_searcher.Searcher unchanged, which emitted a
logging.warning("Date-range searches not well supported yet; use datetime
rather than dates") for every object checked during client-side filtering.
Fix: extract _populate_searcher() helper (eliminating the duplicated loop
between searcher() and search()), and coerce any date to
datetime(Y, M, D, tzinfo=utc) before setting start/end/alarm_start/alarm_end
on the searcher.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ble()
A bodyless PROPFIND is treated as allprop by RFC 4918 §9.1. Xandikos
handles allprop on the RootPage by iterating all registered properties,
several of which (owner, creationdate, comment) are not implemented on
RootPage and raise NotImplementedError, producing spurious ERROR log
lines on every liveness poll during test-server startup.
Send a minimal PROPFIND body that asks only for {DAV:}resourcetype. This
verifies the server is a real WebDAV endpoint (no false positives from
non-DAV HTTP servers) without triggering allprop enumeration. The
Content-Type: application/xml header is required; without it Xandikos
rejects the request with 415 Unsupported Media Type.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nsion
- Fully document all config-file features implemented in caldav/config.py:
calendar_name/calendar_url per-section, inherits (section inheritance),
contains (meta-sections), glob patterns, * wildcard, disable flag,
${VAR} / ${VAR:-default} env-var expansion, features key, CALDAV_*
env vars, and CALDAV_CONFIG_FILE / CALDAV_CONFIG_SECTION.
- Add configfile to the toctree (index.rst).
- Add a :doc:`configfile` cross-reference from the tutorial's
"Real Configuration" section.
- Add TestConfigSectionInheritance (3 tests) covering the inherits key.
- Add TestExpandEnvVars (5 tests) covering expand_env_vars and its
end-to-end effect through get_all_file_connection_params.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
get_davclient, get_calendar, get_calendars are defined in caldav.davclient, not in the top-level caldav namespace from Sphinx's perspective. Update all six :func: roles to use the canonical caldav.davclient.* paths so they render as actual hyperlinks. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- examples/basic_usage_examples.py used the deprecated `c.name` property, causing test_examples to fail with DeprecationWarning - aiohttp is a transitive dep used only inside XandikosServer.start() in caldav/testing.py; add it to deptry DEP003 ignore to silence false positive Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
I'm redoing the tutorial. While working on it, I found quite many issues. I'm tossing both the tutorial work and the fixes needed to get the tutorial to work into one pull request. Also, piggybacking in a fix for a test breakage for Stalwart. Somehow that commit fell out while I was doing the v3.0.2-release.