Literate programming in python with org-mode and noweb
Posted March 27, 2014 at 10:46 AM | categories: org-mode, python | tags:
Updated March 27, 2014 at 11:07 AM
Table of Contents
This post examines a different approach to literate programming with org-mode that uses noweb . I have adapted an example from http://home.fnal.gov/~neilsen/notebook/orgExamples/org-examples.html which has some pretty cool ideas in it.
The gist of using noweb is that in your source blocks you have labels like <<imports>>
, that refer to other named code blocks that get substituted in place of the label. In the example below, we put labels for a code block of imports, for a function definition, a class definition, and a main function. This code block will get tangled to main.py . The noweb expansion happens at export, so here is the literal code block:
#+BEGIN_SRC python :noweb yes :tangle main.py <<imports>> <<some-func>> <<class-dfn>> <<main-func>> if __name__ == '__main__': status = main() sys.exit(status) #+END_SRC
You may want to just check out the org-mode source link at the bottom of the post to see all the details.
import sys import numpy as np import matplotlib.pyplot as plt from argparse import ArgumentParser def utility_func(arg=None): return 'you called a utility function with this arg: {0}'.format(arg) class HelloWorld(object): def __init__(self, who): self.who = who def __call__(self): return 'Hello {0}'.format(self.who) def test(self): return True def main(): parser = ArgumentParser(description="Say hi") parser.add_argument("-w", "--who", type=str, default="world", help="Who to say hello to") args = parser.parse_args() who = args.who greeter = HelloWorld(who) greeter() print 'test func = ', greeter.test() print utility_func() print utility_func(5) return 0 if __name__ == '__main__': status = main() sys.exit(status)
1 imports
Now, we define a block that gives us the imports. We do not have to use any tangle headers here because noweb will put it in where it belongs.
import sys import numpy as np import matplotlib.pyplot as plt from argparse import ArgumentParser
2 utility function
Now we define a function we will want imported from the main file.
def utility_func(arg=None): return 'you called a utility function with this arg: {0}'.format(arg)
3 class definition
Finally, let us define a class. Note we use noweb here too, and we get the indentation correct!
class HelloWorld(object): def __init__(self, who): self.who = who def __call__(self): return 'Hello {0}'.format(self.who) def test(self): return True
3.1 some class function
Now, let us make the some-other-func. This block is not indented, but with the noweb syntax above, it seems to get correctly indented. Amazing.
def test(self): return True
4 The main function
This is a typical function that could be used to make your module into a script, and is only run when the module is used as a script..
def main(): parser = ArgumentParser(description="Say hi") parser.add_argument("-w", "--who", type=str, default="world", help="Who to say hello to") args = parser.parse_args() who = args.who greeter = HelloWorld(who) greeter() print 'test func = ', greeter.test() print utility_func() print utility_func(5) return 0
5 Tangle and run the code
This link will extract the code to main.py:
elisp:org-babel-tangle
We can run the code like this (linux):
python main.py --w John 2>&1 true
test func = True you called a utility function with this arg: None you called a utility function with this arg: 5
or this (windows, which as no sh)
from main import * main()
test func = True you called a utility function with this arg: None you called a utility function with this arg: 5
6 Summary thoughts
The use of noweb syntax is pretty cool. I have not done anything serious with it, but it looks like you could pretty easily create a sophisticated python module this way that is documented in org-mode.
Copyright (C) 2014 by John Kitchin. See the License for information about copying.
Org-mode version = 8.2.5h