Skip to content

Commit e81a4dd

Browse files
authored
Merge branch 'master' into issu653
2 parents e7b53b2 + 8f13339 commit e81a4dd

File tree

3 files changed

+27
-0
lines changed

3 files changed

+27
-0
lines changed

CHANGELOG.md

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

1919
* Reusing a `CalDAVSearcher` across multiple `search()` calls could yield inconsistent results: the first call would return only pending tasks (correct), but subsequent calls would change behaviour because `icalendar_searcher.Searcher.check_component()` mutated the `include_completed` field from `None` to `False` as a side-effect. Fixed by passing a copy with `include_completed` already resolved to `filter_search_results()`, leaving the original searcher object unchanged. Fixes https://github.com/python-caldav/caldav/issues/650
2020
* `Calendar.get_supported_components()` raised `KeyError` when the server did not include the `supported-calendar-component-set` property in its response. RFC 4791 section 5.2.3 states this property is optional and that its absence means all component types are accepted; the method now returns the RFC default `["VEVENT", "VTODO", "VJOURNAL"]` in that case, trimmed by any known server limitations from the compatibility hints (e.g. if `save-load.todo` is `unsupported`, `VTODO` is excluded). Fixes https://github.com/python-caldav/caldav/issues/653
21+
* `_resolve_properties()` would crash with `UnboundLocalError` in production mode when a server returned an empty or unrecognisable PROPFIND response (the response paths did not match the request URI and there was more than one or zero paths returned). Fixed by returning `{}` instead of falling through to an unbound variable. Related: https://github.com/pycalendar/calendar-cli/issues/114
2122

2223
## [3.1.0] - 2026-03-19
2324

caldav/davobject.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ def _resolve_properties(self, properties: dict) -> dict:
356356
f"paths found: {list(properties.keys())}"
357357
)
358358
error.assert_(False)
359+
return {}
359360
self.props.update(rc)
360361
return rc
361362

tests/test_caldav_unit.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2762,3 +2762,28 @@ def test_meta_section_returns_multiple_dicts(self, tmp_path):
27622762
"https://work.example.com/dav/",
27632763
"https://personal.example.com/dav/",
27642764
}
2765+
2766+
2767+
class TestResolveProperties:
2768+
"""Tests for _resolve_properties unbound variable bug (issue #647 / calendar-cli #114)."""
2769+
2770+
def _make_calendar(self, path="/calendar/"):
2771+
client = DAVClient(url="https://example.com")
2772+
return Calendar(client=client, url=f"https://example.com{path}")
2773+
2774+
def test_resolve_properties_empty_dict_production_mode(self):
2775+
"""In PRODUCTION mode, error.assert_ only logs; _resolve_properties must
2776+
not crash with UnboundLocalError when properties dict is empty."""
2777+
cal = self._make_calendar()
2778+
with mock.patch.object(error, "debugmode", "PRODUCTION"):
2779+
result = cal._resolve_properties({})
2780+
assert result == {}
2781+
2782+
def test_resolve_properties_unmatched_paths_production_mode(self):
2783+
"""Same but with a non-empty properties dict where path does not match."""
2784+
cal = self._make_calendar("/calendar/")
2785+
with mock.patch.object(error, "debugmode", "PRODUCTION"):
2786+
result = cal._resolve_properties(
2787+
{"/other/path/": {"foo": "bar"}, "/yet/another/": {"baz": "qux"}}
2788+
)
2789+
assert result == {}

0 commit comments

Comments
 (0)