Add a new parser API built on attrs
for defining classes instantiated from scanned stanzas
#52
Labels
attrs-parser
enhancement
New feature or request therefor
under consideration
Dev has not yet decided whether or how to implement
Milestone
A parser will be defined via a class decorated with
@parsable
. Header fields will be mapped to attributes of the class, with non-trivial mappings defined via field declarations of the formfieldname: Annotation = Field(...)
.Alternative idea: Replace
Field
withtyping.Annotated
à la Pydantic 2.0.Field
constructs anattr.Attribute
with headerparser-specific parameters stored in the attribute metadata under a "headerparser" key@parsable
compiles the class's parsing metadata into aParserSpec
instance that is then saved as a class variable, which is then used by the actualparse*()
functions.@parsable
can be passed the following arguments:name_decoder
— what the v1 parser calls the "normalizer"; defaults tolambda s: re.sub(r'[^\w_]', "_", s.lower())
scanner_options: dict[str, Any]
**kwargs
— passed toattr.define
Field
— For defining nontrivialmultiple=False
fieldsalias
decoder
— A callable that takes a header name (str
) and a value**kwargs
— passed toattr.field
MultiField
: For definingmultiple=True
fieldsField
, except thatdecoder
is passed a header name and a list of valuesExtraFields
: For defining an attribute to store additional fields withmultiple=False
ondecoder
— a callable that is passed a list of(name, value)
pairs with unique names**kwargs
— passed toattr.field
MultiExtraFields
is presentExtraFields
orMultiExtraFields
MultiExtraFields
: For defining an attribute to store additional fields withmultiple=True
ondecoder
— a callable that is passed a list of(name, value)
pairs in which the names need not be unique**kwargs
— passed toattr.field
BodyField
: For defining the attribute on which the body will be storeddecoder
— a callable that takes just a value**kwargs
— passed toattr.field
BodyField
is present in the classBodyField
Functions:
parse(klass: Type[L], data: Union[Iterable[str], str, Scanner]) -> L
parse_stanzas(klass: Type[L], data: Union[Iterable[str], str, Scanner]) -> Iterator[L]
parse_stream(klass: Type[L], fields: Iterable[Tuple[Optional[str], str]]) -> L
parse_stanzas_stream()
into the non-stream versions, as either way this function or an equivalent will be needed for the others to callparse_stanzas_stream(klass: Type[L], fields: Iterable[Iterable[Tuple[str, str]]]) -> Iterator[L]
parse_next_stanza()
; to get this effect, the user should scan the stanza themselves usingScanner
and pass the results toparse_stream()
parse_next_stanza()
exist but only take aScanner
?make_parsable(…)
— wrapsattr.make_class()
is_parsable(Any) -> bool
get_scanner()
?) for taking a parsable and returning aScanner
initialized with its scanner options?feed()
methodThere is a
ParserMixin
(?) mixin class that implements equivalents of all of theparse*()
functions as classmethods that get theklass
fromcls
Supply a premade set of decoders for parsing bools, timestamps, etc.?
Supply higher-order functions for converting single-argument functions to
(name, value)
decoders, converting(name, value)
decoders to(name, [value])
decoders, and converting single-argument functions to(name, [value])
decodersSupply one or more equivalents of attrs'
pipe()
et alii?Add an option for just discarding all extra/unknown fields?
The text was updated successfully, but these errors were encountered: