-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy path__init__.py
More file actions
344 lines (274 loc) · 11 KB
/
Copy path__init__.py
File metadata and controls
344 lines (274 loc) · 11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
"""
A Python interface for Discount, the C Markdown parser
This module contains ``libmarkdown``, a ctypes binding for Discount,
as well as ``Markdown``, a helper class built on top of this library.
Visit the Discount homepage:
http://www.pell.portland.or.us/~orc/Code/discount/
Basic usage examples:
>>> md = Markdown('`test`')
>>> md.get_html_content()
'<p><code>test</code></p>'
>>> md = Markdown(sys.stdin, autolink=True)
>>> md.write_html_content(sys.stdout)
See the ``Markdown`` docstrings for all keyword arguments, or the
docstrings for ``libmarkdown`` if you want to use the C functions
directly.
"""
import ctypes
from discount import libmarkdown
_KWARGS_TO_LIBMARKDOWN_FLAGS = {
'toc': libmarkdown.MKD_TOC,
'strict': libmarkdown.MKD_STRICT,
'autolink': libmarkdown.MKD_AUTOLINK,
'safelink': libmarkdown.MKD_SAFELINK,
'ignore_header': libmarkdown.MKD_NOHEADER,
'ignore_links': libmarkdown.MKD_NOLINKS,
'ignore_images': libmarkdown.MKD_NOIMAGE,
'ignore_tables': libmarkdown.MKD_NOTABLES,
'ignore_smartypants': libmarkdown.MKD_NOPANTS,
'ignore_embedded_html': libmarkdown.MKD_NOHTML,
'ignore_pseudo_protocols': libmarkdown.MKD_NO_EXT,
}
def add_html5_tags():
"""
Adds (globally, and non-removably) a handful of new tags for html5
support.
"""
libmarkdown.mkd_with_html5_tags()
def define_tag(tag, selfclose=False):
if selfclose:
_selfclose = 1
else:
_selfclose = 0
cp = ctypes.c_char_p(tag)
libmarkdown.mkd_define_tag(cp, _selfclose)
class MarkdownError(Exception):
"""
Exception raised when a discount c function
returns an error code, ``-1``.
"""
def __str__(self):
return '%s failure' % self.args[0]
class Markdown(object):
"""
Markdown to HTML conversion.
A single argument is required, ``input_file_or_string``, the
Markdown formatted data. If this argument is a file-like object,
the file must be a real OS file descriptor, i.e. ``sys.stdin``
yes, a ``StringIO`` object, no. The argument is otherwise assumed
to be a string-like object. The same is true for ``Markdown``
methods that write HTML output to files.
Optionally, you can specify two callback functions,
``rewrite_links_func`` and ``link_attrs_func``, which are hooks
when links are processed in the markdown document (See the
``rewrite_links()`` and ``link_attrs()`` methods).
Additional boolean keyword arguments are also accepted:
``toc`` : bool
Generate table-of-contents headers (each generated <h1>,
<h2>, etc will include a id="name" argument.) Use
``get_html_toc()`` or ``write_html_toc()`` to generate the
table-of-contents itself.
``strict``
Disable relaxed emphasis and superscripts.
``autolink``
Greedily expand links; if a url is encountered, convert it to
a hyperlink even if it isn't surrounded with ``<>s``.
``safelink``
Be paranoid about how ``[][]`` is expanded into a link - if the
url isn't a local reference, ``http://``, ``https://``, ``ftp://``,
or ``news://``, it will not be converted into a hyperlink.
``ignore_header``
Do not process the document header, but treat it like regular
text. See http://johnmacfarlane.net/pandoc/README.html#title-blocks
``ignore_links``
Do not allow ``<a`` or expand ``[][]`` into a link.
``ignore_images``
Do not allow ``<img`` or expand ``![][]`` into a image.
``ignore_tables``
Don't process PHP Markdown Extra tables. See
http://michelf.com/projects/php-markdown/extra/.
``ignore_smartypants``
Disable SmartyPants processing. See
http://daringfireball.net/projects/smartypants/.
``ignore_embedded_html``
Disable all embedded HTML by replacing all ``<``'s with ``<``.
``ignore_pseudo_protocols``
Do not process pseudo-protocols. See
http://www.pell.portland.or.us/~orc/Code/discount/#pseudo
Pandoc header elements can be retrieved with the methods
``get_pandoc_title()``, ``get_pandoc_author()`` and
``get_pandoc_date()``.
The converted HTML document parts can be retrieved as a string
with the ``get_html_css()``, ``get_html_toc()`` and
``get_html_content()`` methods, or written to a file with the
``write_html_css(fp)``, ``write_html_toc(fp)`` and
``write_html_content(fp)`` methods, where ``fp`` is the output
file descriptor.
"""
def __init__(
self, input_file_or_string,
rewrite_links_func=None, link_attrs_func=None,
**kwargs):
self.input = input_file_or_string
# Convert a ``kwargs`` dict to a bitmask of libmarkdown flags.
# All but one flag is exposed; MKD_1_COMPAT, which, according
# to the original documentation, is not really useful other
# than running MarkdownTest_1.0
flags = 0
for key in kwargs:
flags |= _KWARGS_TO_LIBMARKDOWN_FLAGS.get(key, 0)
self.flags = flags
if rewrite_links_func is not None:
self.rewrite_links(rewrite_links_func)
if link_attrs_func is not None:
self.link_attrs(link_attrs_func)
self._alloc = []
def __del__(self):
try:
libmarkdown.mkd_cleanup(self._doc)
except AttributeError:
pass
def _get_compiled_doc(self):
if not hasattr(self, '_doc'):
if hasattr(self.input, 'read'):
# If the input is file-like
input_ = ctypes.pythonapi.PyFile_AsFile(self.input)
self._doc = libmarkdown.mkd_in(input_, self.flags)
else:
# Otherwise, treat it as a string
input_ = ctypes.c_char_p(self.input)
self._doc = libmarkdown.mkd_string(
input_, len(self.input), self.flags)
ret = libmarkdown.mkd_compile(self._doc, self.flags)
if ret == -1:
raise MarkdownError('mkd_compile')
if hasattr(self, '_rewrite_links_func'):
libmarkdown.mkd_e_url(self._doc, self._rewrite_links_func)
if hasattr(self, '_link_attrs_func'):
libmarkdown.mkd_e_flags(self._doc, self._link_attrs_func)
return self._doc
def _generate_html_content(self, fp=None):
if fp is not None:
fp_ = ctypes.pythonapi.PyFile_AsFile(fp)
ret = libmarkdown.mkd_generatehtml(self._get_compiled_doc(), fp_)
if ret == -1:
raise MarkdownError('mkd_generatehtml')
else:
sb = ctypes.c_char_p('')
ln = libmarkdown.mkd_document(self._get_compiled_doc(), ctypes.byref(sb))
if ln == -1:
raise MarkdownError('mkd_document')
else:
return sb.value[:ln] if sb.value else ''
self._alloc = []
def _generate_html_toc(self, fp=None):
self.flags |= libmarkdown.MKD_TOC
if fp is not None:
fp_ = ctypes.pythonapi.PyFile_AsFile(fp)
ret = libmarkdown.mkd_generatetoc(self._get_compiled_doc(), fp_)
if ret == -1:
raise MarkdownError('mkd_generatetoc')
else:
sb = ctypes.c_char_p('')
ln = libmarkdown.mkd_toc(self._get_compiled_doc(), ctypes.byref(sb))
if ln == -1:
raise MarkdownError('mkd_toc')
else:
return sb.value[:ln] if sb.value else ''
self._alloc = []
def _generate_html_css(self, fp=None):
if fp is not None:
fp_ = ctypes.pythonapi.PyFile_AsFile(fp)
ret = libmarkdown.mkd_generatecss(self._get_compiled_doc(), fp_)
# Returns -1 even on success
# if ret == -1:
# raise MarkdownError('mkd_generatecss')
else:
sb = ctypes.c_char_p('')
ln = libmarkdown.mkd_css(self._get_compiled_doc(), ctypes.byref(sb))
if ln == -1:
raise MarkdownError('mkd_css')
else:
return sb.value[:ln] if sb.value else ''
self._alloc = []
def rewrite_links(self, func):
"""
Add a callback for rewriting links.
The callback should take a single argument, the url, and
should return a replacement url. The callback function is
called everytime a ``[]()`` or ``<link>`` is processed.
You can use this method as a decorator on the function you
want to set as the callback.
"""
@libmarkdown.e_url_callback
def _rewrite_links_func(string, size, context):
ret = func(string[:size])
if ret is not None:
buf = ctypes.create_string_buffer(ret)
self._alloc.append(buf)
return ctypes.addressof(buf)
self._rewrite_links_func = _rewrite_links_func
return func
def link_attrs(self, func):
"""
Add a callback for adding attributes to links.
The callback should take a single argument, the url, and
should return additional text to be inserted in the link tag,
i.e. ``"target="_blank"``.
You can use this method as a decorator on the function you
want to set as the callback.
"""
@libmarkdown.e_flags_callback
def _link_attrs_func(string, size, context):
ret = func(string[:size])
if ret is not None:
buf = ctypes.create_string_buffer(ret)
self._alloc.append(buf)
return ctypes.addressof(buf)
self._link_attrs_func = _link_attrs_func
return func
def get_pandoc_title(self):
"""
Get the document title from the pandoc header.
"""
return libmarkdown.mkd_doc_title(self._get_compiled_doc())
def get_pandoc_author(self):
"""
Get the document author(s) from the pandoc header.
"""
return libmarkdown.mkd_doc_author(self._get_compiled_doc())
def get_pandoc_date(self):
"""
Get the document date from the pandoc header.
"""
return libmarkdown.mkd_doc_date(self._get_compiled_doc())
def get_html_content(self):
"""
Get the document content as HTML.
"""
return self._generate_html_content()
def get_html_toc(self):
"""
Get the document's table of contents as HTML.
"""
return self._generate_html_toc()
def get_html_css(self):
"""
Get any style blocks in the document as HTML.
"""
return self._generate_html_css()
def write_html_content(self, fp):
"""
Write the document content to the file, ``fp``.
"""
self._generate_html_content(fp)
def write_html_toc(self, fp):
"""
Write the document's table of contents to the file, ``fp``.
"""
self._generate_html_toc(fp)
def write_html_css(self, fp):
"""
Write any style blocks in the document to the file, ``fp``.
"""
self._generate_html_css(fp)