11from __future__ import annotations
2- from typing import Any , Callable , Generator , Literal as Lit , TYPE_CHECKING , Self
2+ from typing import TYPE_CHECKING
33import re
4- from dataclasses import dataclass , asdict , field as datacls_field
5- import hashlib
6- import pickle
4+ from dataclasses import dataclass , asdict , field as dataclass_field
75from ast import literal_eval
86
7+ if TYPE_CHECKING :
8+ from typing import Any , Callable , Generator , Self , Literal as Lit
9+
910
1011#########################################
1112# Basic classes: Value, Form, Flag, ... #
1920class ValueWrapper :
2021 v : Value
2122
22- def as_value (self ) -> PlainValue | None :
23+ def as_plain (self ) -> PlainValue | None :
2324 if self .v is None :
2425 return None
2526 if isinstance (self .v , list ):
@@ -28,6 +29,9 @@ def as_value(self) -> PlainValue | None:
2829 return self .v [0 ]
2930 return self .v
3031
32+ def as_str (self ) -> str | None :
33+ return str (self .as_plain ())
34+
3135 def as_list (self ) -> list [PlainValue ]:
3236 if self .v is None :
3337 return []
@@ -36,8 +40,13 @@ def as_list(self) -> list[PlainValue]:
3640 else :
3741 return [self .v ]
3842
39- def as_str (self ) -> str | None :
40- return str (self .as_value ())
43+ def as_str_list (self ) -> list [str ]:
44+ if self .v is None :
45+ return []
46+ elif isinstance (self .v , list ):
47+ return [str (x ) for x in self .v ]
48+ else :
49+ return [str (self .v )]
4150
4251
4352@dataclass (frozen = True )
@@ -151,31 +160,25 @@ class RawData:
151160
152161
153162@dataclass
154- class ParsedData :
163+ class Data :
155164 name : Value
156165 attrs : dict [str , Value ]
157166 content : Value
158167
159168 def ascontext (self ) -> dict [str , Any ]:
160169 return asdict (self )
161170
162- @property
163171 def title (self ) -> str | None :
164172 return ValueWrapper (self .name ).as_str ()
165173
166- @property
167- def hexdigest (self ) -> str :
168- return hashlib .sha1 (pickle .dumps (self )).hexdigest ()[:7 ]
169-
170-
171174@dataclass
172175class Field :
173176 #: Type of element.
174177 etype : type = str
175178 #: Type of container (if the field holds multiple values).
176179 ctype : type | None = None
177180 #: Flags of field.
178- flags : dict [str , Value ] = datacls_field (default_factory = dict )
181+ flags : dict [str , Value ] = dataclass_field (default_factory = dict )
179182
180183 # Type hints for builtin flags.
181184 if TYPE_CHECKING :
@@ -386,7 +389,7 @@ def _parse_single(
386389 except Exception as e :
387390 raise ValueError (f'parsing { field [0 ]} : { e } ' )
388391
389- def parse (self , data : RawData ) -> ParsedData :
392+ def parse (self , data : RawData ) -> Data :
390393 if data .name :
391394 name = self ._parse_single (('name' , self .name ), data .name )
392395 else :
@@ -409,25 +412,36 @@ def parse(self, data: RawData) -> ParsedData:
409412 else :
410413 content = None
411414
412- return ParsedData (name , attrs , content )
415+ return Data (name , attrs , content )
416+
417+ def fields (self , pred : Callable [[Field ], bool ] | None = None ) -> Generator [tuple [str , Field ]]:
418+ def ok (f : Field ) -> bool :
419+ return not pred or pred (f )
420+
421+ if self .name and ok (self .name ):
422+ yield 'name' , self .name
423+
424+ for name , field in self .attrs .items ():
425+ if ok (field ):
426+ yield name , field
427+
428+ if self .content and ok (self .content ):
429+ yield 'content' , self .content
430+
413431
414432 def items (
415- self , data : ParsedData , pred = Callable [[Field ], bool ]
433+ self , data : Data , pred : Callable [[Field ], bool ] | None = None
416434 ) -> Generator [tuple [str , Field , Value ]]:
417435 def ok (f : Field ) -> bool :
418436 return not pred or pred (f )
419437
420438 if self .name and ok (self .name ):
421439 yield 'name' , self .name , data .name
422440
423- if isinstance (self .attrs , Field ) and ok (self .attrs ):
424- for name , val in data .attrs .items ():
425- yield name , self .attrs , val
426- else :
427- for name , field in self .attrs .items ():
428- if not ok (field ):
429- continue
430- yield name , field , data .attrs .get (name )
441+ for name , field in self .attrs .items ():
442+ if not ok (field ):
443+ continue
444+ yield name , field , data .attrs .get (name )
431445
432446 if self .content and ok (self .content ):
433447 yield 'content' , self .content , data .content
0 commit comments