Skip to content

V3.2 release candidate#668

Merged
tobixen merged 17 commits into
masterfrom
v3.2-preparations
Apr 24, 2026
Merged

V3.2 release candidate#668
tobixen merged 17 commits into
masterfrom
v3.2-preparations

Conversation

@tobixen
Copy link
Copy Markdown
Member

@tobixen tobixen commented Apr 24, 2026

... just waiting for more tests to pass ...

tobixen and others added 16 commits April 24, 2026 13:11
Code quality pass per #634:
* Remove unused imports (copy, lxml.etree, caldav.compatibility_hints,
  CalendarSet, cdav/dav, Optional, timezone, Event/Todo TYPE_CHECKING stubs,
  bare `import niquests` availability check)
* Replace bare `except:` with specific types (KeyError, AttributeError,
  Exception) across lib/error, lib/debug, lib/vcal, elements/cdav,
  calendarobjectresource, collection, compatibility_hints, davobject
* Remove unused local variables (old_id, status, i, path, rv, feat_type,
  sup, feature_info, rc) across library modules
* Use `# noqa: F401` for vobject availability checks in calendarobjectresource

AI-generated commit, this is tedious work, well-suited for AI-assistance

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This is mostly AI-generated, unfortunately the prompts are missing.

The compatibility hints file is not (yet) considered an important part
of the library.  Reorganizations are considered to be tedious work,
well suited for AI generation with human oversight.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Nextcloud 33 introduced (or changed) soft-delete behaviour: when a calendar
is deleted via CalDAV, both the calendar and its objects are soft-deleted to
a trashbin.  When tests delete and re-create a calendar with the same slug,
Nextcloud reuses the same calendarid, and the soft-deleted objects (with their
UIDs) remain in oc_calendarobjects.  Trying to add a new event with the same
UID then fails with "UNIQUE constraint failed: calendarid, calendartype, uid".

Fix: set calendarRetentionObligation=0 in setup_nextcloud.sh.  When this
config value is the string '0', CalDavBackend::deleteCalendar and
deleteCalendarObject skip the trashbin entirely and hard-delete immediately
(see apps/dav/lib/CalDAV/CalDavBackend.php lines 940-941, 1572).  Also run
dav:retention:clean-up at startup to purge any leftover soft-deleted entries
from prior runs.

Also update the nextcloud compatibility hints to reflect the changed behaviour
of scheduling.mailbox.inbox-delivery in Nextcloud 33: the server now delivers
the iTIP notification to the inbox AND auto-schedules into the attendee's
calendar (quirk), whereas previously it only delivered to the inbox (full).

Prompt: `pytest -k nextcloud --last-failed` gives lots of failures.  It seems
to be something wrong with the nextcloud container.  Looks a bit like the
problems are due to the setup (user provisioning) is not being compatible with
the latest version of the image.  Please investigate.

This commit was AI-generated, according to the AI-POLICY it's acceptable to
use AI on the test server framework.  Without AI help, we would still only have
radicale, xandios and personal testing accounts available for  integration
tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Earlier work on scheduling testing stalled a bit due to lack
of proper testing accounts.  Docker test servers (Baikal, Nextcloud,
Cyrus, SOGo, DAViCal, Davis, CCS, Zimbra, Stalwart) now all pre-create
multiple users for scheduling tests.

This commit adds a testing framework for multi-account testing plus
various tests.

Prompt: The sync integration tests in tests/caldav_test.py has a
_TestSchedulingBase class, for tests that requires several users -
`save_with_invites` is tested there.  The class has a TODO-comment,
"Stalled a bit due to lack of proper testing accounts" is for sure not
true, I believe the whole comment is obsoleted, please verify.  The
async tests should be made as symmetric and similar to the sync tests as
possible.  There is a todo-comment there that FreeBusy isn't tested
(FreeBusy as defined in RFC663), please make tests for it, both for sync
and freesync.

(some followup-prompts appears to be missing here)

Test code is AI-generated, tests are among the things identified as
particularly suitable for AI-assisted codeing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Prompt: The previous commit adds a _async_schedule_inbox to
collection.py, this method is only used from the test.  Why is this?
Shouldn't it be possible to get the inbox through the public API while
doing async operations?
Prompt: pytest -k 'not compat' --last-failed -k 'test_invite_and_respond'
breaks now

This was predominantly written by Claude.  Trivial refactoring and bugfixing.

More work on async is done in a later commit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This is a combination of various work done on the compatibility hints.

The compatibility hints are still a bit "work in progress", so it's
considered OK to do breaking changes here even on minor-versions.
It's used mostly in the test code, but also internally in the library
to work around various compatibility problems.

The aim of the work is to get rid of all the old "compatibility issue
flags", all the new "features" should be tested by the
caldav-server-tester, this file should contain everything needed by
the code and by the tests, in addition to other useful/interessting
features checked by the caldav-server-tester project.

The work here is predominantly done by Claude - maintaining this file
is tedious work and not part of the core logic.  Quite some of the
prompts have gone missing, quite much of the work has been done in the
caldav-server-tester project.  I have noticed Claude hallucinating up
RFC references, so it's important to ask it to actually read the
RFCs and verify the references.

Here are some of the prompts:

prompt: (in caldav-server-tester project ) Create a check for freebusy-query.rfc6638
followup-prompt: this being a RFC6638-thing, it's meant to be used in a multi-user-scenario, so consider usage of ServerQuirkChecker.extra_principals.  (Also, rename the old FreeBusy check class with a name consistent with the new FreeBusy class)
followup-prompt: rename the feature to scheduling.freebusy-query (not freebusy-query.rfc6638)
followup-prompt: but then freebusy-query.rfc4791 becomes a bit redundant, it can be removed and merged into freebusy-query, with the description of freebusy-query emphasizing that it's a check for freebusy as given in rfc4791 (with reference to scheduling.freebusy-query for the rfc6638-variant)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This is a combination of various work done on the compatibility hints.

The compatibility hints are still a bit "work in progress", so it's
considered OK to do breaking changes here even on minor-versions.
It's used mostly in the test code, but also internally in the library
to work around various compatibility problems.

The aim of the work is to get rid of all the old "compatibility issue
flags", all the new "features" should be tested by the
caldav-server-tester, this file should contain everything needed by
the code and by the tests, in addition to other useful/interessting
features checked by the caldav-server-tester project.

This commit is a combination of many commits, many of them
AI-generated.  Maintaining this file is tedious work and not part of
the core logic.  Quite some of the prompts have gone missing, quite
much of the work has been done in the caldav-server-tester project.

I do believe the changes have been through sufficient scrutiny.  Claude
does have a history of messing up this file - hallucinating up RFC
references, and frequently flipping compatibility support instead of
doing research on why the feature support is "fragile".

Here are some of the prompts:

prompt: (in caldav-server-tester project ) Create a check for freebusy-query.rfc6638
followup-prompt: this being a RFC6638-thing, it's meant to be used in a multi-user-scenario, so consider usage of ServerQuirkChecker.extra_principals.  (Also, rename the old FreeBusy check class with a name consistent with the new FreeBusy class)
followup-prompt: rename the feature to scheduling.freebusy-query (not freebusy-query.rfc6638)
followup-prompt: but then freebusy-query.rfc4791 becomes a bit redundant, it can be removed and merged into freebusy-query, with the description of freebusy-query emphasizing that it's a check for freebusy as given in rfc4791 (with reference to scheduling.freebusy-query for the rfc6638-variant)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The scheduling freebusy-requests were completely untested and didn't work at all.

logic was human-written, test-code by Claude

Prompt: look into https://datatracker.ietf.org/doc/html/rfc6638#appendix-B.5
and make a pure unit test with a mocked-up response to a freebusy scheduling
request, exercising the handling part of it.  This will break with a
NotImplementedError as for now.  Only fix the test, do not fix the code logic.
Consider the TODO-comment in response.py, line 247, and give me an opinion on
weather it makes sense to reuse the _find_objects_and_props for scheduling
response or if it's better to create a dedicated separate method for this.
Adds the pycalendar/ai-prompt-auto-commit hooks so that Claude Code
prompts are automatically appended to commit messages.

- unstage-ai-prompts: keeps .prompts/ out of the index
- append-ai-prompts (prepare-commit-msg): injects prompts into message
- archive-ai-prompts (post-commit): moves .prompts/ to committed/
- prepare-ai-repository (manual): one-time setup already run

Also installs prepare-commit-msg and post-commit git hooks locally.

prompt: ~/.claude/skills/python-project-modernization.md is now updated
  with the https://github.com/pycalendar/ai-prompt-auto-commit tool.
  Please fix set this up for the caldav repo.

AI-generated: This is considered to be CI-infrastructure, one of the
areas identified to be particularly suitable for AI assistance.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ref RFC 6638 §3.2-3.3

Schedule-Tag implementation:
- `save()` and `add_event()` capture the Schedule-Tag from the
  response header and stores it in `self.props` (same logic with etag).
- `save()` sends `If-Schedule-Tag-Match` or `If-Match`-header if
  etag or schedule-tag is set.
- raises `ScheduleTagMismatchError` or `ETagMismatchError` on 412.
- `_reply_to_invite_request()`: when the server auto-schedules the event
  into the attendee's calendar (`scheduling.auto-schedule` supported),
  search all attendee calendars first and update the existing copy in
  place to preserve the server-assigned Schedule-Tag.  Fall through to
  `add_event()` only for non-auto-schedule servers.
- Assume SEQUENCE:0 default when SEQUENCE property is absent (RFC 5546
  section 2.1.4 requires incrementing for significant changes).
  test: add failing tests for Schedule-Tag support (RFC 6638)

Also adds design docs:
- docs/design/TODO_SCHEDULE_TAG.md (analysis and implementation plan,
  refs #660)
- docs/design/TODO_COMPATIBILITY_HINTS.md (FeatureSet cleanup analysis,
  refs #659)

The schedule-tag logic was predominantly hand-written (with some
trivial bugfixing done by Claude).  Claude contributed with design
suggestions, which have been partly followed.  Test code is predominantly
AI-written.

prompt: (exact prompt is lost, but I was discussing the schedule-tags,
  and the output is in the new file docs/design/TODO_SCHEDULE_TAG.md)
prompt: Please write up test code (unit tests + integration tests) on the
schedule tags.  Don't fix the code yet.
prompt: Please write up test code (unit tests + integration tests) on the
  schedule tags.  Don't fix the code yet.
followup-prompts: (discussions on test breakages.  While debugging,
  Claude has been insisting on searching for the Schedule-Tag by using
  a PropFind if it wasn't included in the headers, but that does not
  make sense)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude has been trying to disable rate-limiting in Stalwart.  I still
got up some RateLimit errors while doing testing on the scheduling,
but while asking Claude to continue debugging it, the problems
disappeared and never came back.  Hm.

prompt: The stalwart docker image stops working every now and then when
  running test code due to ratelimiting errors.  Any possibilities for
  deactivating this in the docker image?
(followup-promps complaining that it isn't working)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Invite-handling (accept_invite, decline_invite, tentatively_accept_invite)
now returns awaitable coroutines when used in async mode.

SynchronizableCalendarObjectCollection.sync() has also been dealt
with, togheter with `MailBox.get_items()` and `DAVObject.children()`

Also adds design docs:
- docs/design/TODO_SCHEDULE_TAG.md (analysis and implementation plan,
  refs #660)
- docs/design/TODO_COMPATIBILITY_HINTS.md (FeatureSet cleanup analysis,
  refs #659)

The code logic here is partly human-created, mostly partly AI-created,
certainly under human guideance.  The final secision on how to handle
async in 3.x has been carved in stone by a human. Test code is
predominantly AI-created.

The AI-generation involves tedious code duplication work and tedious
routine refactoring, chances for mistakes are bigger when doing it by
hand than by AI.  I've been looking through the changes, and I trust
the tests to uncover any errors slipping through.

Some of the (many) commits dealing with this have been squashed
into this commit.

The return type of cached properties should always be an awaitable
coroutine in async mode.

prompt: Please make an async version of the get_items method in
  `caldav/collections.py`
followup-prompt: I don't want workarounds in _async_get_items for
  async-unaware get_objects_by_sync_token. Fix _async_get_items assuming
  get_objects_by_sync_token will be made async-aware.
followup-prompt: Please make an async-version of get_objects_by_sync_token
prompt: Please investigate those failures:
  FAILED tests/test_async_integration.py::TestAsyncSchedulingForStalwart (...)
prompt: make an async version of _reply_to_invite_request in
  `caldav/calendarobjectresource.py`
prompt: In collection.py and calendarobjectresource.py and
  possibly in some of the other files as well, many methods are split up
  into a sync version and an async version.  Please make appropriate
  type-hints for all methods that in async mode will yield a coroutine
  rather than an object
Prompt: Let's refactor the async methods where it's possible.
The existing pattern goes like this:
  * we have a sync version `foo` or `_foo`
  * we have an async version of the same method, `_async_foo`
  * `foo` is *most of the time* doing
    `if self.is_async_client: return self._async_foo(...)`
I'd like to reduce the amount of duplicated code as well as to split out
the IO-logic as much as possible.  As for now, I want to go with this
pattern:
  * `foo` should *always* do the
    `if self.is_async_client: return self._async_foo(...)`-logic
  * `self._async_foo` should never be called upon other places
  * Quite many of the methods are doing some preparations, firing off
    some other method causing I/O, and then doing some processing of
    the data returned from the server.  Other methods are more complex,
    having mutliple code lines causing I/O.
  * For methods containing significant amount of logic (like, two or
    more code lines) before doing any IO, the
    `if self.is_async_client: return self._async_foo(...)`-logic
    should be moved to the last possible point in the method. * For methods
    containing significant amount of logic after doing the IO, split the
    logic out in a `_post_foo`-method.
(the rules above was later moved to a document and tweaked a bit)
prompt: Apply the rules from `docs/design/ASYNC_DUAL_MODE.md` for the
`def sync` and `def async_sync` in `collection.py`

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After an audit of caldav/operations/ and caldav/protocol/ (documented in
docs/design/OPERATIONS_PROTOCOL_AUDIT.md), both directories are deleted,
the code that was in use have been moved elsewhere.

The code changes are predominantly AI-written.  Tedious refactoring
work, chances for mistakes are bigger when doing it by hand than by AI.
I've been looking through the changes, and I trust the tests to uncover
any errors slipping through.

prompt: During the great attempt on Sans-IO refactoring, a directory
`caldav/operations/` was made. Please check up how much code is
duplicated and/or dead there, and come with recommendations on whether
to keep "operations" there or not. Same with the protocols folder.
followup-prompt: Save the analysis to the docs/design folder
followup-prompt: Kill the operations directory ref the document
followup-prompt: All response-related logic in the protocol directory
should be moved back to the response class. Make sure there is no
duplicated code or logic.
followup-prompt: move xml builders to the dav base client, and ensure
sync and async code paths uses the same builder methods
prompt: Deal with the code duplication in response.py
followup-prompt: It seems like the last commit, with purpose "remove code duplication in response.py" has more code additions than removed code?

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Funding.json is some kind of industry standard on how to beg for
funding.  Added it.  Fixes
#608 aka 95b9f5e.  The
funding file was partially generated with Claude Code - I asked the AI
to help reading the specs and setting up the json structure
accordingly.

The async support has proven a lot more fragile than what I had hoped for,
so it's appropriate to add some warnings in the async documentation.

Git commit messages should now follow the industry standard.

CHANGELOG prepared for v3.2.0 release

The commit is predominantly human-written, with the following exceptions:

* The code review document was AI-generated, human-updated
* Changelog was AI-maintained, but most of it has been rewritten by hand

prompt: Make a code review of all changes since v3.0.0
followup-prompt: write the review to a file under docs/design/
followup-prompt: The code review was not committed.  Commit, then work on the code duplication in response.py
prompt: the ChangeLog should be maintained

Assisted-By: Claude Sonnet 4.6
@tobixen tobixen marked this pull request as ready for review April 24, 2026 13:03
@tobixen tobixen merged commit 42135a0 into master Apr 24, 2026
8 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant