Skip to content

Commit ab56331

Browse files
cbcoutinhoclaude
andcommitted
feat: Add save_event/todo/journal and fix async search
- Add save_event, save_todo, save_journal methods to AsyncCalendar - Add _use_or_create_ics helper method for ical data handling - Wire up sync wrappers to call async implementations - Fix async_search to use async classes instead of sync classes - Add simple httpx integration test suite for sync/async clients 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent e5a3bb4 commit ab56331

4 files changed

Lines changed: 399 additions & 5 deletions

File tree

‎caldav/_async/collection.py‎

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,78 @@ async def journal_by_uid(self, uid: str):
658658
"""Returns the journal with the given uid"""
659659
return await self.object_by_uid(uid, comp_filter=cdav.CompFilter("VJOURNAL"))
660660

661+
def _use_or_create_ics(self, ical, objtype, **ical_data):
662+
"""Create or use an ical object."""
663+
from caldav.lib import vcal
664+
from caldav.lib.python_utilities import to_wire
665+
666+
if ical_data or (
667+
(isinstance(ical, str) or isinstance(ical, bytes))
668+
and b"BEGIN:VCALENDAR" not in to_wire(ical)
669+
):
670+
if ical and "ical_fragment" not in ical_data:
671+
ical_data["ical_fragment"] = ical
672+
return vcal.create_ical(objtype=objtype, **ical_data)
673+
return ical
674+
675+
async def save_object(
676+
self,
677+
objclass,
678+
ical=None,
679+
no_overwrite=False,
680+
no_create=False,
681+
**ical_data,
682+
):
683+
"""Add a new object to the calendar.
684+
685+
Args:
686+
objclass: AsyncEvent, AsyncTodo, AsyncJournal
687+
ical: ical object (text, icalendar or vobject instance)
688+
no_overwrite: existing calendar objects should not be overwritten
689+
no_create: don't create a new object
690+
"""
691+
o = objclass(
692+
self.client,
693+
data=self._use_or_create_ics(
694+
ical,
695+
objtype=f"V{objclass.__name__.replace('Async', '').upper()}",
696+
**ical_data,
697+
),
698+
parent=self,
699+
)
700+
o = await o.save(no_overwrite=no_overwrite, no_create=no_create)
701+
return o
702+
703+
async def save_event(
704+
self, ical=None, no_overwrite=False, no_create=False, **ical_data
705+
):
706+
"""Save an event to the calendar."""
707+
from caldav._async.calendarobjectresource import AsyncEvent
708+
709+
return await self.save_object(
710+
AsyncEvent, ical, no_overwrite, no_create, **ical_data
711+
)
712+
713+
async def save_todo(
714+
self, ical=None, no_overwrite=False, no_create=False, **ical_data
715+
):
716+
"""Save a todo to the calendar."""
717+
from caldav._async.calendarobjectresource import AsyncTodo
718+
719+
return await self.save_object(
720+
AsyncTodo, ical, no_overwrite, no_create, **ical_data
721+
)
722+
723+
async def save_journal(
724+
self, ical=None, no_overwrite=False, no_create=False, **ical_data
725+
):
726+
"""Save a journal entry to the calendar."""
727+
from caldav._async.calendarobjectresource import AsyncJournal
728+
729+
return await self.save_object(
730+
AsyncJournal, ical, no_overwrite, no_create, **ical_data
731+
)
732+
661733

662734
class AsyncScheduleMailbox(AsyncCalendar):
663735
"""

‎caldav/_sync/collection.py‎

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -343,19 +343,26 @@ def journal_by_uid(self, uid):
343343
def save_event(self, ical=None, no_overwrite=False, no_create=False, **ical_data):
344344
from caldav._sync.calendarobjectresource import Event
345345

346-
# This needs to delegate to the original sync logic for now
347-
# as the async version doesn't have save_event yet
348-
raise NotImplementedError("save_event not yet implemented in async")
346+
async_event = _run_sync(
347+
self._async.save_event, ical, no_overwrite, no_create, **ical_data
348+
)
349+
return Event._from_async(async_event, self._sync_client, self)
349350

350351
def save_todo(self, ical=None, no_overwrite=False, no_create=False, **ical_data):
351352
from caldav._sync.calendarobjectresource import Todo
352353

353-
raise NotImplementedError("save_todo not yet implemented in async")
354+
async_todo = _run_sync(
355+
self._async.save_todo, ical, no_overwrite, no_create, **ical_data
356+
)
357+
return Todo._from_async(async_todo, self._sync_client, self)
354358

355359
def save_journal(self, ical=None, no_overwrite=False, no_create=False, **ical_data):
356360
from caldav._sync.calendarobjectresource import Journal
357361

358-
raise NotImplementedError("save_journal not yet implemented in async")
362+
async_journal = _run_sync(
363+
self._async.save_journal, ical, no_overwrite, no_create, **ical_data
364+
)
365+
return Journal._from_async(async_journal, self._sync_client, self)
359366

360367

361368
class ScheduleMailbox(Calendar):

‎caldav/search.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,15 @@ async def async_search(
564564
server_expand, props=props, filters=xml, _hacks=_hacks
565565
)
566566

567+
# Convert sync comp_class to async comp_class for async search
568+
sync_to_async_class = {
569+
Event: AsyncEvent,
570+
Todo: AsyncTodo,
571+
Journal: AsyncJournal,
572+
}
573+
if self.comp_class in sync_to_async_class:
574+
self.comp_class = sync_to_async_class[self.comp_class]
575+
567576
if not self.comp_class and not calendar.client.features.is_supported(
568577
"search.comp-type-optional"
569578
):

0 commit comments

Comments
 (0)