Skip to content

Commit fc7300c

Browse files
tobixenclaude
andcommitted
chore: reduce ruff ignore list; fix 302 redirect in sync _put()
Code quality pass per #634: * Remove unused imports (copy, lxml.etree, caldav.compatibility_hints, CalendarSet, cdav/dav, Optional, timezone, Event/Todo TYPE_CHECKING stubs, bare `import niquests` availability check) * Replace bare `except:` with specific types (KeyError, AttributeError, Exception) across lib/error, lib/debug, lib/vcal, elements/cdav, calendarobjectresource, collection, compatibility_hints, davobject * Remove unused local variables (old_id, status, i, path, rv, feat_type, sup, feature_info, rc) across library modules * Use `# noqa: F401` for vobject availability checks in calendarobjectresource Sync _put() now updates self.url from the Location header on a 302 redirect, matching the existing async _async_put() behaviour. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 9b4ba5d commit fc7300c

12 files changed

Lines changed: 29 additions & 39 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ This project should adhere to [Semantic Versioning](https://semver.org/spec/v2.0
2828
* async path returned an unawaited coroutine instead of the actual result.
2929
* `accept_invite()` (and `decline_invite()`, `tentatively_accept_invite()`) now fall back to the client username as the attendee email address when the server does not expose the `calendar-user-address-set` property (RFC6638 §2.4.1). A `NotFoundError` with a descriptive message is raised when the username is also not an email address. Fixes https://github.com/python-caldav/caldav/issues/399
3030

31+
### Housekeeping
32+
33+
* Code quality: reduced ruff ignore list (https://github.com/python-caldav/caldav/issues/634) — removed unused imports (`copy`, `lxml.etree`, `CalendarSet`, `cdav/dav` re-exports, `Optional`, `timezone`, `Event`/`Todo` type stubs), replaced bare `except:` clauses with specific exception types (`KeyError`, `AttributeError`, `Exception` where broad catching is intentional), and removed unused local variables.
34+
* Sync `_put()` now updates `self.url` from the `Location` header on a 302 redirect, mirroring the existing async behaviour.
35+
3136
### Test framework, compatibility hints, documentation, examples
3237

3338
* RFC 6638 scheduling feature-detection infrastructure: new `scheduling`, `scheduling.mailbox`, and `scheduling.calendar-user-address-set` compatibility hints; legacy `no_scheduling` flags migrated. Default scheduling hints set for all the servers tested.

caldav/async_davclient.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from urllib.parse import unquote
1616

1717
if TYPE_CHECKING:
18-
from caldav.calendarobjectresource import CalendarObjectResource, Event, Todo
18+
from caldav.calendarobjectresource import CalendarObjectResource
1919
from caldav.collection import Calendar, Principal
2020

2121
# Try httpx first (preferred), fall back to niquests
@@ -50,7 +50,6 @@ def auth_flow(self, request):
5050

5151
if not _USE_HTTPX:
5252
try:
53-
import niquests
5453
from niquests import AsyncSession
5554
from niquests.structures import CaseInsensitiveDict
5655

caldav/calendarobjectresource.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ def __init__(
139139
if data is not None:
140140
self.data = data
141141
if id and self._get_component_type_cheap():
142-
old_id = self.icalendar_component.pop("UID", None)
142+
self.icalendar_component.pop("UID", None)
143143
self.icalendar_component.add("UID", id)
144144
# Clear raw data and update state to use the modified icalendar instance
145145
self._data = None
@@ -985,11 +985,11 @@ def _put(self, retry_on_failure=True):
985985
## SECURITY TODO: we should probably have a check here to verify that no such object exists already
986986
r = self.client.put(self.url, self.data, {"Content-Type": 'text/calendar; charset="utf-8"'})
987987
if r.status == 302:
988-
path = [x[1] for x in r.headers if x[0] == "location"][0]
988+
self.url = URL.objectify([x[1] for x in r.headers if x[0] == "location"][0])
989989
elif r.status not in (204, 201):
990990
if retry_on_failure:
991991
try:
992-
import vobject
992+
import vobject # noqa: F401
993993
except ImportError:
994994
retry_on_failure = False
995995
if retry_on_failure:
@@ -1013,7 +1013,7 @@ async def _async_put(self, retry_on_failure=True):
10131013
elif r.status not in (204, 201):
10141014
if retry_on_failure:
10151015
try:
1016-
import vobject
1016+
import vobject # noqa: F401
10171017
except ImportError:
10181018
retry_on_failure = False
10191019
if retry_on_failure:
@@ -1468,7 +1468,7 @@ def _set_icalendar_instance(self, inst):
14681468
try: ## DEPRECATION TODO: remove this try/except the future
14691469
## icalendar 7.x behaviour (not released yet as of 2025-09
14701470
cal = icalendar.Calendar.new()
1471-
except:
1471+
except AttributeError:
14721472
cal = icalendar.Calendar()
14731473
cal.add("prodid", "-//python-caldav//caldav//en_DK")
14741474
cal.add("version", "2.0")
@@ -2087,7 +2087,7 @@ def _complete_ical(self, i=None, completion_timestamp=None) -> None:
20872087
if i is None:
20882088
i = self.icalendar_component
20892089
assert self.is_pending(i)
2090-
status = i.pop("STATUS", None)
2090+
i.pop("STATUS", None)
20912091
i.add("STATUS", "COMPLETED")
20922092
i.add("COMPLETED", completion_timestamp)
20932093

@@ -2166,7 +2166,6 @@ def set_due(self, due, move_dtstart=False, check_dependent=False):
21662166
WARNING: the check_dependent-logic may be rewritten to support
21672167
RFC9253 in 3.x
21682168
"""
2169-
i = self.icalendar_component
21702169
if hasattr(due, "tzinfo") and not due.tzinfo:
21712170
due = due.astimezone(timezone.utc)
21722171
if check_dependent:

caldav/collection.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,7 @@ def _create(
604604

605605
mkcol = (dav.Mkcol() if method == "mkcol" else cdav.Mkcalendar()) + set
606606

607-
r = self._query(root=mkcol, query_method=method, url=path, expected_return_value=201)
607+
self._query(root=mkcol, query_method=method, url=path, expected_return_value=201)
608608

609609
# COMPATIBILITY ISSUE
610610
# name should already be set, but we've seen caldav servers failing
@@ -619,7 +619,7 @@ def _create(
619619
try:
620620
current_display_name = self.get_display_name()
621621
error.assert_(current_display_name == name)
622-
except:
622+
except Exception:
623623
log.warning(
624624
"calendar server does not support display name on calendar? Ignoring",
625625
exc_info=True,
@@ -680,7 +680,7 @@ async def _async_create(
680680
try:
681681
current_display_name = await self._async_get_property(dav.DisplayName())
682682
error.assert_(current_display_name == name)
683-
except:
683+
except Exception:
684684
log.warning(
685685
"calendar server does not support display name on calendar? Ignoring",
686686
exc_info=True,
@@ -973,7 +973,6 @@ def _multiget(self, event_urls: Iterable[URL], raise_notfound: bool = False) ->
973973
if self.url is None:
974974
raise ValueError("Unexpected value None for self.url")
975975

976-
rv = []
977976
prop = dav.Prop() + cdav.CalendarData()
978977
root = cdav.CalendarMultiGet() + prop + [dav.Href(value=u.path) for u in event_urls]
979978
# RFC 4791 section 7.9: "the 'Depth' header MUST be ignored by the
@@ -1713,7 +1712,7 @@ def get_objects_by_sync_token(
17131712
## TODO: look more into this, I think sync_token should be directly available through response object
17141713
try:
17151714
sync_token = response.sync_token
1716-
except:
1715+
except AttributeError:
17171716
sync_token = response.tree.findall(".//" + dav.SyncToken.tag)[0].text
17181717

17191718
## this is not quite right - the etag we've fetched can already be outdated
@@ -1873,7 +1872,7 @@ def __init__(
18731872
# we ignore the type here as this is defined in sub-classes only; require more changes to
18741873
# properly fix in a future revision
18751874
self.url = self.client.url.join(URL(self.get_property(self.findprop()))) # type: ignore
1876-
except:
1875+
except Exception:
18771876
logging.error("something bad happened", exc_info=True)
18781877
error.assert_(self.client.check_scheduling_support())
18791878
self.url = None
@@ -1891,7 +1890,7 @@ def get_items(self):
18911890
if not self._items:
18921891
try:
18931892
self._items = self.objects(load_objects=True)
1894-
except:
1893+
except Exception:
18951894
logging.debug(
18961895
"caldav server does not seem to support a sync-token REPORT query on a scheduling mailbox"
18971896
)
@@ -1904,7 +1903,7 @@ def get_items(self):
19041903
else:
19051904
try:
19061905
self._items.sync()
1907-
except:
1906+
except Exception:
19081907
self._items = [
19091908
CalendarObjectResource(url=x[0], client=self.client) for x in self.children()
19101909
]

caldav/compatibility_hints.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -375,9 +375,6 @@ def set_feature(self, feature, value=True):
375375
else:
376376
raise AssertionError
377377
self.copyFeatureSet(fc, collapse=False)
378-
feat_def = self.find_feature(feature)
379-
feat_type = feat_def.get('type', 'server-feature')
380-
sup = fc[feature].get('support', feat_def.get('default', 'full'))
381378

382379

383380
## TODO: Why is this camelCase while every other method is with under_score? rename ...
@@ -396,7 +393,6 @@ def copyFeatureSet(self, feature_set, collapse=True):
396393
UserWarning,
397394
stacklevel=3,
398395
)
399-
feature_info = {}
400396
value = feature_set[feature]
401397
if feature not in self._server_features:
402398
self._server_features[feature] = {}
@@ -570,7 +566,7 @@ def _derive_from_subfeatures(self, feature, feature_info, return_type, accept_fr
570566
subfeature_info = self.find_feature(subfeature_key)
571567
if 'default' in subfeature_info:
572568
continue
573-
except:
569+
except Exception:
574570
pass
575571

576572
total_relevant += 1

caldav/davclient.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
For async code, use: from caldav import aio
99
"""
1010

11-
import copy
1211
import logging
1312
import sys
1413
import time
@@ -38,19 +37,15 @@
3837

3938
from collections.abc import Mapping
4039

41-
from lxml import etree
42-
43-
import caldav.compatibility_hints
4440
from caldav import __version__
4541
from caldav.base_client import BaseDAVClient
4642
from caldav.base_client import get_calendars as _base_get_calendars
4743
from caldav.base_client import get_davclient as _base_get_davclient
48-
from caldav.collection import Calendar, CalendarSet, Principal
44+
from caldav.collection import Calendar, Principal
4945
from caldav.compatibility_hints import FeatureSet
5046

5147
# Re-export CONNKEYS for backward compatibility
5248
from caldav.config import CONNKEYS # noqa: F401
53-
from caldav.elements import cdav, dav
5449
from caldav.lib import error
5550
from caldav.lib.python_utilities import to_wire
5651
from caldav.lib.url import URL
@@ -65,7 +60,7 @@
6560
from typing import Self
6661

6762
if TYPE_CHECKING:
68-
from caldav.calendarobjectresource import CalendarObjectResource, Event, Todo
63+
from caldav.calendarobjectresource import CalendarObjectResource
6964

7065

7166
"""

caldav/davobject.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,6 @@ def get_properties(
389389
if self.is_async_client:
390390
return self._async_get_properties(props, depth, parse_response_xml, parse_props)
391391

392-
rc = None
393392
response = self._query_properties(props, depth)
394393
if not parse_response_xml:
395394
return response
@@ -427,7 +426,6 @@ async def _async_get_properties(
427426
parse_props: bool = True,
428427
):
429428
"""Async implementation of get_properties."""
430-
rc = None
431429
response = await self._async_query_properties(props, depth)
432430
if not parse_response_xml:
433431
return response
@@ -577,7 +575,7 @@ def name(self) -> str | None:
577575
def __str__(self) -> str:
578576
try:
579577
return str(self.get_property(dav.DisplayName(), use_cached=True)) or self.url
580-
except:
578+
except Exception:
581579
return str(self.url)
582580

583581
def __repr__(self) -> str:

caldav/elements/cdav.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def _to_utc_date_string(ts):
2020
## in python 3.6 and higher, ts.astimezone() will assume a
2121
## naive timestamp is localtime (and so do we)
2222
ts = ts.astimezone(utc_tz)
23-
except:
23+
except (OverflowError, OSError):
2424
## native time stamp and the current python version is
2525
## not able to treat it as localtime.
2626
import tzlocal

caldav/jmap/convert/_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from __future__ import annotations
66

7-
from datetime import date, datetime, timedelta, timezone
7+
from datetime import date, datetime, timedelta
88

99

1010
def _timedelta_to_duration(td: timedelta) -> str:

caldav/lib/debug.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ def xmlstring(root):
88
root = root.xmlelement()
99
try:
1010
return etree.tostring(root, pretty_print=True).decode("utf-8")
11-
except:
11+
except Exception:
1212
return root
1313

1414

0 commit comments

Comments
 (0)