11from __future__ import annotations
22
33from collections .abc import Callable , MutableMapping
4+ import dataclasses as dc
45from typing import Any
56import warnings
67
7- import attr
8-
98
109def convert_attrs (value : Any ) -> Any :
1110 """Convert Token.attrs set as ``None`` or ``[[key, value], ...]`` to a dict.
@@ -19,43 +18,65 @@ def convert_attrs(value: Any) -> Any:
1918 return value
2019
2120
22- @attr . s ( slots = True )
21+ @dc . dataclass ( )
2322class Token :
24- # Type of the token (string, e.g. "paragraph_open")
25- type : str = attr .ib ()
26- # html tag name, e.g. "p"
27- tag : str = attr .ib ()
28- # Level change (number in {-1, 0, 1} set), where:
29- # - `1` means the tag is opening
30- # - `0` means the tag is self-closing
31- # - `-1` means the tag is closing
32- nesting : int = attr .ib ()
33- # Html attributes. Note this differs from the upstream "list of lists" format
34- attrs : dict [str , str | int | float ] = attr .ib (factory = dict , converter = convert_attrs )
35- # Source map info. Format: `[ line_begin, line_end ]`
36- map : list [int ] | None = attr .ib (default = None )
37- # nesting level, the same as `state.level`
38- level : int = attr .ib (default = 0 )
39- # An array of child nodes (inline and img tokens)
40- children : list [Token ] | None = attr .ib (default = None )
41- # In a case of self-closing tag (code, html, fence, etc.),
42- # it has contents of this tag.
43- content : str = attr .ib (default = "" )
44- # '*' or '_' for emphasis, fence string for fence, etc.
45- markup : str = attr .ib (default = "" )
46- # Additional information:
47- # - Info string for "fence" tokens
48- # - The value "auto" for autolink "link_open" and "link_close" tokens
49- # - The string value of the item marker for ordered-list "list_item_open" tokens
50- info : str = attr .ib (default = "" )
51- # A place for plugins to store any arbitrary data
52- meta : dict = attr .ib (factory = dict )
53- # True for block-level tokens, false for inline tokens.
54- # Used in renderer to calculate line breaks
55- block : bool = attr .ib (default = False )
56- # If it's true, ignore this element when rendering.
57- # Used for tight lists to hide paragraphs.
58- hidden : bool = attr .ib (default = False )
23+
24+ type : str
25+ """Type of the token (string, e.g. "paragraph_open")"""
26+
27+ tag : str
28+ """HTML tag name, e.g. 'p'"""
29+
30+ nesting : int
31+ """Level change (number in {-1, 0, 1} set), where:
32+ - `1` means the tag is opening
33+ - `0` means the tag is self-closing
34+ - `-1` means the tag is closing
35+ """
36+
37+ attrs : dict [str , str | int | float ] = dc .field (default_factory = dict )
38+ """HTML attributes.
39+ Note this differs from the upstream "list of lists" format,
40+ although than an instance can still be initialised with this format.
41+ """
42+
43+ map : list [int ] | None = None
44+ """Source map info. Format: `[ line_begin, line_end ]`"""
45+
46+ level : int = 0
47+ """Nesting level, the same as `state.level`"""
48+
49+ children : list [Token ] | None = None
50+ """Array of child nodes (inline and img tokens)."""
51+
52+ content : str = ""
53+ """Inner content, in the case of a self-closing tag (code, html, fence, etc.),"""
54+
55+ markup : str = ""
56+ """'*' or '_' for emphasis, fence string for fence, etc."""
57+
58+ info : str = ""
59+ """Additional information:
60+ - Info string for "fence" tokens
61+ - The value "auto" for autolink "link_open" and "link_close" tokens
62+ - The string value of the item marker for ordered-list "list_item_open" tokens
63+ """
64+
65+ meta : dict = dc .field (default_factory = dict )
66+ """A place for plugins to store any arbitrary data"""
67+
68+ block : bool = False
69+ """True for block-level tokens, false for inline tokens.
70+ Used in renderer to calculate line breaks
71+ """
72+
73+ hidden : bool = False
74+ """If true, ignore this element when rendering.
75+ Used for tight lists to hide paragraphs.
76+ """
77+
78+ def __post_init__ (self ):
79+ self .attrs = convert_attrs (self .attrs )
5980
6081 def attrIndex (self , name : str ) -> int :
6182 warnings .warn (
@@ -98,17 +119,17 @@ def attrJoin(self, name: str, value: str) -> None:
98119 else :
99120 self .attrs [name ] = value
100121
101- def copy (self ) -> Token :
122+ def copy (self , ** changes : Any ) -> Token :
102123 """Return a shallow copy of the instance."""
103- return attr . evolve (self )
124+ return dc . replace (self , ** changes )
104125
105126 def as_dict (
106127 self ,
107128 * ,
108129 children : bool = True ,
109130 as_upstream : bool = True ,
110131 meta_serializer : Callable [[dict ], Any ] | None = None ,
111- filter : Callable [[attr . Attribute , Any ], bool ] | None = None ,
132+ filter : Callable [[str , Any ], bool ] | None = None ,
112133 dict_factory : Callable [..., MutableMapping [str , Any ]] = dict ,
113134 ) -> MutableMapping [str , Any ]:
114135 """Return the token as a dictionary.
@@ -119,16 +140,15 @@ def as_dict(
119140 :param meta_serializer: hook for serializing ``Token.meta``
120141 :param filter: A callable whose return code determines whether an
121142 attribute or element is included (``True``) or dropped (``False``).
122- Is called with the `attr.Attribute` as the first argument and the
123- value as the second argument.
143+ Is called with the (key, value) pair.
124144 :param dict_factory: A callable to produce dictionaries from.
125145 For example, to produce ordered dictionaries instead of normal Python
126146 dictionaries, pass in ``collections.OrderedDict``.
127147
128148 """
129- mapping = attr . asdict (
130- self , recurse = False , filter = filter , dict_factory = dict_factory # type: ignore[arg-type]
131- )
149+ mapping = dict_factory (( f . name , getattr ( self , f . name )) for f in dc . fields ( self ))
150+ if filter :
151+ mapping = dict_factory (( k , v ) for k , v in mapping . items () if filter ( k , v ) )
132152 if as_upstream and "attrs" in mapping :
133153 mapping ["attrs" ] = (
134154 None
0 commit comments