Skip to content

Commit 34b05b9

Browse files
committed
Merge PR 827: fix: make sync clients work without async http library
#827 The sync client no longer imports the async client just to check whether it is async, so a requests-only install works again. The whole PR, including the follow-up commit adding a regression test and a changelog fragment, has been reviewed manually. Commits: - b2776c4 fix: make sync clients work without async http library - c42efbc fix: tolerate clients without an is_async attribute Assisted-By: Claude Sonnet 5.5 <noreply@anthropic.com> Reviewed-by: Tobias Brox <tobias@redpill-linpro.com>
2 parents 00c58dc + c42efbc commit 34b05b9

5 files changed

Lines changed: 38 additions & 3 deletions

File tree

‎caldav/async_davclient.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ class AsyncDAVClient(BaseDAVClient):
125125
principal = await client.get_principal()
126126
"""
127127

128+
is_async = True
128129
proxy: str | None = None
129130
url: URL = None
130131
huge_tree: bool = False

‎caldav/base_client.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,8 @@ class BaseDAVClient(ABC):
175175
- Common properties (username, password, auth_type, etc.)
176176
"""
177177

178+
is_async = False
179+
178180
# Property lists for PROPFIND requests - shared between sync and async
179181
CALENDAR_HOME_SET_PROPS = ["{urn:ietf:params:xml:ns:caldav}calendar-home-set"]
180182
CALENDAR_LIST_PROPS = [

‎caldav/davobject.py‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,8 @@ def is_async_client(self) -> bool:
143143
"""
144144
if self.client is None:
145145
return False
146-
from caldav.async_davclient import AsyncDAVClient
147-
148-
return isinstance(self.client, AsyncDAVClient)
146+
# `is True` is deliberate: a Mock attribute must count as False.
147+
return getattr(self.client, "is_async", False) is True
149148

150149
def children(
151150
self, type: str | None = None
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
The sync client raised `ImportError` on calendar and object operations when no async HTTP library was installed (e.g. a `requests`-only install). https://github.com/python-caldav/caldav/pull/827

‎tests/test_http_libraries.py‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,3 +313,35 @@ def test_response_resolves(self) -> None:
313313
from caldav.lib.http_sync import Response as SharedResponse
314314

315315
assert Response is SharedResponse
316+
317+
318+
_SYNC_WITHOUT_ASYNC = textwrap.dedent(
319+
"""
320+
import sys
321+
322+
class Blocker:
323+
def find_spec(self, fullname, path=None, target=None):
324+
if fullname.split(".")[0] in {names!r}:
325+
raise ImportError("blocked by the test: " + fullname)
326+
return None
327+
328+
sys.meta_path.insert(0, Blocker())
329+
from caldav import DAVClient
330+
from caldav.collection import Calendar
331+
332+
client = DAVClient(url="http://cal.example.com/")
333+
calendar = Calendar(client=client, url="http://cal.example.com/cal/")
334+
assert calendar.is_async_client is False
335+
"""
336+
)
337+
338+
339+
@pytest.mark.skipif(not importlib.util.find_spec("requests"), reason="requests not installed")
340+
def test_sync_client_works_without_an_async_library() -> None:
341+
"""A requests-only install must not trip over the async client's import."""
342+
result = subprocess.run(
343+
[sys.executable, "-c", _SYNC_WITHOUT_ASYNC.format(names=set(ASYNC_CANDIDATES))],
344+
capture_output=True,
345+
text=True,
346+
)
347+
assert result.returncode == 0, result.stderr or result.stdout

0 commit comments

Comments
 (0)