Skip to content

Commit bfa33ae

Browse files
tobixenclaude
andcommitted
fix: Handle full XML in _search_with_comptypes instead of raising
When search() is called with a full calendar-query XML and the server does not support search.comp-type.optional, the code raised NotImplementedError. Fall back to a single REPORT with the XML as-is (comp_class auto-detected from response data) for both the sync and async code paths. Fixes #637 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 8ea2757 commit bfa33ae

3 files changed

Lines changed: 49 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ This project should adhere to [Semantic Versioning](https://semver.org/spec/v2.0
1717
### Fixed
1818

1919
* Communication dump (`PYTHON_CALDAV_COMMDUMP` / `debug_dump_communication`) was accidentally dropped during the v3.0 refactor. Restored, with the dump logic extracted into a shared helper so both the sync and async code paths benefit. Fixes https://github.com/python-caldav/caldav/issues/638
20+
* `search()` raised `NotImplementedError` when a full calendar-query XML was passed and the server does not support `search.comp-type.optional` (e.g. DavMail). Falls back to a single REPORT with the XML as-is. Fixes https://github.com/python-caldav/caldav/issues/637
2021

2122
### Documentation
2223

caldav/search.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -688,9 +688,12 @@ def _search_with_comptypes(
688688
Internal method - does three searches, one for each comp class (event, journal, todo).
689689
"""
690690
if xml and (isinstance(xml, str) or "calendar-query" in xml.tag):
691-
raise NotImplementedError(
692-
"full xml given, and it has to be patched to include comp_type"
693-
)
691+
# Full XML provided – cannot inject a comp-type filter into it.
692+
# Fall back to a single REPORT request with the XML as-is; the
693+
# server is expected to return whatever comp-types the query
694+
# matches, and comp_class detection falls back to auto-detect.
695+
_, objects = calendar._request_report_build_resultlist(xml, None, props)
696+
return self.sort(objects)
694697
objects = []
695698

696699
assert self.event is None and self.todo is None and self.journal is None
@@ -769,9 +772,10 @@ async def _async_search_with_comptypes(
769772
Internal async method - does three searches, one for each comp class.
770773
"""
771774
if xml and (isinstance(xml, str) or "calendar-query" in xml.tag):
772-
raise NotImplementedError(
773-
"full xml given, and it has to be patched to include comp_type"
774-
)
775+
# Full XML provided – cannot inject a comp-type filter into it.
776+
# Fall back to a single REPORT request with the XML as-is.
777+
_, objects = await calendar._request_report_build_resultlist(xml, None, props)
778+
return self.sort(objects)
775779
objects: list[AsyncCalendarObjectResource] = []
776780

777781
assert self.event is None and self.todo is None and self.journal is None

tests/test_search.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,3 +829,41 @@ def mock_is_supported(feat, type_=bool):
829829
# Only the recurring event without DTEND should be returned
830830
assert len(result) == 1
831831
assert result[0].icalendar_component.get("DTEND") is None
832+
833+
834+
class TestSearchWithCompTypesFullXML:
835+
"""Regression tests for issue #637.
836+
837+
When search() is called with a full calendar-query XML and the server does
838+
not support search.comp-type.optional, the code used to raise
839+
NotImplementedError. It should instead do a single REPORT request with
840+
the XML as-is.
841+
"""
842+
843+
def test_search_full_xml_string_no_comp_type_optional(
844+
self, mock_client: DAVClient, mock_url: str
845+
) -> None:
846+
"""Passing a full XML string to search() must not raise NotImplementedError
847+
when the server does not support search.comp-type.optional."""
848+
849+
def mock_is_supported(feat, type_=bool):
850+
if feat == "search.comp-type.optional":
851+
return False
852+
if type_ == str:
853+
return "full"
854+
return True
855+
856+
mock_client.features.is_supported = mock.Mock(side_effect=mock_is_supported)
857+
mock_client.features.backward_compatibility_mode = False
858+
859+
event = Event(client=mock_client, url=mock_url, data=SIMPLE_EVENT)
860+
calendar = mock.Mock()
861+
calendar.client = mock_client
862+
calendar._request_report_build_resultlist.return_value = (mock.Mock(), [event])
863+
864+
full_xml = "<C:calendar-query xmlns:C='urn:ietf:params:xml:ns:caldav'/>"
865+
searcher = CalDAVSearcher()
866+
result = searcher.search(calendar, xml=full_xml)
867+
868+
assert result == [event]
869+
calendar._request_report_build_resultlist.assert_called_once_with(full_xml, None, None)

0 commit comments

Comments
 (0)