forked from lethain/python-markdown-graphviz
-
Notifications
You must be signed in to change notification settings - Fork 3
/
mdx_graphviz.py
144 lines (111 loc) · 4.19 KB
/
mdx_graphviz.py
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
"""
### Markdown-Python-Graphviz
This module is an extention to [Python-Markdown][pymd] which makes it
possible to embed [Graphviz][gv] syntax into Markdown documents.
### Requirements
Using this module requires:
* Python-Markdown
* Graphviz (particularly ``dot``)
### Syntax
Wrap Graphviz definitions within a dot/neato/dotty/lefty tag.
An example document:
This is some text above a graph.
<dot>
digraph a {
nodesep=1.0;
rankdir=LR;
a -> b -> c ->d;
}
</dot>
Some other text between two graphs.
<neato>
some graph in neato...
</neato>
This is also some text below a graph.
Note that the opening and closing tags should come at the beginning of
their lines and should be immediately followed by a newline.
### Usage
import markdown
md = markdown.Markdown(
extensions=['graphviz'],
extension_configs={'graphviz' : {'DOT','/usr/bin/dot'}}
)
return md.convert(some_text)
[pymd]: http://www.freewisdom.org/projects/python-markdown/ "Python-Markdown"
[gv]: http://www.graphviz.org/ "Graphviz"
"""
from os.path import exists
import re
import subprocess
import crcmod.predefined
import markdown
import markdown.preprocessors
class GraphvizExtension(markdown.Extension):
def __init__(self, configs):
self.config = {'BINARY_PATH': "", 'WRITE_IMGS_DIR': "",
"BASE_IMG_LINK_DIR": "", "FORMAT": "png"}
for key, value in configs:
self.config[key] = value
def reset(self):
pass
def extendMarkdown(self, md, md_globals):
"""Add GraphvizExtension to the Markdown instance."""
md.registerExtension(self)
self.parser = md.parser
md.preprocessors.add('graphviz', GraphvizPreprocessor(self), '_begin')
class GraphvizPreprocessor(markdown.preprocessors.Preprocessor):
"""Find all graphviz blocks, generate images and inject image link to
generated images.
"""
def __init__(self, graphviz):
self.formatters = ["dot", "neato", "lefty", "dotty"]
self.graphviz = graphviz
self.start_re = re.compile(r'^<(%s)>' % '|'.join(self.formatters))
self.end_re = re.compile(r'^</(%s)>' % '|'.join(self.formatters))
self.crc64 = lambda x: crcmod.predefined.mkCrcFun('crc-64')(x)
def run(self, lines):
new_lines = []
block = []
in_block = None
for line in lines:
start_tag = self.start_re.match(line)
end_tag = self.end_re.match(line)
if start_tag:
assert(block == [])
in_block = start_tag.group(1)
elif end_tag:
new_lines.append(self.graph(in_block, block))
block = []
in_block = None
elif in_block in self.formatters:
block.append(line)
else:
new_lines.append(line)
assert(block == [])
return new_lines
def graph(self, type, lines):
"""Generates a graph from lines and returns a string containing n image
link to created graph."""
code = "\n".join(lines)
name = self.crc64(code)
assert(type in self.formatters)
filepath = "%s%s.%s" % (self.graphviz.config["WRITE_IMGS_DIR"],
name, self.graphviz.config["FORMAT"])
if not exists(filepath):
cmd = "%s%s -T%s" % (self.graphviz.config["BINARY_PATH"],
type,
self.graphviz.config["FORMAT"])
p = subprocess.Popen(cmd, shell=True,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE, close_fds=True)
p.stdin.write(code)
p.stdin.close()
p.wait()
with open(filepath, 'w') as fout:
fout.write(p.stdout.read())
output_path = "%s%s.%s" % (self.graphviz.config["BASE_IMG_LINK_DIR"],
name,
self.graphviz.config["FORMAT"])
return "![Graphviz chart %s](%s)" % (name, output_path)
def makeExtension(configs=None):
return GraphvizExtension(configs=configs)