33from __future__ import annotations
44
55import sys
6+ from collections .abc import Iterator
67from dataclasses import dataclass , fields
7- from typing import Any
8+ from typing import Any , TypeVar
89
910if sys .version_info >= (3 , 11 ):
1011 import tomllib
1112else :
12- import tomli as tomllib # type: ignore[no-redef]
13+ import tomli as tomllib
1314
1415
1516# ---------------------------------------------------------------------------
1617# Shared mixin for _Raw* data classes
1718# ---------------------------------------------------------------------------
1819
20+ _T = TypeVar ("_T" , bound = "_FromDictMixin" )
21+
22+
1923class _FromDictMixin :
2024 """Mixin providing a generic from_dict classmethod for dataclasses."""
2125
2226 @classmethod
23- def from_dict (cls , d : dict [str , Any ]):
27+ def from_dict (cls : type [ _T ] , d : dict [str , Any ]) -> _T :
2428 known = {f .name for f in fields (cls )} # type: ignore[arg-type]
2529 return cls (** {k : v for k , v in d .items () if k in known })
2630
@@ -90,7 +94,7 @@ class _RawUI:
9094 selection_fg : str | None = None
9195
9296 @classmethod
93- def from_dict (cls , d : dict [str , Any ]) -> " _RawUI" :
97+ def from_dict (cls , d : dict [str , Any ]) -> _RawUI :
9498 """Build from a nested dict as produced by TOML [ui.bg], [ui.fg], etc."""
9599 field_names = {f .name for f in fields (cls )}
96100 flat : dict [str , str ] = {}
@@ -126,7 +130,7 @@ class _RawBase16:
126130 base0f : str | None = None
127131
128132 @classmethod
129- def from_dict (cls , d : dict [str , Any ]) -> " _RawBase16" :
133+ def from_dict (cls , d : dict [str , Any ]) -> _RawBase16 :
130134 field_names = {f .name for f in fields (cls )}
131135 # Normalise keys to lowercase (spec examples show base0A etc.)
132136 normalised = {k .lower (): v for k , v in d .items ()}
@@ -153,7 +157,7 @@ def __len__(self) -> int:
153157 def __getitem__ (self , index : int ) -> str :
154158 return self .colors [index ]
155159
156- def __iter__ (self ):
160+ def __iter__ (self ) -> Iterator [ str ] :
157161 return iter (self .colors )
158162
159163
@@ -262,7 +266,7 @@ class Theme:
262266 base16 : Base16
263267
264268 @classmethod
265- def _from_raw (cls , raw : " _RawTheme" ) -> " Theme" :
269+ def _from_raw (cls , raw : _RawTheme ) -> Theme :
266270 meta = raw .resolve_meta ()
267271 palette = raw .resolve_palette ()
268272 ansi = raw .resolve_ansi ()
@@ -284,12 +288,12 @@ def get_palette_ramp(self, name: str) -> dict[int, str]:
284288 return {i : color for i , color in enumerate (ramp .colors )}
285289
286290 @classmethod
287- def from_toml (cls , data_str : str ) -> " Theme" :
291+ def from_toml (cls , data_str : str ) -> Theme :
288292 raw = _RawTheme .from_toml (data_str )
289293 return cls ._from_raw (raw )
290294
291295 @classmethod
292- def from_dict (cls , data : dict [str , Any ]) -> " Theme" :
296+ def from_dict (cls , data : dict [str , Any ]) -> Theme :
293297 raw = _RawTheme .from_dict (data )
294298 return cls ._from_raw (raw )
295299
@@ -505,14 +509,14 @@ def _is_dark(self) -> bool:
505509 # ------------------------------------------------------------------
506510
507511 @classmethod
508- def from_toml (cls , data_str : str ) -> " _RawTheme" :
512+ def from_toml (cls , data_str : str ) -> _RawTheme :
509513 data = tomllib .loads (data_str )
510514 if not data :
511515 raise ValueError ("Empty theme file" )
512516 return cls .from_dict (data )
513517
514518 @classmethod
515- def from_dict (cls , data : dict [str , Any ]) -> " _RawTheme" :
519+ def from_dict (cls , data : dict [str , Any ]) -> _RawTheme :
516520 # --- [theme] section (required) ---
517521 theme_dict = data .get ("theme" , {})
518522 theme_meta = _RawThemeMeta .from_dict (theme_dict )
0 commit comments