From 785b19812ee3b41a01eae4c10ba56dfaded07154 Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Fri, 10 Apr 2026 07:48:10 +0200 Subject: [PATCH] hook/email: remove/solve some type:ignore --- radicale/hook/email/__init__.py | 50 ++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/radicale/hook/email/__init__.py b/radicale/hook/email/__init__.py index d46f0e9b..e89961db 100644 --- a/radicale/hook/email/__init__.py +++ b/radicale/hook/email/__init__.py @@ -296,7 +296,7 @@ class VComponent: return [None] if not isinstance(sub_vobjects, (list, tuple)): sub_vobjects = [sub_vobjects] - return ([_class(vobject_item=so) for so in sub_vobjects if # type: ignore + return ([_class(vobject_item=so) for so in sub_vobjects if # type: ignore # TODO: Missing positional argument "component_type" in call to "VComponent" isinstance(so, vobject.base.Component)] or [None]) @@ -444,12 +444,12 @@ class Timezone(VComponent): @property def standard(self) -> Optional[StandardTimezone]: """Return the STANDARD subcomponent if it exists.""" - return self._get_sub_vobjects("standard", StandardTimezone)[0] # type: ignore + return self._get_sub_vobjects("standard", StandardTimezone)[0] # type: ignore # TODO: got "VComponent | None", expected "StandardTimezone | None" @property def daylight(self) -> Optional[DaylightTimezone]: """Return the DAYLIGHT subcomponent if it exists.""" - return self._get_sub_vobjects("daylight", DaylightTimezone)[0] # type: ignore + return self._get_sub_vobjects("daylight", DaylightTimezone)[0] # type: ignore # TODO: got "VComponent | None", expected "DaylightTimezone | None" class Event(VComponent): @@ -513,7 +513,7 @@ class Event(VComponent): @property def alarms(self) -> List[Alarm]: """Return a list of VALARM items in the event.""" - return self._get_sub_vobjects("valarm", Alarm) # type: ignore # Can be multiple + return self._get_sub_vobjects("valarm", Alarm) # type: ignore # Can be multiple # TODO: got "list[VComponent | None]", expected "list[Alarm]" @property def attendees(self) -> List[Attendee]: @@ -541,14 +541,14 @@ class Calendar(VComponent): @property def event(self) -> Optional[Event]: """Return the VEVENT item in the calendar.""" - return self._get_sub_vobjects("vevent", Event)[0] # type: ignore + return self._get_sub_vobjects("vevent", Event)[0] # type: ignore # TODO: got "VComponent | None", expected "Event | None" # TODO: Add VTODO and VJOURNAL support if needed @property def timezone(self) -> Optional[Timezone]: """Return the VTIMEZONE item in the calendar.""" - return self._get_sub_vobjects("vtimezone", Timezone)[0] # type: ignore + return self._get_sub_vobjects("vtimezone", Timezone)[0] # type: ignore # TODO: got "VComponent | None", expected "Timezone | None" class EmailEvent: @@ -637,19 +637,20 @@ class MessageTemplate: :param attendee: The specific attendee to include in the message, if not a mass email. :return: The formatted message body. """ + attendee_name: Union[str, None] if mass_email: # If this is a mass email, we do not use individual attendee names attendee_name = "everyone" else: assert attendee is not None, "Attendee must be provided for non-mass emails" - attendee_name = attendee.name if attendee else "Unknown Name" # type: ignore + attendee_name = attendee.name if attendee else "Unknown Name" context = { "attendee_name": attendee_name, "from_email": from_email, "organizer_name": event.event.organizer or "Unknown Organizer", "event_title": event.event.summary or "No Title", - "event_start_time": event.event.datetime_start.time_string(), # type: ignore + "event_start_time": event.event.datetime_start.time_string() if event.event.datetime_start else "No Start Time", "event_end_time": event.event.datetime_end.time_string() if event.event.datetime_end else "No End Time", "event_location": event.event.location or "No Location Specified", } @@ -666,19 +667,20 @@ class MessageTemplate: :param attendee: The specific attendee to include in the message, if not a mass email. :return: The formatted message subject. """ + attendee_name: Union[str, None] if mass_email: # If this is a mass email, we do not use individual attendee names attendee_name = "everyone" else: assert attendee is not None, "Attendee must be provided for non-mass emails" - attendee_name = attendee.name if attendee else "Unknown Name" # type: ignore + attendee_name = attendee.name if attendee else "Unknown Name" context = { "attendee_name": attendee_name, "from_email": from_email, "organizer_name": event.event.organizer or "Unknown Organizer", "event_title": event.event.summary or "No Title", - "event_start_time": event.event.datetime_start.time_string(), # type: ignore + "event_start_time": event.event.datetime_start.time_string() if event.event.datetime_start else "No Start Time", "event_end_time": event.event.datetime_end.time_string() if event.event.datetime_end else "No End Time", "event_location": event.event.location or "No Location Specified", } @@ -890,10 +892,10 @@ def _read_event(vobject_data: str) -> EmailEvent: """ v_cal: vobject.base.Component = vobject.readOne(vobject_data) cal: Calendar = Calendar(vobject_item=v_cal) - event: Event = cal.event # type: ignore + event: Union[Event, None] = cal.event return EmailEvent( - event=event, + event=event, # type: ignore # TODO: Argument "event" to "EmailEvent" has incompatible type "Event | None"; expected "Event" ics_content=vobject_data, ics_file_name="event.ics" ) @@ -955,6 +957,10 @@ class Hook(BaseHook): :type notification_item: HookNotificationItem :return: None """ + email_success: bool + email_event: EmailEvent + new_event: Union[Event, None] + if self.email_config.dryrun: logger.warning("Hook 'email': DRY-RUN received notification_item: %r", vars(notification_item)) else: @@ -972,7 +978,7 @@ class Hook(BaseHook): elif notification_type == HookNotificationItemTypes.UPSERT: # Handle upsert notifications - new_item_str: str = notification_item.new_content # type: ignore # A serialized vobject.base.Component + new_item_str: str = notification_item.new_content # A serialized vobject.base.Component previous_item_str: Optional[str] = notification_item.old_content if not ics_contents_contains_event(contents=new_item_str): @@ -980,15 +986,15 @@ class Hook(BaseHook): logger.debug("No event found in the ICS file, skipping notification.") return - email_event: EmailEvent = _read_event(vobject_data=new_item_str) # type: ignore + email_event = _read_event(vobject_data=new_item_str) if not email_event: logger.error("Failed to read event from new content: %s", new_item_str) return - email_event_event = email_event.event # type: ignore + email_event_event = email_event.event if not email_event_event: logger.error("Event could not be parsed from the new content: %s", new_item_str) return - email_event_end_time = email_event_event.datetime_end # type: ignore + email_event_end_time = email_event_event.datetime_end # Skip notification if the event end time is more than 1 minute in the past. if email_event_end_time and email_event_end_time.time: event_end = email_event_end_time.time @@ -1038,7 +1044,7 @@ class Hook(BaseHook): # Notify added attendees as "event created" if added_attendees: - email_success: bool = self.email_config.send_added_email( # type: ignore + email_success = self.email_config.send_added_email( attendees=added_attendees, event=email_event ) @@ -1048,7 +1054,7 @@ class Hook(BaseHook): # Notify removed attendees as "event deleted" if removed_attendees: - email_success: bool = self.email_config.send_deleted_email( # type: ignore + email_success = self.email_config.send_deleted_email( attendees=removed_attendees, event=email_event ) @@ -1059,7 +1065,7 @@ class Hook(BaseHook): # Notify unaltered attendees as "event updated" if details other than attendees have changed if unaltered_attendees and event_details_other_than_attendees_changed(original_event=previous_event, new_event=new_event): - email_success: bool = self.email_config.send_updated_email( # type: ignore + email_success = self.email_config.send_updated_email( attendees=unaltered_attendees, event=email_event ) @@ -1075,16 +1081,16 @@ class Hook(BaseHook): elif notification_type == HookNotificationItemTypes.DELETE: # Handle delete notifications - deleted_item_str: str = notification_item.old_content # type: ignore # A serialized vobject.base.Component + deleted_item_str: str = notification_item.old_content # A serialized vobject.base.Component if not ics_contents_contains_event(contents=deleted_item_str): # If the ICS file does not contain an event, we do not send any notifications. logger.debug("No event found in the ICS file, skipping notification.") return - email_event: EmailEvent = _read_event(vobject_data=deleted_item_str) # type: ignore + email_event = _read_event(vobject_data=deleted_item_str) - email_success: bool = self.email_config.send_deleted_email( # type: ignore + email_success = self.email_config.send_deleted_email( attendees=email_event.event.attendees, event=email_event )