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 math
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 Gdk
@ -339,8 +342,8 @@ class TimelineEvent:
"""Represents an event in the timeline with all necessary data."""
date_sort: int
date_obj: Date
event: 'Any' # gramps.gen.lib.Event
person: Optional['Any'] # gramps.gen.lib.Person
event: 'Event' # gramps.gen.lib.Event
person: Optional['Person'] # gramps.gen.lib.Person
event_type: EventType
y_pos: float = 0.0
@ -402,7 +405,7 @@ class MyTimelineView(NavigationView):
self._cached_date_range: Optional[int] = None
self._cached_min_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
self.filter_enabled: bool = False
@ -1641,7 +1644,7 @@ class MyTimelineView(NavigationView):
if self.drawing_area:
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.
@ -1693,7 +1696,7 @@ class MyTimelineView(NavigationView):
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.
@ -1743,7 +1746,7 @@ class MyTimelineView(NavigationView):
except (AttributeError, KeyError) as e:
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.
@ -1786,7 +1789,7 @@ class MyTimelineView(NavigationView):
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.
@ -2081,7 +2084,7 @@ class MyTimelineView(NavigationView):
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.
@ -2191,7 +2194,7 @@ class MyTimelineView(NavigationView):
(date_sort - min_date) / date_range
) * (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.
@ -3033,7 +3036,7 @@ class MyTimelineView(NavigationView):
drawer(context, x, y, size)
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:
"""
Draw the label for an event with modern styling.