Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/markdown_it/token.py: 54%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

69 statements  

1from __future__ import annotations 

2 

3from collections.abc import Callable, MutableMapping 

4import dataclasses as dc 

5from typing import Any, Literal 

6import warnings 

7 

8from markdown_it._compat import DATACLASS_KWARGS 

9 

10 

11def convert_attrs(value: Any) -> Any: 

12 """Convert Token.attrs set as ``None`` or ``[[key, value], ...]`` to a dict. 

13 

14 This improves compatibility with upstream markdown-it. 

15 """ 

16 if not value: 

17 return {} 

18 if isinstance(value, list): 

19 return dict(value) 

20 return value 

21 

22 

23@dc.dataclass(**DATACLASS_KWARGS) 

24class Token: 

25 type: str 

26 """Type of the token (string, e.g. "paragraph_open")""" 

27 

28 tag: str 

29 """HTML tag name, e.g. 'p'""" 

30 

31 nesting: Literal[-1, 0, 1] 

32 """Level change (number in {-1, 0, 1} set), where: 

33 - `1` means the tag is opening 

34 - `0` means the tag is self-closing 

35 - `-1` means the tag is closing 

36 """ 

37 

38 attrs: dict[str, str | int | float] = dc.field(default_factory=dict) 

39 """HTML attributes. 

40 Note this differs from the upstream "list of lists" format, 

41 although than an instance can still be initialised with this format. 

42 """ 

43 

44 map: list[int] | None = None 

45 """Source map info. Format: `[ line_begin, line_end ]`""" 

46 

47 level: int = 0 

48 """Nesting level, the same as `state.level`""" 

49 

50 children: list[Token] | None = None 

51 """Array of child nodes (inline and img tokens).""" 

52 

53 content: str = "" 

54 """Inner content, in the case of a self-closing tag (code, html, fence, etc.),""" 

55 

56 markup: str = "" 

57 """'*' or '_' for emphasis, fence string for fence, etc.""" 

58 

59 info: str = "" 

60 """Additional information: 

61 - Info string for "fence" tokens 

62 - The value "auto" for autolink "link_open" and "link_close" tokens 

63 - The string value of the item marker for ordered-list "list_item_open" tokens 

64 """ 

65 

66 meta: dict[Any, Any] = dc.field(default_factory=dict) 

67 """A place for plugins to store any arbitrary data""" 

68 

69 block: bool = False 

70 """True for block-level tokens, false for inline tokens. 

71 Used in renderer to calculate line breaks 

72 """ 

73 

74 hidden: bool = False 

75 """If true, ignore this element when rendering. 

76 Used for tight lists to hide paragraphs. 

77 """ 

78 

79 def __post_init__(self) -> None: 

80 self.attrs = convert_attrs(self.attrs) 

81 

82 def attrIndex(self, name: str) -> int: 

83 warnings.warn( # noqa: B028 

84 "Token.attrIndex should not be used, since Token.attrs is a dictionary", 

85 UserWarning, 

86 ) 

87 if name not in self.attrs: 

88 return -1 

89 return list(self.attrs.keys()).index(name) 

90 

91 def attrItems(self) -> list[tuple[str, str | int | float]]: 

92 """Get (key, value) list of attrs.""" 

93 return list(self.attrs.items()) 

94 

95 def attrPush(self, attrData: tuple[str, str | int | float]) -> None: 

96 """Add `[ name, value ]` attribute to list. Init attrs if necessary.""" 

97 name, value = attrData 

98 self.attrSet(name, value) 

99 

100 def attrSet(self, name: str, value: str | int | float) -> None: 

101 """Set `name` attribute to `value`. Override old value if exists.""" 

102 self.attrs[name] = value 

103 

104 def attrGet(self, name: str) -> None | str | int | float: 

105 """Get the value of attribute `name`, or null if it does not exist.""" 

106 return self.attrs.get(name, None) 

107 

108 def attrJoin(self, name: str, value: str) -> None: 

109 """Join value to existing attribute via space. 

110 Or create new attribute if not exists. 

111 Useful to operate with token classes. 

112 """ 

113 if name in self.attrs: 

114 current = self.attrs[name] 

115 if not isinstance(current, str): 

116 raise TypeError( 

117 f"existing attr 'name' is not a str: {self.attrs[name]}" 

118 ) 

119 self.attrs[name] = f"{current} {value}" 

120 else: 

121 self.attrs[name] = value 

122 

123 def copy(self, **changes: Any) -> Token: 

124 """Return a shallow copy of the instance.""" 

125 return dc.replace(self, **changes) 

126 

127 def as_dict( 

128 self, 

129 *, 

130 children: bool = True, 

131 as_upstream: bool = True, 

132 meta_serializer: Callable[[dict[Any, Any]], Any] | None = None, 

133 filter: Callable[[str, Any], bool] | None = None, 

134 dict_factory: Callable[..., MutableMapping[str, Any]] = dict, 

135 ) -> MutableMapping[str, Any]: 

136 """Return the token as a dictionary. 

137 

138 :param children: Also convert children to dicts 

139 :param as_upstream: Ensure the output dictionary is equal to that created by markdown-it 

140 For example, attrs are converted to null or lists 

141 :param meta_serializer: hook for serializing ``Token.meta`` 

142 :param filter: A callable whose return code determines whether an 

143 attribute or element is included (``True``) or dropped (``False``). 

144 Is called with the (key, value) pair. 

145 :param dict_factory: A callable to produce dictionaries from. 

146 For example, to produce ordered dictionaries instead of normal Python 

147 dictionaries, pass in ``collections.OrderedDict``. 

148 

149 """ 

150 mapping = dict_factory((f.name, getattr(self, f.name)) for f in dc.fields(self)) 

151 if filter: 

152 mapping = dict_factory((k, v) for k, v in mapping.items() if filter(k, v)) 

153 if as_upstream and "attrs" in mapping: 

154 mapping["attrs"] = ( 

155 None 

156 if not mapping["attrs"] 

157 else [[k, v] for k, v in mapping["attrs"].items()] 

158 ) 

159 if meta_serializer and "meta" in mapping: 

160 mapping["meta"] = meta_serializer(mapping["meta"]) 

161 if children and mapping.get("children", None): 

162 mapping["children"] = [ 

163 child.as_dict( 

164 children=children, 

165 filter=filter, 

166 dict_factory=dict_factory, 

167 as_upstream=as_upstream, 

168 meta_serializer=meta_serializer, 

169 ) 

170 for child in mapping["children"] 

171 ] 

172 return mapping 

173 

174 @classmethod 

175 def from_dict(cls, dct: MutableMapping[str, Any]) -> Token: 

176 """Convert a dict to a Token.""" 

177 token = cls(**dct) 

178 if token.children: 

179 token.children = [cls.from_dict(c) for c in token.children] # type: ignore[arg-type] 

180 return token