11 |
12 | {% include 'content.html' %}
13 |
14 |
15 |
--------------------------------------------------------------------------------
/literate/templates/content.html:
--------------------------------------------------------------------------------
1 | {% for block in blocks %}
2 |
3 |
4 | {% for chunk in block.prose %}
5 | {{ chunk.html }}
6 | {% endfor %}
7 |
8 |
9 | {% for chunk in block.code %}
10 |
11 |
{{ chunk.text }}
12 |
13 | {% if chunk.output %}
14 |
15 |
{{ chunk.output }}
16 |
17 | {% endif %}
18 | {% endfor %}
19 |
20 |
21 | {% endfor %}
--------------------------------------------------------------------------------
/examples/basic/example.pylit:
--------------------------------------------------------------------------------
1 | # Literate Python example
2 |
3 | This is a Literate Python document.
4 |
5 |
6 | Non-indented text is regular **Markdown**,
7 | but indented text is Python. For example:
8 |
9 | l = [1, 2, 3]
10 | print [float(el*3) for el in l]
11 | x = l[0]
12 |
13 | And then we can write more.
14 |
15 | And more still.
16 |
17 | print "But continue with some Python"
18 |
19 | v = " ".join(['If we', 'care to'])
20 | print v
21 |
22 |
23 | print "And two lines if we want to split up contiguous Python samples"
24 |
25 | We can also include variables, like `x` (valued **{x}**) from a couple of blocks back.
26 |
27 | If it can be stringified, we'll print it: {l}
28 |
29 | And _that's_ what Literate Python is all about. It's a really nice tool for
30 | data analysis, scientific notebooks, documentation and what-not.
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (C) 2013 by Stijn Debrouwere
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/literate/core/utils.py:
--------------------------------------------------------------------------------
1 | import os
2 | import shutil
3 | import re
4 | from copy import copy
5 |
6 | def line(string, **kwargs):
7 | string = string.format(**kwargs)
8 | string = re.sub(r'\n+', ' ', string)
9 | return re.sub(r'\s{2,}', ' ', string)
10 |
11 | def here(*segments):
12 | current = os.path.dirname(__file__)
13 | return os.path.abspath(os.path.join(current, '..', *segments))
14 |
15 | def find(location):
16 | if os.path.isfile(location):
17 | return [location]
18 | elif os.path.isdir(location):
19 | ls = []
20 | for root, dirs, files in os.walk('./'):
21 | ls.extend([os.path.join(root, file) for file in files])
22 | return [file for file in ls if os.path.splitext(file)[1] in loader.EXTENSIONS]
23 | else:
24 | raise ValueError("Need a directory or a file.")
25 |
26 | def out(output, dest, stdout):
27 | if stdout:
28 | print output
29 | else:
30 | try:
31 | os.makedirs(os.path.dirname(dest))
32 | except OSError:
33 | pass
34 |
35 | with open(dest, 'w') as f:
36 | f.write(output)
--------------------------------------------------------------------------------
/literate/core/templates.py:
--------------------------------------------------------------------------------
1 | import jinja2
2 | import utils
3 | import parser
4 |
5 | loader = jinja2.FileSystemLoader(utils.here('templates'))
6 | environment = jinja2.Environment(loader=loader)
7 |
8 | def content(blocks):
9 | template = environment.get_template('content.html')
10 | return template.render(blocks=blocks)
11 |
12 | def document(title, blocks, **options):
13 | template = environment.get_template('document.html')
14 | return template.render(title=title, blocks=blocks, options=options)
15 |
16 | def pipe():
17 | raise NotImplementedError()
18 |
19 | # REFACTOR: old code, but I want to offer similar functionality
20 | # to provide a very easy way to allow for custom templating
21 | def replace(src, body):
22 | assets = None
23 |
24 | if os.path.isfile(src):
25 | template = open(src).read()
26 | else:
27 | template = open(os.path.join(src, 'template.html')).read()
28 | assets_dir = os.path.join(src, 'assets')
29 |
30 | if os.path.exists(assets_dir):
31 | assets = assets_dir
32 |
33 | output = template.replace('', body)
34 |
35 | return (output, assets)
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import os
2 | from setuptools import setup, find_packages
3 | from literate import VERSION
4 |
5 |
6 | f = open(os.path.join(os.path.dirname(__file__), 'README.md'))
7 | readme = f.read()
8 | f.close()
9 |
10 | setup(
11 | name='python-literate',
12 | version=".".join(map(str, VERSION)),
13 | description='A wedding of literate programming and IPython notebooks. Create Markdown/HTML notebooks that include documentation, code and the output of that code.',
14 | long_description=readme,
15 | author='Stijn Debrouwere',
16 | author_email='stijn@stdout.be',
17 | url='http://github.com/stdbrouw/python-literate/tree/master',
18 | packages=find_packages(),
19 | zip_safe=False,
20 | classifiers=[
21 | 'Development Status :: 4 - Beta',
22 | 'Intended Audience :: Developers',
23 | 'License :: OSI Approved :: MIT License',
24 | 'Operating System :: OS Independent',
25 | 'Programming Language :: Python',
26 | ],
27 | entry_points = {
28 | 'console_scripts': [
29 | 'literate = literate.commands:initialize',
30 | ],
31 | },
32 | )
33 |
34 |
--------------------------------------------------------------------------------
/literate/core/interceptor.py:
--------------------------------------------------------------------------------
1 | """
2 | The way in which a hijack / hook / override / patch
3 | works will be different for every class or library:
4 | sometimes it's a function, sometimes a class method,
5 | sometimes an instance method, but the mechanism is
6 | identical in all cases: take an object, return a
7 | modified or different one.
8 |
9 | This way we can hook into the output of all sorts of
10 | different libraries and finetune the representation
11 | for our python-literate reports, without having
12 | to change anything to those libraries themselves.
13 | Add in images, HTML visualizations, tables and what-not.
14 |
15 | See `load.py` for proof of concept.
16 | """
17 |
18 | import pandas as pd
19 |
20 | def hijack(cls):
21 | class Sub(cls):
22 | def plot(self, *vargs, **kwargs):
23 | return 'hijack!'
24 | return Sub
25 |
26 | hijacks = []
27 |
28 | def clshijack(cls):
29 | _plot = cls.plot
30 | def plot(*vargs, **kwargs):
31 | res = _plot(*vargs, **kwargs)
32 | hijacks.append(res)
33 | return res
34 |
35 | cls.plot = plot
36 | return cls
37 |
38 | pd.Series = clshijack(pd.Series)
--------------------------------------------------------------------------------
/literate/commands/commands.py:
--------------------------------------------------------------------------------
1 | import fs
2 | from literate import parser, templates, package
3 |
4 | def run(options):
5 | for path in options.src:
6 | source = fs.File(path).read()
7 | parser.run(source)
8 |
9 | def untangle(options):
10 | raise NotImplementedError()
11 |
12 | def weave(options):
13 | src = fs.File(options.src)
14 | dest = fs.Directory(options.dest)
15 |
16 | # find literate python files
17 | if src.is_file:
18 | documents = [src]
19 | else:
20 | # TODO: specify whether we should search recursively or not
21 | # recursive=options.recursive
22 | # TODO: support for Literate Python with docstrings
23 | documents = fs.Directory(src.path).find('*.pylit') # '*.py.md', '.md.py'
24 |
25 | package.create(dest)
26 |
27 | # weave literate python
28 | for doc in documents:
29 | raw = doc.read()
30 | blocks = parser.weave(raw, evaluate=options.evaluate)
31 | # template=None
32 | # TODO: template should be handled differently depending on whether
33 | # it's a Jinja template (.html) or an executable that we should
34 | # pass the context (w/ simplify=True above)
35 | html = templates.document(doc.name, blocks, prose=options.prose, capture=options.capture)
36 | filename = doc.name + '.html'
37 | f = fs.File(dest.path, filename)
38 | f.write(html)
39 |
40 | # documents are groups of blocks
41 | #literate.package(documents, options.dest)
--------------------------------------------------------------------------------
/vendor/highlight.js/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2006, Ivan Sagalaev
2 | All rights reserved.
3 | Redistribution and use in source and binary forms, with or without
4 | modification, are permitted provided that the following conditions are met:
5 |
6 | * Redistributions of source code must retain the above copyright
7 | notice, this list of conditions and the following disclaimer.
8 | * Redistributions in binary form must reproduce the above copyright
9 | notice, this list of conditions and the following disclaimer in the
10 | documentation and/or other materials provided with the distribution.
11 | * Neither the name of highlight.js nor the names of its contributors
12 | may be used to endorse or promote products derived from this software
13 | without specific prior written permission.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
16 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 |
--------------------------------------------------------------------------------
/literate/templates/styles/literate.styl:
--------------------------------------------------------------------------------
1 | @font-face
2 | font-family: 'novecento-bold'
3 | src: url('../vendor/fonts/novecento-bold.eot')
4 | src: url('../vendor/fonts/novecento-bold.eot?#iefix') format('embedded-opentype')
5 | src: url('../vendor/fonts/novecento-bold.woff') format('woff')
6 | src: url('../vendor/fonts/novecento-bold.ttf') format('truetype')
7 | font-weight: normal
8 | font-style: normal
9 |
10 | h1, h2, h3
11 | font-family: 'novecento-bold'
12 | font-weight: normal
13 |
14 | body
15 | background: white
16 | width: 720px
17 | margin: 20px auto
18 | font-family: palatino, helvetica, arial, serif
19 |
20 | p
21 | font-size: 18px
22 | line-height: 1.4
23 |
24 | blockquote, pre
25 | margin: 0 0 0 0
26 |
27 | blockquote.source, blockquote.output
28 | padding: 0.6em
29 |
30 | div.code
31 | box-shadow: 0 0 10px #ddd inset
32 |
33 | pre, pre code
34 | font-family: monaco
35 | font-size: 12px
36 |
37 | blockquote.source
38 | &, code
39 | background: rgba(255, 255, 255, 0.2)
40 |
41 | border-left: 2px solid #ccc
42 |
43 | pre code
44 | padding: 0
45 |
46 | blockquote p
47 | margin: 0
48 |
49 | div.code blockquote.output
50 | font-size: 14px
51 | background: rgba(0, 0, 0, 0.075)
52 | border-left: 2px solid #999
53 |
54 | body.parallel
55 | background: #fff
56 | width: 960px
57 |
58 | div.block
59 | float: left
60 | margin-bottom: 20px
61 |
62 | div
63 | float: left
64 |
65 | div.prose
66 | width: 400px
67 | margin-right: 20px
68 |
69 | div.code
70 | width: 540px
--------------------------------------------------------------------------------
/literate/core/loader.py:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | import __builtin__
4 | import os
5 | from tempfile import NamedTemporaryFile
6 | import py_compile
7 | import parser
8 | from contextlib import contextmanager
9 |
10 | builtin_import = __builtin__.__import__
11 | EXTENSIONS = ('.py.md', '.pylit', )
12 |
13 |
14 | @contextmanager
15 | def tempfile(data):
16 | temp = NamedTemporaryFile(delete=False)
17 | temp.write(data)
18 | temp.close()
19 | yield temp.name
20 | os.unlink(temp.name)
21 |
22 |
23 | def exists(name):
24 | for extension in EXTENSIONS:
25 | path = name.replace('.', '/') + extension
26 | if os.path.exists(path) is True:
27 | return (name, path)
28 | return False
29 |
30 |
31 | def importer(name, globals=globals(), locals=locals(), fromlist=[], level=-1):
32 | try:
33 | return builtin_import(name, globals, locals, fromlist, level)
34 | except ImportError as error:
35 | location = exists(name)
36 | if location:
37 | name, path = location
38 | source = open(path).read()
39 | code = parser.python(source)
40 | dest = name + '.py'
41 | cache_dest = name + '.pyc'
42 |
43 | # the importer doesn't create plain `.py` files by itself, but
44 | # the pylit executable can (if asked) and if they're there, we
45 | # keep them up to date
46 | if os.path.exists(dest):
47 | with open(dest, 'w'):
48 | dest.write(code)
49 | else:
50 | with tempfile(code) as buff:
51 | py_compile.compile(buff, cache_dest, path, doraise=True)
52 |
53 | return builtin_import(name)
54 | else:
55 | raise error
56 |
57 |
58 | def patch():
59 | __builtin__.__import__ = importer
--------------------------------------------------------------------------------
/vendor/highlight.js/styles/github.css:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | github.com style (c) Vasily Polovnyov
4 |
5 | */
6 |
7 | pre code {
8 | display: block; padding: 0.5em;
9 | color: #333;
10 | background: #f8f8ff
11 | }
12 |
13 | pre .comment,
14 | pre .template_comment,
15 | pre .diff .header,
16 | pre .javadoc {
17 | color: #998;
18 | font-style: italic
19 | }
20 |
21 | pre .keyword,
22 | pre .css .rule .keyword,
23 | pre .winutils,
24 | pre .javascript .title,
25 | pre .nginx .title,
26 | pre .subst,
27 | pre .request,
28 | pre .status {
29 | color: #333;
30 | font-weight: bold
31 | }
32 |
33 | pre .number,
34 | pre .hexcolor,
35 | pre .ruby .constant {
36 | color: #099;
37 | }
38 |
39 | pre .string,
40 | pre .tag .value,
41 | pre .phpdoc,
42 | pre .tex .formula {
43 | color: #d14
44 | }
45 |
46 | pre .title,
47 | pre .id {
48 | color: #900;
49 | font-weight: bold
50 | }
51 |
52 | pre .javascript .title,
53 | pre .lisp .title,
54 | pre .clojure .title,
55 | pre .subst {
56 | font-weight: normal
57 | }
58 |
59 | pre .class .title,
60 | pre .haskell .type,
61 | pre .vhdl .literal,
62 | pre .tex .command {
63 | color: #458;
64 | font-weight: bold
65 | }
66 |
67 | pre .tag,
68 | pre .tag .title,
69 | pre .rules .property,
70 | pre .django .tag .keyword {
71 | color: #000080;
72 | font-weight: normal
73 | }
74 |
75 | pre .attribute,
76 | pre .variable,
77 | pre .lisp .body {
78 | color: #008080
79 | }
80 |
81 | pre .regexp {
82 | color: #009926
83 | }
84 |
85 | pre .class {
86 | color: #458;
87 | font-weight: bold
88 | }
89 |
90 | pre .symbol,
91 | pre .ruby .symbol .string,
92 | pre .lisp .keyword,
93 | pre .tex .special,
94 | pre .prompt {
95 | color: #990073
96 | }
97 |
98 | pre .built_in,
99 | pre .lisp .title,
100 | pre .clojure .built_in {
101 | color: #0086b3
102 | }
103 |
104 | pre .preprocessor,
105 | pre .pi,
106 | pre .doctype,
107 | pre .shebang,
108 | pre .cdata {
109 | color: #999;
110 | font-weight: bold
111 | }
112 |
113 | pre .deletion {
114 | background: #fdd
115 | }
116 |
117 | pre .addition {
118 | background: #dfd
119 | }
120 |
121 | pre .diff .change {
122 | background: #0086b3
123 | }
124 |
125 | pre .chunk {
126 | color: #aaa
127 | }
128 |
--------------------------------------------------------------------------------
/thoughts.txt:
--------------------------------------------------------------------------------
1 | TODO: figure out a better name than "untangle"
2 | while it describes the process, it doesn't describe
3 | the fact that the Markdown and HTML files are rendered
4 | results, not just a part of the original .pylit we
5 | extracted. `render` comes closer, but also not quite.
6 | `run` doesn't have the connotation of generating output
7 |
8 | noweb has `notangle` (produce code) and `noweave` (produce documentation)
9 | (so you can do notangle myliterate.py | xargs python)
10 | Sweave similarly has tangle and weave commands.
11 |
12 | `unweave` perhaps has a more physical connotation than `untangle`.
13 | Or perhaps it's really "untangling" (the code) but "weaving together" (a report).
14 |
15 | If `untangle` produced any Markdown, to be consistent with the metaphor
16 | it should give you the raw thing, either without any code (.md.py) or with the code unrun (.pylit)
17 |
18 | And if we want to give `literate` the option to run your code through Python,
19 | maybe don't have a subcommand but just
20 |
21 | literate code.pylit
22 |
23 | or perhaps (if having it all as subcommands is more elegant)
24 |
25 | literate python code.pylit
26 |
27 | and
28 |
29 | literate weave code.pylit --formats md html
30 |
31 | and I don't think we really need a command that does both at the same time.
32 |
33 | ---
34 |
35 | literate weave -o reports myreport.pylit -i $input
36 |
37 | # this is kind of problematic, isn't it?
38 | # do parameters go to `weave` (as usual)
39 | # or to `.pylit` and what if they clash?
40 | # ---
41 | # should such a file specify `#!bin/env literate weave`
42 | # at the top?
43 | # ---
44 | # interestingly enough, the behavior of the `python`
45 | # executable is to take anything before a file name
46 | # as python parameters, everything else as script parameters
47 | #
48 | # I suppose that could work
49 |
50 | ---
51 |
52 | Docco CLI options are well thought-out:
53 |
54 | Usage: docco [options] FILES
55 |
56 | Options:
57 |
58 | -h, --help output usage information
59 | -V, --version output the version number
60 | -l, --layout [layout] choose a built-in layouts (parallel, linear)
61 | -c, --css [file] use a custom css file
62 | -o, --output [path] use a custom output path
63 | -t, --template [file] use a custom .jst template
64 | -e, --extension [ext] use the given file extension for all inputs
--------------------------------------------------------------------------------
/literate/commands/__init__.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import commands
3 |
4 | def initialize():
5 | arguments = argparse.ArgumentParser()
6 | subparsers = arguments.add_subparsers()
7 |
8 | run = subparsers.add_parser('run')
9 | run.set_defaults(func=commands.run, help="run a Literate Python script")
10 | run.add_argument('src', nargs=1)
11 |
12 | weave = subparsers.add_parser('weave',
13 | help="create HTML notebooks or an intermediate Markdown representation \
14 | out of Literate Python scripts")
15 | weave.set_defaults(func=commands.weave)
16 | weave.add_argument('src',
17 | default='./', nargs='?',
18 | help="source file or directory")
19 | weave.add_argument('dest',
20 | default='./notebook', nargs='?',
21 | help="destination file or directory")
22 | weave.add_argument('-t', '--template',
23 | help="pass woven files to an executable instead of the default templating engine")
24 | weave.add_argument('-r', '--recursive',
25 | default=False, action='store_true',
26 | help="look for .pylit files in subdirectories")
27 | weave.add_argument('-e', '--evaluate',
28 | default=False, action='store_true',
29 | help="evaluate the code and make global variables available to the prose")
30 | weave.add_argument('-c', '--capture',
31 | default=False, action='store_true',
32 | help="capture and display the code's output")
33 | weave.add_argument('-p', '--prose',
34 | default=False, action='store_true',
35 | help="display only prose, not code")
36 |
37 | untangle = subparsers.add_parser('untangle',
38 | help="untangle code from documentation and write away both to separate files")
39 | untangle.set_defaults(func=commands.untangle)
40 | untangle.add_argument('-f', '--formats',
41 | default=['md', 'py'], nargs='*',
42 | help="what formats you'd like to output")
43 | untangle.add_argument('-p', '--print',
44 | dest='stdout', default=False, action='store_true',
45 | help="print to stdout (only useful when you've selected \
46 | a single output format)")
47 | untangle.add_argument('-r', '--recursive',
48 | default=False, action='store_true',
49 | help="look for .pylit files in subdirectories")
50 |
51 | args = arguments.parse_args()
52 | args.func(args)
--------------------------------------------------------------------------------
/vendor/highlight.js/styles/default.css:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Original style from softwaremaniacs.org (c) Ivan Sagalaev
4 |
5 | */
6 |
7 | pre code {
8 | display: block; padding: 0.5em;
9 | background: #F0F0F0;
10 | }
11 |
12 | pre code,
13 | pre .subst,
14 | pre .tag .title,
15 | pre .lisp .title,
16 | pre .clojure .built_in,
17 | pre .nginx .title {
18 | color: black;
19 | }
20 |
21 | pre .string,
22 | pre .title,
23 | pre .constant,
24 | pre .parent,
25 | pre .tag .value,
26 | pre .rules .value,
27 | pre .rules .value .number,
28 | pre .preprocessor,
29 | pre .ruby .symbol,
30 | pre .ruby .symbol .string,
31 | pre .aggregate,
32 | pre .template_tag,
33 | pre .django .variable,
34 | pre .smalltalk .class,
35 | pre .addition,
36 | pre .flow,
37 | pre .stream,
38 | pre .bash .variable,
39 | pre .apache .tag,
40 | pre .apache .cbracket,
41 | pre .tex .command,
42 | pre .tex .special,
43 | pre .erlang_repl .function_or_atom,
44 | pre .markdown .header {
45 | color: #800;
46 | }
47 |
48 | pre .comment,
49 | pre .annotation,
50 | pre .template_comment,
51 | pre .diff .header,
52 | pre .chunk,
53 | pre .markdown .blockquote {
54 | color: #888;
55 | }
56 |
57 | pre .number,
58 | pre .date,
59 | pre .regexp,
60 | pre .literal,
61 | pre .smalltalk .symbol,
62 | pre .smalltalk .char,
63 | pre .go .constant,
64 | pre .change,
65 | pre .markdown .bullet,
66 | pre .markdown .link_url {
67 | color: #080;
68 | }
69 |
70 | pre .label,
71 | pre .javadoc,
72 | pre .ruby .string,
73 | pre .decorator,
74 | pre .filter .argument,
75 | pre .localvars,
76 | pre .array,
77 | pre .attr_selector,
78 | pre .important,
79 | pre .pseudo,
80 | pre .pi,
81 | pre .doctype,
82 | pre .deletion,
83 | pre .envvar,
84 | pre .shebang,
85 | pre .apache .sqbracket,
86 | pre .nginx .built_in,
87 | pre .tex .formula,
88 | pre .erlang_repl .reserved,
89 | pre .prompt,
90 | pre .markdown .link_label,
91 | pre .vhdl .attribute,
92 | pre .clojure .attribute,
93 | pre .coffeescript .property {
94 | color: #88F
95 | }
96 |
97 | pre .keyword,
98 | pre .id,
99 | pre .phpdoc,
100 | pre .title,
101 | pre .built_in,
102 | pre .aggregate,
103 | pre .css .tag,
104 | pre .javadoctag,
105 | pre .phpdoc,
106 | pre .yardoctag,
107 | pre .smalltalk .class,
108 | pre .winutils,
109 | pre .bash .variable,
110 | pre .apache .tag,
111 | pre .go .typename,
112 | pre .tex .command,
113 | pre .markdown .strong,
114 | pre .request,
115 | pre .status {
116 | font-weight: bold;
117 | }
118 |
119 | pre .markdown .emphasis {
120 | font-style: italic;
121 | }
122 |
123 | pre .nginx .built_in {
124 | font-weight: normal;
125 | }
126 |
127 | pre .coffeescript .javascript,
128 | pre .javascript .xml,
129 | pre .tex .formula,
130 | pre .xml .javascript,
131 | pre .xml .vbscript,
132 | pre .xml .css,
133 | pre .xml .cdata {
134 | opacity: 0.5;
135 | }
136 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Literate helps you author reproducible HTML reports from Python. Integrate Python code and Markdown documentation in `.pylit` files (or `.py.md` if you prefer.) Literate will capture the output of your code and weave it in with your thoughts and the code itself.
2 |
3 | Here's what a `.pylit` document can look like:
4 |
5 | numbers = [1, 2, 3]
6 | print [float(number * 3) for number in numbers]
7 | first, middle, last = numbers
8 |
9 | And then we can write more, and talk about our calculations
10 | like {first} and {last}.
11 |
12 | Indented text is Python. Non-indented text is Markdown. You can use any
13 | variables from Python in Markdown, and they will be interpolated like
14 | you're used to from regular Python string formatting.
15 |
16 | We can run that through Literate with the command:
17 |
18 | ./literate untangle myreport.pylit --capture --print
19 |
20 | Or save it to somewhere:
21 |
22 | ./literate untangle ./myreport.pylit ./build --capture
23 |
24 | The output will look like:
25 |
26 | > Here's what a `.pylit` document can look like:
27 | >
28 | > > numbers = [1, 2, 3]
29 | > > print [float(number * 3) for number in numbers]
30 | > > first, middle, last = numbers
31 | > > `[3.0, 6.0, 9.0]`
32 | >
33 | > And then we can write more, and talk about our calculations
34 | > like 1 and 3.
35 |
36 | If you want to capture output (like in the example above), pass the `--capture` flag
37 | to the command line interface. It's great for research notebooks, and akin to
38 | IPython notebooks, R Markdown, Sweave and Pweave.
39 |
40 | For regular literate programming (just code and your explanations of that code)
41 | simply leave off the `--capture` flag: it's what Literate does by default.
42 |
43 | ## The command-line interface
44 |
45 | To find out more about how to use the command-line interface, try:
46 |
47 | ./literate untangle --help
48 | ./literate run --help
49 |
50 | ## Working with Literate Python files
51 |
52 | The Python interpreter doesn't understand Literate Python by default, but that's easily fixed:
53 |
54 | # add support for importing Literate Python
55 | # and then import our `myreport.pylit` file
56 | # like a regular Python source file
57 | import literate
58 | import myreport
59 |
60 | Just like `.py` files generate `.pyc` bytecode, `.pylit` files sometimes generate
61 | a `.py` representation. Running `literate untangle myreport.pylit` or doing
62 | `import myreport` produces a `myreport.py` file.
63 |
64 | **Never put source code in, or edit, a `.py` file if there's a `.pylit` file
65 | with the same basename. It may be overwritten and you will lose whatever code
66 | you had in your `.py` file.**
67 |
68 | ## Output formats
69 |
70 | Literate can output to `.py`, `.md` and `.html`. HTML output is very useful for simple reports.
71 |
72 | ./literate untangle examples/basic examples/basic/build \
73 | --template examples/basic/template.html \
74 | --formats html \
75 | --capture
76 |
77 | There's a very basic HTML template built into Literate, but you can also use your own.
78 | Use the `--template` flag, e.g. `--template mytemplate.html`. Your template file should
79 | contain a `` tag, which is where Literate will put your generated report.
80 |
81 | For finer control over layout and report rendering, use the Markdown output (`--formats md`) which
82 | will contain code, documentation and captured output. With those Markdown files, build your own
83 | reports using the site generator of your choice. Jekyll's a good one.
84 |
85 | ## Does it do...
86 |
87 | Literate Python is inspired on Literate CoffeeScript rather than the traditional Sweave syntax.
88 | You won't get fine-grained control over which individual chunk to run or display because I
89 | don't believe that's how literate reports should work.
90 |
91 | Don't include code in a `.pylit` file you don't want in the generated output but instead
92 | put it in library files, it's as simple as that.
93 |
94 | ## Roadmap
95 |
96 | ### 1.0rc
97 |
98 | * fix './' (which should be the CWD, not the dir of the literate script)
99 | * tests
100 | * Also, when using the Literate style, add Markdown to the Python output
101 | in docstrings.
102 | * Python output hooks
103 | - initial support for `matplotlib` plots
104 | * different weaving and untangling modes
105 | weave
106 | - literate (code + interpolated documentation)
107 | - --prose (interpolated documentation)
108 | - --notebook (code + output + interpolated documentation)
109 | untangle (--formats)
110 | - md
111 | - py
112 | * Make it possible to pass on woven files to a templating language executable
113 | Ideally: `literate weave . --template jade --object {context} --output build`
114 | (with {context} and {contextFile} variables)
115 | * A good default template
116 | - dynamic one-column / two-column
117 | - works on directories (includes a TOC)
118 | * Review code and put it up on PyPI
119 |
120 | ### Other
121 |
122 | * A Sublime Text syntax highlighter
123 |
124 | ### Future
125 |
126 | * Support for plain text (may make it easier for people who
127 | want to use formats other than those we support to DIY)
128 | * Support for D3 plots, tables and other visualizations
129 | * Support for `.md.py` (Python with Markdown docstrings)
130 | (I suppose the catch is that the one works with Python out of the box,
131 | the other works with Markdown out of the box...)
--------------------------------------------------------------------------------
/vendor/highlight.js/highlight.pack.js:
--------------------------------------------------------------------------------
1 | var hljs=new function(){function l(o){return o.replace(/&/gm,"&").replace(//gm,">")}function b(p){for(var o=p.firstChild;o;o=o.nextSibling){if(o.nodeName=="CODE"){return o}if(!(o.nodeType==3&&o.nodeValue.match(/\s+/))){break}}}function h(p,o){return Array.prototype.map.call(p.childNodes,function(q){if(q.nodeType==3){return o?q.nodeValue.replace(/\n/g,""):q.nodeValue}if(q.nodeName=="BR"){return"\n"}return h(q,o)}).join("")}function a(q){var p=(q.className+" "+q.parentNode.className).split(/\s+/);p=p.map(function(r){return r.replace(/^language-/,"")});for(var o=0;o"}while(x.length||v.length){var u=t().splice(0,1)[0];y+=l(w.substr(p,u.offset-p));p=u.offset;if(u.event=="start"){y+=s(u.node);r.push(u.node)}else{if(u.event=="stop"){var o,q=r.length;do{q--;o=r[q];y+=(""+o.nodeName.toLowerCase()+">")}while(o!=u.node);r.splice(q,1);while(q'+L[0]+""}else{r+=L[0]}N=A.lR.lastIndex;L=A.lR.exec(K)}return r+K.substr(N)}function z(){if(A.sL&&!e[A.sL]){return l(w)}var r=A.sL?d(A.sL,w):g(w);if(A.r>0){v+=r.keyword_count;B+=r.r}return''+r.value+""}function J(){return A.sL!==undefined?z():G()}function I(L,r){var K=L.cN?'':"";if(L.rB){x+=K;w=""}else{if(L.eB){x+=l(r)+K;w=""}else{x+=K;w=r}}A=Object.create(L,{parent:{value:A}});B+=L.r}function C(K,r){w+=K;if(r===undefined){x+=J();return 0}var L=o(r,A);if(L){x+=J();I(L,r);return L.rB?0:r.length}var M=s(A,r);if(M){if(!(M.rE||M.eE)){w+=r}x+=J();do{if(A.cN){x+=""}A=A.parent}while(A!=M.parent);if(M.eE){x+=l(r)}w="";if(M.starts){I(M.starts,"")}return M.rE?0:r.length}if(t(r,A)){throw"Illegal"}w+=r;return r.length||1}var F=e[D];f(F);var A=F;var w="";var B=0;var v=0;var x="";try{var u,q,p=0;while(true){A.t.lastIndex=p;u=A.t.exec(E);if(!u){break}q=C(E.substr(p,u.index-p),u[0]);p=u.index+q}C(E.substr(p));return{r:B,keyword_count:v,value:x,language:D}}catch(H){if(H=="Illegal"){return{r:0,keyword_count:0,value:l(E)}}else{throw H}}}function g(s){var o={keyword_count:0,r:0,value:l(s)};var q=o;for(var p in e){if(!e.hasOwnProperty(p)){continue}var r=d(p,s);r.language=p;if(r.keyword_count+r.r>q.keyword_count+q.r){q=r}if(r.keyword_count+r.r>o.keyword_count+o.r){q=o;o=r}}if(q.language){o.second_best=q}return o}function i(q,p,o){if(p){q=q.replace(/^((<[^>]+>|\t)+)/gm,function(r,v,u,t){return v.replace(/\t/g,p)})}if(o){q=q.replace(/\n/g," ")}return q}function m(r,u,p){var v=h(r,p);var t=a(r);if(t=="no-highlight"){return}var w=t?d(t,v):g(v);t=w.language;var o=c(r);if(o.length){var q=document.createElement("pre");q.innerHTML=w.value;w.value=j(o,c(q),v)}w.value=i(w.value,u,p);var s=r.className;if(!s.match("(\\s|^)(language-)?"+t+"(\\s|$)")){s=s?(s+" "+t):t}r.innerHTML=w.value;r.className=s;r.result={language:t,kw:w.keyword_count,re:w.r};if(w.second_best){r.second_best={language:w.second_best.language,kw:w.second_best.keyword_count,re:w.second_best.r}}}function n(){if(n.called){return}n.called=true;Array.prototype.map.call(document.getElementsByTagName("pre"),b).filter(Boolean).forEach(function(o){m(o,hljs.tabReplace)})}function k(){window.addEventListener("DOMContentLoaded",n,false);window.addEventListener("load",n,false)}var e={};this.LANGUAGES=e;this.highlight=d;this.highlightAuto=g;this.fixMarkup=i;this.highlightBlock=m;this.initHighlighting=n;this.initHighlightingOnLoad=k;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|\\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\[\\s\\S]",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE],r:0};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE],r:0};this.CLCM={cN:"comment",b:"//",e:"$"};this.CBLCLM={cN:"comment",b:"/\\*",e:"\\*/"};this.HCM={cN:"comment",b:"#",e:"$"};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.inherit=function(q,r){var o={};for(var p in q){o[p]=q[p]}if(r){for(var p in r){o[p]=r[p]}}return o}}();hljs.LANGUAGES.python=function(a){var f={cN:"prompt",b:"^(>>>|\\.\\.\\.) "};var c=[{cN:"string",b:"(u|b)?r?'''",e:"'''",c:[f],r:10},{cN:"string",b:'(u|b)?r?"""',e:'"""',c:[f],r:10},{cN:"string",b:"(u|r|ur)'",e:"'",c:[a.BE],r:10},{cN:"string",b:'(u|r|ur)"',e:'"',c:[a.BE],r:10},{cN:"string",b:"(b|br)'",e:"'",c:[a.BE]},{cN:"string",b:'(b|br)"',e:'"',c:[a.BE]}].concat([a.ASM,a.QSM]);var e={cN:"title",b:a.UIR};var d={cN:"params",b:"\\(",e:"\\)",c:["self",a.CNM,f].concat(c)};var b={bWK:true,e:":",i:"[${=;\\n]",c:[e,d],r:10};return{k:{keyword:"and elif is global as in if from raise for except finally print import pass return exec else break not with class assert yield try while continue del or def lambda nonlocal|10",built_in:"None True False Ellipsis NotImplemented"},i:"(|->|\\?)",c:c.concat([f,a.HCM,a.inherit(b,{cN:"function",k:"def"}),a.inherit(b,{cN:"class",k:"class"}),a.CNM,{cN:"decorator",b:"@",e:"$"},{b:"\\b(print|exec)\\("}])}}(hljs);
--------------------------------------------------------------------------------
/literate/core/parser.py:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | import sys
4 | import utils
5 | from cStringIO import StringIO
6 | import markdown as md
7 | import re
8 |
9 |
10 | def br(string):
11 | # in Markdown, ending a line with more than one space
12 | # signifies a (single) line break
13 | return re.sub('\n{1}', ' \n', string)
14 |
15 | def pad(string, padding):
16 | pattern = re.compile(r'^', re.MULTILINE)
17 | return re.sub(pattern, padding, string)
18 |
19 | def strip(l):
20 | while len(l) and not len(l[0]):
21 | l = l[1:]
22 | while len(l) and not len(l[-1]):
23 | l = l[:-1]
24 | return l
25 |
26 | def issibling(a, b):
27 | return a.__class__ == b.__class__
28 |
29 | class Chunk(object):
30 | def __init__(self, raw, variables={}):
31 | self.raw = [raw]
32 | self.variables = variables
33 | self.output = False
34 |
35 | def append(self, line):
36 | self.raw.append(line)
37 |
38 | @property
39 | def text(self):
40 | return "\n".join(self.lines)
41 |
42 | def html(self, run):
43 | return md.markdown(self.markdown(run))
44 |
45 | @property
46 | def name(self):
47 | return self.__class__.__name__.lower().replace('chunk', '')
48 |
49 | def __repr__(self):
50 | return "<{kind} [{lines}]>".format(kind=self.name, lines=len(self.lines))
51 |
52 | @property
53 | def is_empty(self):
54 | return not len(self.lines)
55 |
56 |
57 | class PythonChunk(Chunk):
58 | def parse(self):
59 | if self.output is not False:
60 | return self.output
61 |
62 | stdout = sys.stdout
63 | sys.stdout = StringIO()
64 | exec self.text
65 | self.output = sys.stdout.getvalue()
66 | sys.stdout = stdout
67 | self.variables.update(locals())
68 |
69 | return self.output
70 |
71 | @property
72 | def lines(self):
73 | _lines = [re.sub('^( {4}|\t)', '', line) for line in self.raw]
74 | return strip(_lines)
75 |
76 | # THOUGHT: I think I want to leave it up to the templating layer
77 | # to decide how to display the output. So we'll pass on chunks
78 | # w/ {type, raw, html, output}
79 | def markdown(self, run):
80 | code = pad(self.text, ' ')
81 | if run:
82 | output = pad(br(self.parse()), '> ')
83 | return "\n\n".join([code, output])
84 | else:
85 | return code
86 |
87 | # See note above.
88 | def serialize(self):
89 | return {
90 | 'type': self.name,
91 | 'text': self.text,
92 | 'html': False,
93 | 'output': self.output,
94 | }
95 |
96 | class MarkdownChunk(Chunk):
97 | def parse(self):
98 | if hasattr(self, '_parsed'):
99 | return self._parsed
100 |
101 | self._parsed = md.markdown(self.text)
102 | return self._parsed
103 |
104 | @property
105 | def lines(self):
106 | return strip(self.raw)
107 |
108 | # THOUGHT: I think I want to leave it up to the templating layer
109 | # to decide how to display the output. So we'll pass on chunks
110 | # w/ {type, raw, html, output}
111 | def markdown(self, run=True):
112 | if run:
113 | try:
114 | return self.text.format(**self.variables)
115 | except KeyError, name:
116 | raise KeyError(utils.line("""
117 | You referenced {name} in your literate document,
118 | but no such variable was found. Did you enable
119 | code evaluation?".format(name=name))
120 | """, name=name))
121 | else:
122 | return self.text
123 |
124 | # See note above.
125 | def serialize(self):
126 | return {
127 | 'type': self.name,
128 | 'raw': self.text,
129 | 'html': self.html(True),
130 | 'output': False,
131 | }
132 |
133 | class Document(object):
134 | def weave(self, evaluate=True, simplify=False):
135 | blocks = []
136 | variables = {}
137 | for chunk in self.chunks:
138 | if evaluate:
139 | # pre-parse our chunks, and give them the combined
140 | # state of all previously parsed chunks too
141 | chunk.variables.update(variables)
142 | chunk.parse()
143 | variables.update(chunk.variables)
144 |
145 | pairs = []
146 | pair = {}
147 | for chunk in self.chunks:
148 | if simplify:
149 | data = chunk.serialize()
150 | else:
151 | data = chunk
152 |
153 | if isinstance(chunk, MarkdownChunk) and len(pair):
154 | pairs.append(pair)
155 | pair = {}
156 |
157 | if isinstance(chunk, MarkdownChunk):
158 | pair.setdefault('prose', []).append(data)
159 | else:
160 | pair.setdefault('code', []).append(data)
161 |
162 | prev = chunk
163 |
164 | pairs.append(pair)
165 | return pairs
166 |
167 | def untangle(self):
168 | types = {'python': '', 'markdown': ''}
169 |
170 | for chunk in self.chunks:
171 | types[chunk.name] += chunk.text + '\n'
172 |
173 | return types
174 |
175 | def run(self):
176 | code = self.untangle()['python']
177 | exec code
178 |
179 | def __init__(self, raw):
180 | self.raw = raw
181 | self.tokenize()
182 |
183 |
184 | class LiterateDocument(Document):
185 | def categorize(self, line):
186 | if line.startswith('\t') or line.startswith(' '):
187 | return PythonChunk
188 | else:
189 | return MarkdownChunk
190 |
191 | def tokenize(self):
192 | lines = self.raw.strip().split('\n')
193 | chunks = []
194 |
195 | for i, line in enumerate(lines):
196 | Chunk = self.categorize(line)
197 |
198 | if len(chunks):
199 | prev = lines[i-1].strip()
200 | cur = lines[i].strip()
201 | same_kind = isinstance(chunks[-1], Chunk)
202 | whitespace = len(cur) is 0 and not len(prev) is 0
203 | double_space = len(cur) is 0 and len(prev) is 0
204 | same_kind = same_kind or whitespace
205 | continuation = same_kind and not double_space
206 | else:
207 | continuation = False
208 |
209 | if continuation:
210 | chunks[-1].append(line)
211 | else:
212 | chunks.append(Chunk(line))
213 |
214 | self.chunks = filter(lambda chunk: not chunk.is_empty, chunks)
215 |
216 | def untangle(self, pure=False):
217 | types = super(LiterateDocument, self).untangle()
218 | # pure means what we remove any python code from our markdown
219 | # document (this is usually not what you want)
220 | if not pure:
221 | types['markdown'] = self.raw
222 |
223 | return types
224 |
225 | class DocStringDocument(Document):
226 | def untangle(self, pure=False):
227 | types = super(DocStringDocument, self).untangle()
228 | # pure means what we remove any docstrings from our python
229 | # code (this is usually not what you want)
230 | if not pure:
231 | types['python'] = self.raw
232 |
233 | return types
234 |
235 | def weave(string, evaluate=True, simplify=True):
236 | document = LiterateDocument(string)
237 | return document.weave(evaluate=evaluate, simplify=simplify)
238 |
239 | def untangle(string):
240 | document = LiterateDocument(string)
241 | return document.untangle()
242 |
243 | def run(string):
244 | document = LiterateDocument(string)
245 | document.run()
--------------------------------------------------------------------------------