Skip to content

Commit 24015ed

Browse files
committed
if we cannot load an object using GET, try using REPORT and multiget. Updates #459
1 parent 0837d46 commit 24015ed

2 files changed

Lines changed: 34 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ In version 2.0, the requests library will be replaced with niquests or httpx. S
3333
### Added
3434

3535
* By now `calendar.search(..., sort_keys=("DTSTART")` will work. Sort keys expects a list or a tuple, but it's easy to send an attribute by mistake. https://github.com/python-caldav/caldav/pull/449
36+
* Compatibility workaround: If `event.load()` fails, it will retry the load by doing a multiget - https://github.com/python-caldav/caldav/pull/475 - https://github.com/python-caldav/caldav/issues/459
3637

3738
## [1.4.0] - 2024-11-05
3839

caldav/objects.py

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ def _query(
241241
expected_return_value is not None and ret.status != expected_return_value
242242
) or ret.status >= 400:
243243
## COMPATIBILITY HACK - see https://github.com/python-caldav/caldav/issues/309
244+
## TODO: server quirks!
244245
body = to_wire(body)
245246
if (
246247
ret.status == 500
@@ -946,7 +947,11 @@ def save(self):
946947
self._create(id=self.id, name=self.name, **self.extra_init_options)
947948
return self
948949

949-
def calendar_multiget(self, event_urls: Iterable[URL]) -> List["Event"]:
950+
## TODO: this is missing test code.
951+
## TODO: needs refactoring:
952+
## Objects found may be Todo and Journal, not only Event.
953+
## Replace the last lines with _request_report_build_resultlist method
954+
def calendar_multiget(self, event_urls: Iterable[URL]) -> List[_CC]:
950955
"""
951956
get multiple events' data
952957
@author mtorange@gmail.com
@@ -1078,15 +1083,15 @@ def date_search(
10781083

10791084
return objects
10801085

1086+
## TODO: this logic has been partly duplicated in calendar_multiget, but
1087+
## the code there is much more readable and condensed than this.
1088+
## Can code below be refactored?
10811089
def _request_report_build_resultlist(
10821090
self, xml, comp_class=None, props=None, no_calendardata=False
10831091
):
10841092
"""
10851093
Takes some input XML, does a report query on a calendar object
10861094
and returns the resource objects found.
1087-
1088-
TODO: similar code is duplicated many places, we ought to do even more code
1089-
refactoring
10901095
"""
10911096
matches = []
10921097
if props is None:
@@ -1122,7 +1127,6 @@ def _request_report_build_resultlist(
11221127
props=pdata,
11231128
)
11241129
)
1125-
11261130
return (response, matches)
11271131

11281132
def search(
@@ -1163,7 +1167,7 @@ def search(
11631167
unless the next parameter is set ...
11641168
* include_completed - include completed tasks
11651169
* event - sets comp_class to event
1166-
* text attribute search parameters: category, uid, summary, omment,
1170+
* text attribute search parameters: category, uid, summary, comment,
11671171
description, location, status
11681172
* no-category, no-summary, etc ... search for objects that does not
11691173
have those attributes. TODO: WRITE TEST CODE!
@@ -2338,6 +2342,8 @@ def copy(self, keep_uid: bool = False, new_parent: Optional[Any] = None) -> Self
23382342
obj.url = self.url
23392343
return obj
23402344

2345+
## TODO: move get-logics to a load_by_get method.
2346+
## The load method should deal with "server quirks".
23412347
def load(self, only_if_unloaded: bool = False) -> Self:
23422348
"""
23432349
(Re)load the object from the caldav server.
@@ -2351,7 +2357,10 @@ def load(self, only_if_unloaded: bool = False) -> Self:
23512357
if self.client is None:
23522358
raise ValueError("Unexpected value None for self.client")
23532359

2354-
r = self.client.request(str(self.url))
2360+
try:
2361+
r = self.client.request(str(self.url))
2362+
except:
2363+
return self.load_by_multiget()
23552364
if r.status == 404:
23562365
raise error.NotFoundError(errmsg(r))
23572366
self.data = vcal.fix(r.raw)
@@ -2361,6 +2370,23 @@ def load(self, only_if_unloaded: bool = False) -> Self:
23612370
self.props[cdav.ScheduleTag.tag] = r.headers["Schedule-Tag"]
23622371
return self
23632372

2373+
def load_by_multiget(self) -> Self:
2374+
"""
2375+
Some servers do not accept a GET, but we can still do a REPORT
2376+
with a multiget query
2377+
"""
2378+
error.assert_(self.url)
2379+
href = self.url.path
2380+
prop = dav.Prop() + cdav.CalendarData()
2381+
root = cdav.CalendarMultiGet() + prop + dav.Href(value=href)
2382+
response = self.parent._query(root, 1, "report")
2383+
results = response.expand_simple_props([cdav.CalendarData()])
2384+
error.assert_(len(results) == 1)
2385+
data = results[href][cdav.CalendarData.tag]
2386+
error.assert_(data)
2387+
self.data = data
2388+
return self
2389+
23642390
## TODO: self.id should either always be available or never
23652391
def _find_id_path(self, id=None, path=None) -> None:
23662392
"""

0 commit comments

Comments
 (0)