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
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-01 06:54 +0000
1import re
2from typing import Dict, Any
4_LINE_END = re.compile(r'\n|$')
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 = []
13 # current cursor position
14 self.cursor = 0
15 self.cursor_max = 0
17 # for list and block quote chain
18 self.list_tight = True
19 self.parent = parent
21 # for saving def references
22 if parent:
23 self.env = parent.env
24 else:
25 self.env = {'ref_links': {}}
27 def child_state(self, src: str):
28 child = self.__class__(self)
29 child.process(src)
30 return child
32 def process(self, src: str):
33 self.src = src
34 self.cursor_max = len(src)
36 def find_line_end(self):
37 m = _LINE_END.search(self.src, self.cursor)
38 return m.end()
40 def get_text(self, end_pos: int):
41 return self.src[self.cursor:end_pos]
43 def last_token(self):
44 if self.tokens:
45 return self.tokens[-1]
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)
51 def append_token(self, token: Dict[str, Any]):
52 """Add token to the end of token list."""
53 self.tokens.append(token)
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})
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
69 def depth(self):
70 d = 0
71 parent = self.parent
72 while parent:
73 d += 1
74 parent = parent.parent
75 return d
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
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)
93 def append_token(self, token: Dict[str, Any]):
94 """Add token to the end of token list."""
95 self.tokens.append(token)
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
107class Parser:
108 sc_flag = re.M
109 state_cls = BlockState
111 SPECIFICATION = {}
112 DEFAULT_RULES = []
114 def __init__(self):
115 self.specification = self.SPECIFICATION.copy()
116 self.rules = list(self.DEFAULT_RULES)
117 self._methods = {}
119 self.__sc = {}
121 def compile_sc(self, rules=None):
122 if rules is None:
123 key = '$'
124 rules = self.rules
125 else:
126 key = '|'.join(rules)
128 sc = self.__sc.get(key)
129 if sc:
130 return sc
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
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.
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)
152 def register_rule(self, name, pattern, func):
153 raise DeprecationWarning('This plugin is not compatible with mistune v3.')
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)
166 def parse_method(self, m, state):
167 func = self._methods[m.lastgroup]
168 return func(m, state)
171class BaseRenderer(object):
172 NAME = 'base'
174 def __init__(self):
175 self.__methods = {}
177 def register(self, name: str, method):
178 """Register a render method for the named token. For example::
180 def render_wiki(renderer, key, title):
181 return f'<a href="/wiki/{key}">{title}</a>'
183 renderer.register('wiki', render_wiki)
184 """
185 # bind self into renderer method
186 self.__methods[name] = lambda *arg, **kwargs: method(self, *arg, **kwargs)
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
197 def render_token(self, token, state):
198 func = self._get_method(token['type'])
199 return func(token, state)
201 def iter_tokens(self, tokens, state):
202 for tok in tokens:
203 yield self.render_token(tok, state)
205 def render_tokens(self, tokens, state):
206 return ''.join(self.iter_tokens(tokens, state))
208 def __call__(self, tokens, state):
209 return self.render_tokens(tokens, state)