Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/mistune/core.py: 96%

137 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-07-01 06:54 +0000

1import re 

2from typing import Dict, Any 

3 

4_LINE_END = re.compile(r'\n|$') 

5 

6 

7class BlockState: 

8 """The state to save block parser's cursor and tokens.""" 

9 def __init__(self, parent=None): 

10 self.src = '' 

11 self.tokens = [] 

12 

13 # current cursor position 

14 self.cursor = 0 

15 self.cursor_max = 0 

16 

17 # for list and block quote chain 

18 self.list_tight = True 

19 self.parent = parent 

20 

21 # for saving def references 

22 if parent: 

23 self.env = parent.env 

24 else: 

25 self.env = {'ref_links': {}} 

26 

27 def child_state(self, src: str): 

28 child = self.__class__(self) 

29 child.process(src) 

30 return child 

31 

32 def process(self, src: str): 

33 self.src = src 

34 self.cursor_max = len(src) 

35 

36 def find_line_end(self): 

37 m = _LINE_END.search(self.src, self.cursor) 

38 return m.end() 

39 

40 def get_text(self, end_pos: int): 

41 return self.src[self.cursor:end_pos] 

42 

43 def last_token(self): 

44 if self.tokens: 

45 return self.tokens[-1] 

46 

47 def prepend_token(self, token: Dict[str, Any]): 

48 """Insert token before the last token.""" 

49 self.tokens.insert(len(self.tokens) - 1, token) 

50 

51 def append_token(self, token: Dict[str, Any]): 

52 """Add token to the end of token list.""" 

53 self.tokens.append(token) 

54 

55 def add_paragraph(self, text: str): 

56 last_token = self.last_token() 

57 if last_token and last_token['type'] == 'paragraph': 

58 last_token['text'] += text 

59 else: 

60 self.tokens.append({'type': 'paragraph', 'text': text}) 

61 

62 def append_paragraph(self): 

63 last_token = self.last_token() 

64 if last_token and last_token['type'] == 'paragraph': 

65 pos = self.find_line_end() 

66 last_token['text'] += self.get_text(pos) 

67 return pos 

68 

69 def depth(self): 

70 d = 0 

71 parent = self.parent 

72 while parent: 

73 d += 1 

74 parent = parent.parent 

75 return d 

76 

77 

78class InlineState: 

79 """The state to save inline parser's tokens.""" 

80 def __init__(self, env: Dict[str, Any]): 

81 self.env = env 

82 self.src = '' 

83 self.tokens = [] 

84 self.in_image = False 

85 self.in_link = False 

86 self.in_emphasis = False 

87 self.in_strong = False 

88 

89 def prepend_token(self, token: Dict[str, Any]): 

90 """Insert token before the last token.""" 

91 self.tokens.insert(len(self.tokens) - 1, token) 

92 

93 def append_token(self, token: Dict[str, Any]): 

94 """Add token to the end of token list.""" 

95 self.tokens.append(token) 

96 

97 def copy(self): 

98 """Create a copy of current state.""" 

99 state = self.__class__(self.env) 

100 state.in_image = self.in_image 

101 state.in_link = self.in_link 

102 state.in_emphasis = self.in_emphasis 

103 state.in_strong = self.in_strong 

104 return state 

105 

106 

107class Parser: 

108 sc_flag = re.M 

109 state_cls = BlockState 

110 

111 SPECIFICATION = {} 

112 DEFAULT_RULES = [] 

113 

114 def __init__(self): 

115 self.specification = self.SPECIFICATION.copy() 

116 self.rules = list(self.DEFAULT_RULES) 

117 self._methods = {} 

118 

119 self.__sc = {} 

120 

121 def compile_sc(self, rules=None): 

122 if rules is None: 

123 key = '$' 

124 rules = self.rules 

125 else: 

126 key = '|'.join(rules) 

127 

128 sc = self.__sc.get(key) 

129 if sc: 

130 return sc 

131 

132 regex = '|'.join(r'(?P<%s>%s)' % (k, self.specification[k]) for k in rules) 

133 sc = re.compile(regex, self.sc_flag) 

134 self.__sc[key] = sc 

135 return sc 

136 

137 def register(self, name: str, pattern, func, before=None): 

138 """Register a new rule to parse the token. This method is usually used to 

139 create a new plugin. 

140 

141 :param name: name of the new grammar 

142 :param pattern: regex pattern in string 

143 :param func: the parsing function 

144 :param before: insert this rule before a built-in rule 

145 """ 

146 self._methods[name] = lambda m, state: func(self, m, state) 

147 if pattern: 

148 self.specification[name] = pattern 

149 if name not in self.rules: 

150 self.insert_rule(self.rules, name, before=before) 

151 

152 def register_rule(self, name, pattern, func): 

153 raise DeprecationWarning('This plugin is not compatible with mistune v3.') 

154 

155 @staticmethod 

156 def insert_rule(rules, name, before=None): 

157 if before: 

158 try: 

159 index = rules.index(before) 

160 rules.insert(index, name) 

161 except ValueError: 

162 rules.append(name) 

163 else: 

164 rules.append(name) 

165 

166 def parse_method(self, m, state): 

167 func = self._methods[m.lastgroup] 

168 return func(m, state) 

169 

170 

171class BaseRenderer(object): 

172 NAME = 'base' 

173 

174 def __init__(self): 

175 self.__methods = {} 

176 

177 def register(self, name: str, method): 

178 """Register a render method for the named token. For example:: 

179 

180 def render_wiki(renderer, key, title): 

181 return f'<a href="/wiki/{key}">{title}</a>' 

182 

183 renderer.register('wiki', render_wiki) 

184 """ 

185 # bind self into renderer method 

186 self.__methods[name] = lambda *arg, **kwargs: method(self, *arg, **kwargs) 

187 

188 def _get_method(self, name): 

189 try: 

190 return object.__getattribute__(self, name) 

191 except AttributeError: 

192 method = self.__methods.get(name) 

193 if not method: 

194 raise AttributeError('No renderer "{!r}"'.format(name)) 

195 return method 

196 

197 def render_token(self, token, state): 

198 func = self._get_method(token['type']) 

199 return func(token, state) 

200 

201 def iter_tokens(self, tokens, state): 

202 for tok in tokens: 

203 yield self.render_token(tok, state) 

204 

205 def render_tokens(self, tokens, state): 

206 return ''.join(self.iter_tokens(tokens, state)) 

207 

208 def __call__(self, tokens, state): 

209 return self.render_tokens(tokens, state)