├── .gitignore
├── .travis.yml
├── LICENSE.txt
├── MANIFEST.in
├── README.md
├── bin
└── pymarkdown
├── examples
├── bokeh.html
├── bokeh.md
├── bokeh.rendered.md
├── images
│ └── 8734720408301.png
├── matplotlib.md
├── render.py
├── text.html
├── text.md
└── text.rendered.md
├── pymarkdown
├── __init__.py
├── compatibility.py
├── core.py
└── tests
│ ├── __init__.py
│ └── test_core.py
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | sudo: false
3 | python:
4 | - "2.6"
5 | - "2.7"
6 |
7 |
8 | install:
9 | # Install conda
10 | - wget http://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh
11 | - bash miniconda.sh -b -p $HOME/miniconda
12 | - export PATH="$HOME/miniconda/bin:$PATH"
13 | - conda config --set always_yes yes --set changeps1 no
14 | - conda update conda
15 |
16 | # Install dependencies
17 | - conda create -n test-environment python=$TRAVIS_PYTHON_VERSION
18 | - source activate test-environment
19 | - conda install pytest matplotlib bokeh toolz
20 |
21 | # Install pymarkdown
22 | - python setup.py install
23 |
24 | script:
25 | py.test --doctest-modules pymarkdown --verbose
26 |
27 | notifications:
28 | email: false
29 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015 Matthew Rocklin
2 |
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | a. Redistributions of source code must retain the above copyright notice,
9 | this list of conditions and the following disclaimer.
10 | b. Redistributions in binary form must reproduce the above copyright
11 | notice, this list of conditions and the following disclaimer in the
12 | documentation and/or other materials provided with the distribution.
13 | c. Neither the name of pymarkdown nor the names of its contributors
14 | may be used to endorse or promote products derived from this software
15 | without specific prior written permission.
16 |
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
28 | DAMAGE.
29 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | recursive-include pymarkdown *.py
2 | recursive-include docs *.rst
3 |
4 | include setup.py
5 | include README.rst
6 | include LICENSE
7 | include MANIFEST.in
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | PyMarkdown
2 | ==========
3 |
4 | *You should not use this project. You should use
5 | [`knitpy`](https://github.com/JanSchulz/knitpy).*
6 |
7 | Evaluate code in markdown.
8 |
9 | Why?
10 | ----
11 |
12 | Mostly because I was jealous of
13 | [RMarkdown/knitr](http://rmarkdown.rstudio.com/).
14 |
15 | The Jupyter notebook teaches us that interleaving prose, code, and results
16 | conveys meaning well. However when we author persistent content we often want a
17 | simple static text-based format. Markdown is good here because it plays well
18 | with other tools like `vi/emacs`, `pandoc`, and `git`.
19 |
20 | RMarkdown/knitr has demonstrated value in the R ecosystem, lets mimic that.
21 |
22 |
23 | How does this work?
24 | -------------------
25 |
26 | PyMarkdown leverages the `doctest` module to parse code into prose and code
27 | segments much like a docstring. It then executes each code segment
28 | sequentially with Python's `exec`, tracking state throughout the document,
29 | emitting or correcting results from computation where appropriate. For some
30 | outputs we use custom rendering, notably leveraging common protocols like
31 | `__repr_html__` and special casing plotting libraries.
32 |
33 | In simple cases both input and output documents are valid markdown appropriate
34 | for publication on github, your favorite blogging software, or with pandoc.
35 | For complex rendering we've specialized on emitting HTML-enhanced Markdown,
36 | which looks great in a browser but limits cross-markup-language compatibility
37 | (sorry LaTeX users).
38 |
39 |
40 | Example
41 | -------
42 |
43 | ### Input
44 |
45 | Our documents contain prose with *rich formatting*.
46 |
47 | ```Python
48 | # And code blocks
49 | >>> x = 1
50 | >>> x + 1
51 |
52 | >>> 2 + 2*x
53 | with potentially missing or wrong results
54 | ```
55 |
56 | We run pymarkdown and look at updated results:
57 |
58 | $ pymarkdown text.md text.out.md
59 |
60 | ### Output
61 |
62 | Our documents contain prose with *rich formatting*.
63 |
64 | ```Python
65 | # And code blocks
66 | >>> x = 1
67 | >>> x + 1
68 | 2
69 |
70 | >>> 2 + 2*x
71 | 4
72 | ```
73 |
74 | Fancy Support
75 | -------------
76 |
77 | ### HTML
78 |
79 | PyMarkdown leverages standard protocols like `to_html` or `__repr_html__`.
80 |
81 | ```python
82 | >>> import pandas as pd
83 | >>> df = pd.DataFrame({'name': ['Alice', 'Bob', 'Charlie'],
84 | ... 'balance': [100, 200, 300]})
85 | >>> df
86 | ```
87 |
88 | ### HTML
89 |
90 | PyMarkdown leverages standard protocols like `to_html` or `__repr_html__`.
91 |
92 | ```python
93 | >>> import pandas as pd
94 | >>> df = pd.DataFrame({'name': ['Alice', 'Bob', 'Charlie'],
95 | ... 'balance': [100, 200, 300]})
96 | >>> df
97 | ```
98 |
99 |
100 |
101 |
102 |
balance
103 |
name
104 |
105 |
106 |
107 |
108 |
0
109 |
100
110 |
Alice
111 |
112 |
113 |
1
114 |
200
115 |
Bob
116 |
117 |
118 |
2
119 |
300
120 |
Charlie
121 |
122 |
123 |
124 |
125 |
126 | Images
127 | ------
128 |
129 | PyMarkdown supports figure objects from both
130 | [Matplotlib](http://matplotlib.org/) and
131 | [Bokeh](http://bokeh.pydata.org/).
132 |
133 | Bokeh plots only work in browser but remain interactive. You must create a
134 | standalone HTML file, possibly with [Pandoc](http://johnmacfarlane.net/pandoc/)
135 |
136 | pymarkdown myfile.md myfile.out.md
137 | pandoc myfile.out.md -o myfile.html --standalone
138 |
139 | But that output doesn't look good in a README, so here we'll use matplotlib
140 |
141 | ```Python
142 | >>> import matplotlib.pyplot as plt
143 |
144 | >>> fig = plt.figure()
145 | >>> plt.plot([1, 2, 3, 4, 5], [6, 7, 2, 4, 5])
146 | >>> fig
147 | ```
148 |
149 | PyMarkdown supports figure objects from both
150 | [Matplotlib](http://matplotlib.org/) and
151 | [Bokeh](http://bokeh.pydata.org/).
152 |
153 | Bokeh plots only work in browser but remain interactive. You must create a
154 | standalone HTML file, possibly with [Pandoc](http://johnmacfarlane.net/pandoc/)
155 |
156 | pymarkdown myfile.md myfile.out.md
157 | pandoc myfile.out.md -o myfile.html --standalone
158 |
159 | But that output doesn't look good in a README, so here we'll use matplotlib
160 |
161 | ```Python
162 | >>> import matplotlib.pyplot as plt
163 |
164 | >>> fig = plt.figure()
165 | >>> plt.plot([1, 2, 3, 4, 5], [6, 7, 2, 4, 5])
166 | []
167 | >>> fig
168 | ```
169 | 
170 |
171 |
172 | Support
173 | -------
174 |
175 | There is none! This is a single-weekend project. Use at your own risk.
176 | Please contribute and take this project over.
177 |
178 | I've learned both that this isn't that hard and that it would be well
179 | appreciated by many people. If you have the attention span to read this far
180 | then I encourage you to extend or reinvent this project.
181 |
182 |
183 | TODO
184 | ----
185 |
186 | - [x] Interact with Bokeh plots. These already implement `__repr_html__` so
187 | this probably just means linking to some static content somewhere.
188 | - [x] Interact with matplotlib
189 | - [x] Better command line interface (should use something like `argparse` rather
190 | than `sys.argv`)
191 | - [ ] Support inlining of values in prose blocks
192 | - [ ] Support options like ignore, echo=False, etc..
193 | - [ ] Handle exceptions
194 | - [ ] Find a better name?
195 |
196 |
197 | Open Questions
198 | --------------
199 |
200 | * I specialized towards HTML because that's what I care about at the
201 | moment. This might not be a good approach long term though.
202 | * Do we want to integrate with pandoc? I tend to do something like the
203 | following
204 |
205 | pymarkdown myfile.md myfile.out.md && \
206 | pandoc myfile.out.md -o myfile.html --standalone
207 |
208 | But it would be nice for this to be easier to write in one go
209 |
210 | pymarkdown myfile.md -o myfile.html
211 |
212 | Have other thoughts? Great! Please implement them :)
213 |
--------------------------------------------------------------------------------
/bin/pymarkdown:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import argparse
4 | from pymarkdown import process
5 |
6 |
7 | def gen_parser():
8 | """
9 | Generates the argument parser
10 | """
11 | description = 'Evaluate code in markdown'
12 | parser = argparse.ArgumentParser(description=description)
13 |
14 | help = "Input md file"
15 | parser.add_argument('infile', type=str, help=help)
16 |
17 | help = "Output md file"
18 | parser.add_argument('output', type=str, help=help)
19 |
20 | return parser
21 |
22 |
23 | if __name__ == '__main__':
24 | parser = gen_parser()
25 | args = parser.parse_args()
26 |
27 | fn, outfn = args.infile, args.output
28 |
29 | with open(fn) as f:
30 | text = f.read()
31 |
32 | output = process(text)
33 |
34 | with open(outfn, 'w') as f:
35 | f.write(output)
36 |
--------------------------------------------------------------------------------
/examples/bokeh.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
28 |
29 |
30 |
31 |
32 |
33 |
We try out bokeh plots
34 |
>>> from bokeh.plotting import figure
35 | >>> x = [1, 2, 3, 4, 5]
36 | >>> y = [6, 7, 2, 4, 5]
37 |
38 |
39 | >>> p = figure(title="simple line example", x_axis_label='x', y_axis_label='y')
40 | >>> p.line(x, y, legend="Temp.", line_width=2)
41 |
42 |
43 |
Did that work?
44 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/examples/bokeh.md:
--------------------------------------------------------------------------------
1 | We try out bokeh plots
2 | ======================
3 |
4 | ```python
5 | >>> from bokeh.plotting import figure
6 | >>> x = [1, 2, 3, 4, 5]
7 | >>> y = [6, 7, 2, 4, 5]
8 |
9 | >>> p = figure(title="simple line example", x_axis_label='x', y_axis_label='y')
10 | >>> p.line(x, y, legend="Temp.", line_width=2)
11 | ```
12 |
13 | Did that work?
14 |
--------------------------------------------------------------------------------
/examples/bokeh.rendered.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We try out bokeh plots
5 | ======================
6 |
7 | ```python
8 | >>> from bokeh.plotting import figure
9 | >>> x = [1, 2, 3, 4, 5]
10 | >>> y = [6, 7, 2, 4, 5]
11 |
12 |
13 | >>> p = figure(title="simple line example", x_axis_label='x', y_axis_label='y')
14 | >>> p.line(x, y, legend="Temp.", line_width=2)
15 | ```
16 |
17 | ```python
18 | ```
19 |
20 | Did that work?
21 |
22 |
23 |
--------------------------------------------------------------------------------
/examples/images/8734720408301.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrocklin/pymarkdown/22ad745e3dd55c5f3b968d310376833020657668/examples/images/8734720408301.png
--------------------------------------------------------------------------------
/examples/matplotlib.md:
--------------------------------------------------------------------------------
1 | Matplotlib
2 | ==========
3 |
4 | We save matplotlib figures in a local `images/` directory and then link to them
5 | from the markdown file.
6 |
7 | ```python
8 | >>> import matplotlib.pyplot as plt
9 |
10 | >>> fig = plt.figure()
11 | >>> plt.plot([1, 2, 3, 4, 5], [6, 7, 2, 4, 5])
12 | >>> fig
13 | ```
14 |
--------------------------------------------------------------------------------
/examples/render.py:
--------------------------------------------------------------------------------
1 | from glob import glob
2 | from pymarkdown import process
3 | import os
4 |
5 | fns = sorted([fn for fn in glob('examples/*.md')
6 | if 'rendered' not in fn])
7 |
8 | for fn in fns:
9 | with open(fn) as f:
10 | text = f.read()
11 |
12 | processed = process(text)
13 |
14 | rendered_fn = fn.rsplit('.', 1)[0] + '.rendered.md'
15 | with open(rendered_fn, 'w') as f:
16 | f.write(processed)
17 |
18 | html_fn = fn.rsplit('.', 1)[0] + '.html'
19 | os.popen('pandoc %s -o %s --standalone' % (rendered_fn, html_fn)).read()
20 |
--------------------------------------------------------------------------------
/examples/text.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
28 |
29 |
30 |
Title
31 |
Some prose
32 |
# And some code
33 |
34 | >>> x = 1
35 | >>> x + 1
36 | 2
37 |
38 |
39 | >>> 2 + 2*x
40 | 4