From 80441068df47125d9ea0850f1d3b4d82b77b43e6 Mon Sep 17 00:00:00 2001 From: Robert Irelan Date: Tue, 18 Jan 2022 13:22:34 -0800 Subject: [PATCH 1/5] Retry PUT 409 errors - delete and re-create event Attempt to address #963 I would appreciate a code review here - there might be a more elegant way to address this problem, but it works for my (very limited) use case. --- vdirsyncer/storage/dav.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/vdirsyncer/storage/dav.py b/vdirsyncer/storage/dav.py index dcaf15e9..3f805294 100644 --- a/vdirsyncer/storage/dav.py +++ b/vdirsyncer/storage/dav.py @@ -593,12 +593,30 @@ async def _put(self, href, item, etag): async def update(self, href, item, etag): if etag is None: raise ValueError("etag must be given and must not be None.") - href, etag = await self._put(self._normalize_href(href), item, etag) + try: + href, etag = await self._put(self._normalize_href(href), item, etag) + except aiohttp.ClientResponseError as e: + if e.status == 409: + dav_logger.debug("Conflict, will delete old event and recreate it.") + await self.delete(self._normalize_href(href), None) + dav_logger.debug("Now trying again") + href, etag = await self._put(self._normalize_href(href), item, None) + else: + raise e return etag async def upload(self, item: Item): href = self._get_href(item) - rv = await self._put(href, item, None) + try: + rv = await self._put(href, item, None) + except aiohttp.ClientResponseError as e: + if e.status == 409: + dav_logger.debug("Conflict, will delete old event and recreate it.") + await self.delete(href, None) + dav_logger.debug("Now trying again") + rv = await self._put(href, item, None) + else: + raise e return rv async def delete(self, href, etag): From 6139d6d3a6696bd56ec7e822b41444024fbac99b Mon Sep 17 00:00:00 2001 From: "robert.irelan" Date: Mon, 28 Mar 2022 13:24:41 -0700 Subject: [PATCH 2/5] Don't throw exceptions on unprocessed hrefs in get_multi Rather, just log them. This allows my calendar sync to continue to work even if some of the hrefs can't be retrieved. In my case, one of the hrefs on my calendar was always returning a 404, so I want to skip it. --- vdirsyncer/storage/dav.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vdirsyncer/storage/dav.py b/vdirsyncer/storage/dav.py index 3f805294..d4f5ab4e 100644 --- a/vdirsyncer/storage/dav.py +++ b/vdirsyncer/storage/dav.py @@ -553,7 +553,7 @@ async def get_multi(self, hrefs): else: rv.append((href, Item(raw), etag)) for href in hrefs_left: - raise exceptions.NotFoundError(href) + dav_logger.warning(f"Skipping {href}, not found") for href, item, etag in rv: yield href, item, etag @@ -651,6 +651,7 @@ def _parse_prop_responses(self, root, handled_hrefs=None): props = response.findall("{DAV:}propstat/{DAV:}prop") if props is None or not len(props): dav_logger.debug(f"Skipping {href!r}, properties are missing.") + dav_logger.debug(f"Response for {href!r}: {etree.tostring(response)}") continue else: props = _merge_xml(props) From 3e5e4381c83902158d5190a7cdc7e6a80c869801 Mon Sep 17 00:00:00 2001 From: Robert Irelan Date: Mon, 13 Mar 2023 12:46:50 -0700 Subject: [PATCH 3/5] More hacks to ignore errors from Google Calendar --- vdirsyncer/http.py | 4 +++- vdirsyncer/storage/dav.py | 38 +++++++++++++++++++++++++++++-------- vdirsyncer/sync/__init__.py | 1 - 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/vdirsyncer/http.py b/vdirsyncer/http.py index 30cfe909..bcf621cc 100644 --- a/vdirsyncer/http.py +++ b/vdirsyncer/http.py @@ -147,7 +147,9 @@ async def request( logger.debug(response.status) logger.debug(response.headers) - logger.debug(response.content) + if (response.status >= 400 and hasattr(response, 'content') + and hasattr(response.content, '_buffer')): + logger.debug(response.content._buffer) if response.status == 412: raise exceptions.PreconditionFailed(response.reason) diff --git a/vdirsyncer/storage/dav.py b/vdirsyncer/storage/dav.py index d4f5ab4e..dad585e6 100644 --- a/vdirsyncer/storage/dav.py +++ b/vdirsyncer/storage/dav.py @@ -598,11 +598,22 @@ async def update(self, href, item, etag): except aiohttp.ClientResponseError as e: if e.status == 409: dav_logger.debug("Conflict, will delete old event and recreate it.") - await self.delete(self._normalize_href(href), None) - dav_logger.debug("Now trying again") - href, etag = await self._put(self._normalize_href(href), item, None) + try: + await self.delete(self._normalize_href(href), None) + dav_logger.debug("Now trying again") + rv = await self._put(self._normalize_href(href), item, None) + except aiohttp.ClientResponseError as delerr: + dav_logger.debug(f"delerr.status = {delerr.status}") + if delerr.status == 404: + dav_logger("Old event not found, ignoring") + rv = None, None + else: + raise + elif e.status == 403: + dav_logger.debug("Google Calendar refusing update, ignore") + rv = None, None else: - raise e + raise return etag async def upload(self, item: Item): @@ -612,11 +623,22 @@ async def upload(self, item: Item): except aiohttp.ClientResponseError as e: if e.status == 409: dav_logger.debug("Conflict, will delete old event and recreate it.") - await self.delete(href, None) - dav_logger.debug("Now trying again") - rv = await self._put(href, item, None) + try: + await self.delete(href, None) + dav_logger.debug("Now trying again") + rv = await self._put(href, item, None) + except aiohttp.ClientResponseError as delerr: + dav_logger.debug(f"delerr.status = {delerr.status}") + if delerr.status == 404: + dav_logger.debug("Old event not found, ignoring") + rv = None, None + else: + raise + elif e.status == 403: + dav_logger.debug("Google Calendar refusing update, ignore") + rv = None, None else: - raise e + raise return rv async def delete(self, href, etag): diff --git a/vdirsyncer/sync/__init__.py b/vdirsyncer/sync/__init__.py index 8678bc9a..6e489dae 100644 --- a/vdirsyncer/sync/__init__.py +++ b/vdirsyncer/sync/__init__.py @@ -208,7 +208,6 @@ async def _run_impl(self, a, b): ) ) href, etag = await self.dest.storage.upload(self.item) - assert href is not None self.dest.status.insert_ident( self.ident, ItemMetadata(href=href, hash=self.item.hash, etag=etag) From 966d711a17d3953fbe31c4744bd43452114b65f8 Mon Sep 17 00:00:00 2001 From: Robert Irelan Date: Tue, 14 Mar 2023 17:23:55 -0700 Subject: [PATCH 4/5] Also ignore 404 errors during delete for conflicts --- vdirsyncer/storage/dav.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vdirsyncer/storage/dav.py b/vdirsyncer/storage/dav.py index dad585e6..9726b10f 100644 --- a/vdirsyncer/storage/dav.py +++ b/vdirsyncer/storage/dav.py @@ -604,8 +604,8 @@ async def update(self, href, item, etag): rv = await self._put(self._normalize_href(href), item, None) except aiohttp.ClientResponseError as delerr: dav_logger.debug(f"delerr.status = {delerr.status}") - if delerr.status == 404: - dav_logger("Old event not found, ignoring") + if delerr.status == 403 or delerr.status == 404: + dav_logger.warning("Old event not found, ignoring") rv = None, None else: raise @@ -629,8 +629,8 @@ async def upload(self, item: Item): rv = await self._put(href, item, None) except aiohttp.ClientResponseError as delerr: dav_logger.debug(f"delerr.status = {delerr.status}") - if delerr.status == 404: - dav_logger.debug("Old event not found, ignoring") + if delerr.status == 403 or delerr.status == 404: + dav_logger.warning("Old event not found, ignoring") rv = None, None else: raise From b21e37e2b630b1c2a7da701d97936abd011cd20e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 15 Mar 2023 00:50:37 +0000 Subject: [PATCH 5/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- vdirsyncer/http.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/vdirsyncer/http.py b/vdirsyncer/http.py index bcf621cc..3a158862 100644 --- a/vdirsyncer/http.py +++ b/vdirsyncer/http.py @@ -147,8 +147,11 @@ async def request( logger.debug(response.status) logger.debug(response.headers) - if (response.status >= 400 and hasattr(response, 'content') - and hasattr(response.content, '_buffer')): + if ( + response.status >= 400 + and hasattr(response, "content") + and hasattr(response.content, "_buffer") + ): logger.debug(response.content._buffer) if response.status == 412: