Improve type hints using TYPE_CHECKING pattern

Add TYPE_CHECKING imports and conditional imports for Gramps types:
- Added TYPE_CHECKING to typing imports
- Added conditional imports for Event, Person, Family under TYPE_CHECKING block
- No runtime overhead, only used for type checking

Update TimelineEvent dataclass type hints:
- Changed event: 'Any' to event: 'Event'
- Changed person: Optional['Any'] to person: Optional['Person']
- Maintains forward references with string literals

Update all method parameter type hints:
- _create_timeline_event(): event: 'Event', person_obj: Optional['Person']
- _process_event(): event: 'Event', person_obj: Optional['Person']
- _find_person_for_event(): event: 'Event' -> Optional['Person']
- _get_event_label_text(): event: 'Event', person: Optional['Person']
- _person_matches_handle(): person: Optional['Person']
- _get_family_display_name(): family: 'Family'
- draw_event_label(): event: 'Event', person: Optional['Person']

Update cache dictionary type hint:
- _event_to_person_cache: Dict[str, Optional[Any]] -> Dict[str, Optional['Person']]

Results:
- All Optional[Any] instances replaced with specific Gramps types
- Better IDE autocomplete and type checking support
- Improved code documentation through type hints
- No runtime overhead (TYPE_CHECKING is False at runtime)
- Maintains backward compatibility
This commit is contained in:
Daniel Viegas 2025-11-29 22:40:03 +01:00
parent 3652246bd4
commit 27de315514

View File

@ -32,7 +32,10 @@ import colorsys
import logging import logging
import math import math
from dataclasses import dataclass from dataclasses import dataclass
from typing import Optional, List, Tuple, Any, Set, Dict from typing import Optional, List, Tuple, Any, Set, Dict, TYPE_CHECKING
if TYPE_CHECKING:
from gramps.gen.lib import Event, Person, Family
from gi.repository import Gtk from gi.repository import Gtk
from gi.repository import Gdk from gi.repository import Gdk
@ -339,8 +342,8 @@ class TimelineEvent:
"""Represents an event in the timeline with all necessary data.""" """Represents an event in the timeline with all necessary data."""
date_sort: int date_sort: int
date_obj: Date date_obj: Date
event: 'Any' # gramps.gen.lib.Event event: 'Event' # gramps.gen.lib.Event
person: Optional['Any'] # gramps.gen.lib.Person person: Optional['Person'] # gramps.gen.lib.Person
event_type: EventType event_type: EventType
y_pos: float = 0.0 y_pos: float = 0.0
@ -402,7 +405,7 @@ class MyTimelineView(NavigationView):
self._cached_date_range: Optional[int] = None self._cached_date_range: Optional[int] = None
self._cached_min_date: Optional[int] = None self._cached_min_date: Optional[int] = None
self._cached_max_date: Optional[int] = None self._cached_max_date: Optional[int] = None
self._event_to_person_cache: Dict[str, Optional[Any]] = {} # Event handle -> Person object (or None) self._event_to_person_cache: Dict[str, Optional['Person']] = {} # Event handle -> Person object (or None)
# Filter state # Filter state
self.filter_enabled: bool = False self.filter_enabled: bool = False
@ -1641,7 +1644,7 @@ class MyTimelineView(NavigationView):
if self.drawing_area: if self.drawing_area:
self.drawing_area.set_size_request(800, self.timeline_height) self.drawing_area.set_size_request(800, self.timeline_height)
def _create_timeline_event(self, event, person_obj: Optional[Any] = None, y_pos: float = 0.0) -> Optional[TimelineEvent]: def _create_timeline_event(self, event: 'Event', person_obj: Optional['Person'] = None, y_pos: float = 0.0) -> Optional[TimelineEvent]:
""" """
Create a TimelineEvent from an event object. Create a TimelineEvent from an event object.
@ -1693,7 +1696,7 @@ class MyTimelineView(NavigationView):
y_pos=y_pos y_pos=y_pos
) )
def _process_event(self, event, person_obj: Optional[Any] = None) -> Optional[TimelineEvent]: def _process_event(self, event: 'Event', person_obj: Optional['Person'] = None) -> Optional[TimelineEvent]:
""" """
Process a single event and create a TimelineEvent. Process a single event and create a TimelineEvent.
@ -1743,7 +1746,7 @@ class MyTimelineView(NavigationView):
except (AttributeError, KeyError) as e: except (AttributeError, KeyError) as e:
logger.warning(f"Error building event-to-person index: {e}", exc_info=True) logger.warning(f"Error building event-to-person index: {e}", exc_info=True)
def _find_person_for_event(self, event) -> Optional[Any]: def _find_person_for_event(self, event: 'Event') -> Optional['Person']:
""" """
Find a primary person associated with an event using the cached index. Find a primary person associated with an event using the cached index.
@ -1786,7 +1789,7 @@ class MyTimelineView(NavigationView):
return None return None
def _get_family_display_name(self, family) -> str: def _get_family_display_name(self, family: 'Family') -> str:
""" """
Get a display name for a family showing parent names. Get a display name for a family showing parent names.
@ -2081,7 +2084,7 @@ class MyTimelineView(NavigationView):
return (min_date, max_date, date_range) return (min_date, max_date, date_range)
def _person_matches_handle(self, person: Optional[Any], handle: str) -> bool: def _person_matches_handle(self, person: Optional['Person'], handle: str) -> bool:
""" """
Check if a person's handle matches the given handle. Check if a person's handle matches the given handle.
@ -2191,7 +2194,7 @@ class MyTimelineView(NavigationView):
(date_sort - min_date) / date_range (date_sort - min_date) / date_range
) * (timeline_y_end - timeline_y_start) ) * (timeline_y_end - timeline_y_start)
def _get_event_label_text(self, event, person: Optional[Any], event_type: EventType) -> str: def _get_event_label_text(self, event: 'Event', person: Optional['Person'], event_type: EventType) -> str:
""" """
Generate label text for an event. Centralized logic. Generate label text for an event. Centralized logic.
@ -3033,7 +3036,7 @@ class MyTimelineView(NavigationView):
drawer(context, x, y, size) drawer(context, x, y, size)
def draw_event_label(self, context: cairo.Context, x: float, y: float, def draw_event_label(self, context: cairo.Context, x: float, y: float,
date_obj: Date, event, person: Optional[Any], date_obj: Date, event: 'Event', person: Optional['Person'],
event_type: EventType, is_hovered: bool = False) -> None: event_type: EventType, is_hovered: bool = False) -> None:
""" """
Draw the label for an event with modern styling. Draw the label for an event with modern styling.