Package: noweb
Version: 2.11b-7
Severity: wishlist
Tags: patch

These patches do considerable violence to the model of noweb, and so
I'm uploading them in preliminary form to discuss what might be the
appropriate action to take.

I could not simply add pipeline stages since the tangled file needs to
refer to itself and to the .nw file, and the filenames are not
available in standard processing.  I also wanted to add column
information to the -L directive, and I found the out of the box
behavior of -L not to work with python--it screwed up the indentation
of subsequent lines.  This had not been an issue, I assume, because
#line did no good in python anyway, and so no one used it.

Some of the final processing to make the output suitable for detangle
could be done as a separate stage rather than baked into the main program.

I'm also contemplating using the python ast module to parse the code
for index purposes in noweave.  Currently, only notangle has changed.

-- System Information:
Debian Release: 5.0.9
  APT prefers oldstable
  APT policy: (995, 'oldstable')
Architecture: i386 (i686)

Kernel: Linux 2.6.26-2-686 (SMP w/2 CPU cores)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/bash

Versions of packages noweb depends on:
ii  gawk                    1:3.1.5.dfsg-4.1 GNU awk, a pattern scanning and pr
ii  iconx                   9.4.3-2          Executor for Icon, a high-level pr
ii  libc6                   2.7-18lenny7     GNU C Library: Shared libraries

noweb recommends no packages.

noweb suggests no packages.

-- no debconf information
01_debian
02_tmp_fix
04_uninstall
05_security_CVE-2005-3342
06_roff_output
08_tmac_dir
09_awk
10_vmargin
11_german
12_nostrip
13_bashism
15_python
16_python-derived
99_touch
#! /bin/sh /usr/share/dpatch/dpatch-run
## 15_python_dpatch by Ross Boylan <r...@biostat.ucsf.edu>
##
## All lines beginning with `## DP:' are a description of the patch.
## DP: Add python #line-like handling

@DPATCH@
diff -Naur noweb-2.11b/CHANGES noweb-2.12rb/CHANGES
--- noweb-2.11b/CHANGES	2006-06-12 21:12:44.000000000 +0000
+++ noweb-2.12rb/CHANGES	2012-02-21 06:38:51.000000000 +0000
@@ -1,3 +1,12 @@
+Changes for Version 2.12rb - released 20 Feb 2012
+Special handling for Python code, by Ross Boylan <r...@biostat.ucsf.edu>
+notangle behavior changed: -Pwfile activates python mode.
+This takes input from wfile, not stdin, and writes to file in -R, not
+stdout.  -R is required; -L is ignored but gets at %I option for indent level
+(usable outside of -P mode too).  The python mode output works with
+the python module detangle to report error messages in the location in the 
+origial noweb file (as indicated by the -P argument).
+
 CHANGES FOR VERSION 2.11b - released 12 Jun 2006
 Make noroff use -mm by default (Debian 218050)
 Fixed flagrant errors in noweave -x -troff (but bad formatting remains)
diff -Naur noweb-2.11b/debian/changelog noweb-2.12rb/debian/changelog
--- noweb-2.11b/debian/changelog	2012-02-22 06:36:15.000000000 +0000
+++ noweb-2.12rb/debian/changelog	2012-02-22 00:59:42.000000000 +0000
@@ -1,3 +1,9 @@
+noweb (2.12rb-1) unstable; urgency=low
+
+  * Add support for -P python mode in notangle.
+
+ -- Ross Boylan <r...@biostat.ucsf.edu> Mon, 21 Feb 2012 22:42:36 -0700
+
 noweb (2.11b-7) unstable; urgency=low
 
   * debian/patches/13_bashism.dpatch: Fix unportable backslash in call to echo.
diff -Naur noweb-2.11b/src/c/main.nw noweb-2.12rb/src/c/main.nw
--- noweb-2.11b/src/c/main.nw	2006-06-12 21:03:53.000000000 +0000
+++ noweb-2.12rb/src/c/main.nw	2012-02-22 00:10:13.000000000 +0000
@@ -21,12 +21,70 @@
 to set global state, and counting any roots it sees.
 It then makes a second pass to emit all the roots, or if there are no
 roots on the command line, it emits the module (chunk) named [[<<*>>]].
+
+@
+\subsubsection{Python Support}
+\newcommand{linedirective}{[[\#line]]}
+Ross Boylan (r...@biostat.ucsf.edu) added support for
+pseudo-\linedirective directives in python.  It requires the python module
+[[detangle]], which  permits error messages that point to the original
+web sources.
+
+This requires a number of changes, not all of which could reasonably
+be done by a filter:
+\begin{description}
+\item[Web file name] The output file must refer to the original web
+  file.  The previous design took input from [[stdin]] only, and so
+  had no way of knowing what the proper name was.
+\item[Preserve Indentation after \linedirective] For reasons that are unclear,
+  but apparently deliberate, the previous design screwed up the
+  indentation after output a \linediretive{} directive.  This is absolutely
+  fatal in python; it was not an issue since such directives are not
+  legal python now.
+\item[Preserve Indentation information in \linedirective] Because
+  tangling can add indentation relative to the original source, we
+  need to communicate that information to permit accurate mapping
+  between reported columns in the original and tangled files.
+\item[Mangle for [[detangle]]] This involves pre-pending a header and
+  then inserting a \# before every output line.
+\end{description}
+
+This implies the following new or changed features:
+\begin{description}
+\item[Command Line] The new option [[-P]] \<{\it web file name}\> now
+  specifies the input file and activates special python mangling.  The
+  [[-L]] option will be ignored, as well any data on the standard
+  input.  You {\em must} specify an output file name with the [[-R]]
+  option, though continues to go to [[stdout]].  The input ultimately
+  comes from the file named in [[-P]], not the standard input of the
+  command line.  That substitution is done in the [[noweave]] shell
+  script, not this program, which continues to read from the standard
+  input.
+\item[I format] [[%I]] in the line format will now cause the
+  indentation setting to be output.  This is available regardless of
+  whether [[-P]] is specified; in fact is is mostly for internal use
+  in python mode.
+\item[Mangled output] The output file will have an initial block of
+  code that activates the python [[detangle]] module and tells it the
+  name of the the original (from [[-P]]) and mangled (from [[-R]])
+  files files.  Then the mangled code appears, with each line starting
+  with a \# and \linedirective{} directives where appropriate.
+\end{description}
+
 <<*>>=
 main(int argc, char **argv) {
     int i;
     char *locformat = "";
     char *Clocformat = "#line %L \"%F\"%N";
+    char *Plocformat = "#line %L %I %F%N";
     int root_options_seen = 0;
+    char *pythonweb = "";
+
+    /* These were in contemplation of switching the file.
+    But the program is part of a pipeline. */
+    FILE *fin = stdin;
+    FILE *fout = stdout;
+    char *fout_name;
 
     tabsize = 0;  /* default for nt is not to use tabs */
     progname = argv[0];
@@ -36,14 +94,22 @@
         <<process argument [[i]]---first pass>>
     }
 
-    read_defs(stdin);                        /* read all the definitions */
+    <<check for valid option combinations>>
+    read_defs(fin);                        /* read all the definitions */
+@ Note that error exits may leave [[fin]] or [[fout]] open.  This is not ideal.
+<<*>>=
+    close(fin);
     apply_each_module(remove_final_newline); /* pretty up the module texts */
     if (root_options_seen == 0)
-      emit_module_named(stdout, "*", locformat);
+      emit_module_named(stdout, "*", locformat, pythonweb);
     else
       for (i=1; i<argc; i++) 
-        if (argv[i][0] == '-' && argv[i][1] == 'R')
-	  emit_module_named(stdout, argv[i]+2, locformat);
+        if (argv[i][0] == '-' && argv[i][1] == 'R') {
+	  fout_name = argv[i]+2;
+	  <<handle python mode in [[main]]>>
+	  emit_module_named(fout, fout_name, locformat, pythonweb);
+	  close(fout);
+	  }
 
     nowebexit(NULL);
     return errorlevel;  /* slay warning */
@@ -63,6 +129,9 @@
 -L[format]&     Use the given format to write {\tt\#line} numbers.
                 If -L is given alone, use ANSI C format.
                 If not given, write no {\tt\#line} numbers.\\
+-Pname&         Enable python-mode with \linedirective.  Requires use of [[-R]] and ignores [[-L]]. 
+                The resulting output file requires the python [[detangle]] module for Python 2.7; 
+                it might work with Python 2.6.\\
 -t[tabsize]& Fiddle with tabs
 \end{fields}
 <<handle option in argument [[i]]>>=
@@ -75,6 +144,11 @@
             else 
                 errormsg(Error, "%s: ill-formed option %s\n", argv[0], argv[i]);
             break;          
+	case 'P': /* enabled special tangled python mode */
+	     pythonweb = argv[i]+2;
+	     if (*pythonweb == 0)
+	        errormsg(Fatal, "-P requires a file name.  No spaces.\n");
+	     break;
         case 'R': /* change name of root module */
             root_options_seen++;
             break;
@@ -90,3 +164,21 @@
 errormsg(Warning,
     "I can't handle arguments yet, so I'll just ignore `%s'",argv[i]);
 
+@ Ross used braces around [[errormsg()]] in my new code so that it is safe even if it
+becomes a macro.  In that case, old code would need to be checked and updated if necessary.
+<<check for valid option combinations>>=
+if (*pythonweb && (root_options_seen == 0)){
+  errormsg(Fatal, "-P requires the use of -R");
+  }
+if (*pythonweb && (*locformat != 0)) {
+  errormsg(Warning, "-P ignore your -L option and uses its own.");
+  }
+
+@
+<<handle python mode in [[main]]>>=
+if (*pythonweb) {
+  locformat = Plocformat;
+  fprintf(fout, "from detangle import detangle\n");
+  fprintf(fout, "detangle('%s', '%s')\n\n", fout_name, pythonweb);
+  fprintf(fout, "#if 0:\n");
+  }	      
diff -Naur noweb-2.11b/src/c/mnt.nw noweb-2.12rb/src/c/mnt.nw
--- noweb-2.11b/src/c/mnt.nw	2006-06-12 21:03:53.000000000 +0000
+++ noweb-2.12rb/src/c/mnt.nw	2012-02-21 08:01:04.000000000 +0000
@@ -22,12 +22,18 @@
 
 <<local prototypes>>
 
+@ [[pythonweb]] is for future use of the [[-P]] option.  It needs to
+be global so the argument can make it to
+[[emit_if_unused_and_conforming]] via [[apply_each_module()]], which
+does not permit any additional arguments.  Where possible, I pass it as an argument.
+
+<<*>>=
 #define Clocformat "#line %L \"%F\"%N"
 static char *locformat = Clocformat;
+static char *pythonweb = "";  /* for future expansion */
 
 main(int argc, char **argv) {
     int i;
-
     tabsize = 0;  /* default for nt is not to use tabs */
 
     progname = argv[0];
@@ -36,7 +42,7 @@
     for (i=1; i<argc; i++) 
       switch (*argv[i]) {
         case '-': <<handle option in [[argv[i]]]>>                 break;
-        default:  emitfile(argv[i]);                               break;
+        default:  emitfile(argv[i], pythonweb);                    break;
       }
     nowebexit(NULL);
     return errorlevel;          /* slay warning */
@@ -77,15 +83,15 @@
 	        errormsg(Error, "@<<*@>> is not a good chunk name for noweb; "
 	                        "use notangle instead");
 	    else
-	        emitfile(mp->name);
+	        emitfile(mp->name, pythonweb);
         else 
             errormsg(Error, "@<<%s@>> cannot be an output chunk; "
                             "it contains a metacharacter", mp->name);
 }
 <<local prototypes>>=
-static void emitfile(char *modname);
+static void emitfile(char *modname, char *pythonweb);
 <<*>>=
-static void emitfile(char *modname) { 
+static void emitfile(char *modname, char *pythonweb) { 
   Module root = lookup(modname);
   char *tempname = tempnam(".", 0);
   FILE *fp;
@@ -117,7 +123,7 @@
 }
 <<expand [[root]] onto [[fp]] and close the file>>=
 resetloc();
-(void) expand(root, 0, 0, 0, lfmt, fp);
+(void) expand(root, 0, 0, 0, lfmt, pythonweb, fp);
 putc('\n', fp);
 fclose(fp);
 @
diff -Naur noweb-2.11b/src/c/modules.nw noweb-2.12rb/src/c/modules.nw
--- noweb-2.11b/src/c/modules.nw	2006-06-12 21:03:54.000000000 +0000
+++ noweb-2.12rb/src/c/modules.nw	2012-02-21 23:50:12.000000000 +0000
@@ -126,7 +126,7 @@
 } *Parent;
 
 int expand (Module mp, int indent, int partial_distance, Parent parent, 
-            char *locformat, FILE *out);
+            char *locformat, char *pythonweb, FILE *out);
         /* expand a module, writing to file out */
 <<local data>>=
 static char *lastfilename = 0;
@@ -143,7 +143,7 @@
 [[partial_distance]] is the width of what has already been written to the current line.
 <<C functions>>=
 int expand (Module mp, int indent, int partial_distance, Parent parent,  
-            char *locformat, FILE *out) {
+            char *locformat, char *pythonweb, FILE *out) {
     struct modpart *p;
     Module newmod;
     int error=Normal;
@@ -172,19 +172,33 @@
 is emitted, {\em except} when it's the first line of a module (hack! hack!).
 [[printloc]] returns nonzero when [[#line]] is actually emitted.
 
+Ross triple-hacks: if in python mode, disable the obnoxious loss of indent.
+Except I don't really understand how this is working, and am not sure blank newlines will be handled properly.
+
 <<print a string>>=
 if (*(p->contents) != '\0') {
-    if (*locformat) {
-        if (printloc(out,locformat,p->loc,partial_distance) && (p != mp->head))
-              indent_for(partial_distance, out);
-    } else if (partial_distance == 0) {
-        indent_for(indent, out);
-        partial_distance = indent;
+    if (*pythonweb) {
+      printloc(out, locformat,p->loc,partial_distance);
+      fputc('#', out);
+      indent_for(indent, out);
+      partial_distance = indent;
+      fprintf(out, "%s",p->contents);
+    }
+    else {
+      if (*locformat) {
+	  if (printloc(out,locformat,p->loc,partial_distance) && (p != mp->head))
+		indent_for(partial_distance, out);
+      } else if (partial_distance == 0) {
+	  indent_for(indent, out);
+	  partial_distance = indent;
+      }
+      fprintf(out,"%s",p->contents);
     }
-    fprintf(out,"%s",p->contents);
     partial_distance = limitcolumn(p->contents, partial_distance);
 }
 <<print a newline>>=
+if (*pythonweb && partial_distance==0)
+   putc('#', out);
 partial_distance = 0;
 putc('\n', out);
 lastlineno++;
@@ -209,7 +223,7 @@
         partial_distance = indent;
     }
     retcode = expand (newmod, partial_distance, partial_distance,
-                      &thismodule, locformat, out);
+                      &thismodule, locformat, pythonweb, out);
     if (retcode > error) error = retcode;
 }
 partial_distance = limitcolumn(p->contents, partial_distance + 2) + 2; 
@@ -267,6 +281,7 @@
             case 'N': putc('\n', fp);                            break;
             case 'F': fprintf(fp, "%s", loc.filename);           break;
             case 'L': fprintf(fp, "%d", loc.lineno);             break;
+	    case 'I': fprintf(fp, "%d", partial);                break;
             case '-': case '+': 
                         if (isdigit(p[1]) && p[2] == 'L') {
                           fprintf(fp, "%d", *p == '+' ? loc.lineno + (p[1] - '0')
diff -Naur noweb-2.11b/src/c/notangle.nw noweb-2.12rb/src/c/notangle.nw
--- noweb-2.11b/src/c/notangle.nw	2006-06-12 21:03:54.000000000 +0000
+++ noweb-2.12rb/src/c/notangle.nw	2012-02-21 06:14:21.000000000 +0000
@@ -13,7 +13,7 @@
 in a table supplied by [[modtrees.h]]; we'll cover the
 details later.
 <<header>>=
-void emit_module_named (FILE *out, char *rootname, char *locformat);
+void emit_module_named (FILE *out, char *rootname, char *locformat, char *pythonweb);
 <<*>>=
 static char rcsid[] = "$Id: notangle.nw,v 2.22 2006/06/12 21:03:53 nr Exp nr $";
 static char rcsname[] = "$Name: v2_11b $";
@@ -34,12 +34,12 @@
 
 <<Function declarations>>
 
-void emit_module_named (FILE *out, char *rootname, char *locformat) {
+void emit_module_named (FILE *out, char *rootname, char *locformat, char *pythonweb) {
     Module root = NULL; /* ptr to root module */
 
     root = lookup(rootname);
     <<quit if we couldn't find the root>>
-    (void) expand(root,0,0,0,locformat,out);
+    (void) expand(root,0,0,0,locformat, pythonweb, out);
     putc('\n',out);                     /* make output end with newline */
 }
 @ 
diff -Naur noweb-2.11b/src/shell/notangle.nw noweb-2.12rb/src/shell/notangle.nw
--- noweb-2.11b/src/shell/notangle.nw	1998-08-17 00:13:05.000000000 +0000
+++ noweb-2.12rb/src/shell/notangle.nw	2012-02-21 18:37:03.000000000 +0000
@@ -8,6 +8,7 @@
 # $Name:  $
 LIB=|LIBDIR|
 markup=$LIB/markup opt= arg= markopt= filters=
+getinput=
 while [ $# -gt 0 ]; do
 	case $1 in
         -ml|-m3|-awk|-icn|-icon|-pascal|-c|-c++|-f77|-f90|-tex|-w[0-9][0-9]*) ;; 
@@ -19,6 +20,7 @@
 	-markup) markup="$2" ; shift ;;
 	-)   arg="$arg '$1'" ;;
 	-L*) opt="$opt -t '$1'" ; markopt="$markopt -t" ;;
+	-R*) opt="$opt $1"; getinput="<${$1:2} ";;
 	-*)  opt="$opt '$1'" ;;
 	*)   arg="$arg '$1'" ;;
 	esac
@@ -26,5 +28,5 @@
 done
 PATH="$PATH:$LIB" 
 export PATH
-eval "$markup $markopt $arg | $filters $LIB/nt $opt "'; rc=$?'
+eval "$getinput $markup $markopt $arg | $filters $LIB/nt $opt "'; rc=$?'
 exit $rc
diff -Naur noweb-2.11b/src/xdoc/docdate.nw noweb-2.12rb/src/xdoc/docdate.nw
--- noweb-2.11b/src/xdoc/docdate.nw	2006-06-12 21:03:53.000000000 +0000
+++ noweb-2.12rb/src/xdoc/docdate.nw	2012-02-22 00:56:01.000000000 +0000
@@ -1,13 +1,15 @@
 <<noweb documentation date>>=
-3/28/2001
+2/21/2012
 <<AUTHOR section>>=
 .SH VERSION
 This man page is from 
 .I noweb
-version $Name: v2_11b $.
+version $Name: v2_12rb $.
 .SH AUTHOR
 Norman Ramsey, Harvard University.
 Internet address \f...@eecs.harvard.edu\fP.
 .br
+Python mode added by Ross Boylan <\fbr...@biostat.ucsf.edu\fP>.
+.br
 Noweb home page at \fBhttp://www.eecs.harvard.edu/~nr/noweb\fP.
 
diff -Naur noweb-2.11b/src/xdoc/manpage.nw noweb-2.12rb/src/xdoc/manpage.nw
--- noweb-2.11b/src/xdoc/manpage.nw	2006-05-04 16:07:04.000000000 +0000
+++ noweb-2.12rb/src/xdoc/manpage.nw	2012-02-22 01:09:41.000000000 +0000
@@ -4,7 +4,7 @@
 notangle, noweave, nountangle \- noweb, a literate-programming tool
 .SH SYNOPSIS
 .B notangle
-[\fB\-R\fProotname ...] [\fB\-filter\fP command]
+[\fB\-P\fPwebname] [\fB\-R\fProotname ...] [\fB\-filter\fP command]
 [\fB\-L\fP[format]] [file] ...
 .br
 \fBnountangle\fP 
@@ -167,6 +167,17 @@
 .B \-R
 option is given, expand the chunk named  \fB@<<\fP*\fB@>>\fP.
 .TP
+.B \-P\fIwebname\fR
+Enter Python mode.  Input will come from the file \fIwebname\fR, not
+the standard input, which will be ignored.  This produces tangled
+output design to work with the python \fBdetangle\fR module so that
+errors and strack traces refer to the correct location in
+\fIwebname\fR rather than in the tangled file.  Ordinarily,
+\fIwebname\fR will have a .py extension.  If you use this option
+\-\fBr\fR is mandatory and \-\fBL\fR will be ignored. The \fIname\fR
+for \-\fBR\fR should match the name of the file the command creates,
+though you still need to specify the output file explicitly.
+.TP
 .B \-L\fIformat\fR
 Emit line number indications at chunk boundaries.
 A line number indication identifies the source of the line that follows it.
@@ -176,6 +187,8 @@
 indicates the name of the source file,
 .B "%L"
 indicates the line number of the source file,
+.B "%I"
+indicates the indentation level of this module.
 .B "%N"
 indicates a newline,
 and 
@@ -249,6 +262,20 @@
 transforms documentation chunks into comments, create comments on lines of width \fIn\fP.
 .I notangle
 ignores this option.
+.TP
+.B Python output format
+.B \-P
+creates a file that imports
+.B detangle
+and then executes it to scan the rest of the file.  Regular code
+begins after
+.nf
+#if 0:
+.fi
+It is the tangled python code with a # before every line.  It also includes special #line directives that
+.B detangle
+uses to map to the proper location in the source noweb file.
+
 .SH WEAVING
 <<man page: WEAVING section>>
 .SH INDEXING AND CROSS-REFERENCE
#! /bin/sh /usr/share/dpatch/dpatch-run
## 16_python-derived.dpatch by Ross Boylan <r...@biostat.ucsf.edu>
##
## All lines beginning with `## DP:' are a description of the patch.
## DP: Add python #line handling--derived files
## All changes in this section can be derived from those of the previous patch.
## They are here so that the build system does not require a working noweb
## to build noweb.

@DPATCH@
diff -Naur noweb-2.11b/src/c/main.c noweb-2.12rb/src/c/main.c
--- noweb-2.11b/src/c/main.c	2006-06-12 21:16:23.000000000 +0000
+++ noweb-2.12rb/src/c/main.c	2012-02-22 00:56:39.000000000 +0000
@@ -11,12 +11,20 @@
 #include "modules.h"
 #include "modtrees.h"
 
-#line 25 "main.nw"
+#line 75 "main.nw"
 main(int argc, char **argv) {
     int i;
     char *locformat = "";
     char *Clocformat = "#line %L \"%F\"%N";
+    char *Plocformat = "#line %L %I %F%N";
     int root_options_seen = 0;
+    char *pythonweb = "";
+
+    /* These were in contemplation of switching the file.
+    But the program is part of a pipeline. */
+    FILE *fin = stdin;
+    FILE *fout = stdout;
+    char *fout_name;
 
     tabsize = 0;  /* default for nt is not to use tabs */
     progname = argv[0];
@@ -24,10 +32,10 @@
 
     for (i=1; i<argc; i++) {
         
-#line 53 "main.nw"
+#line 119 "main.nw"
 if (*argv[i]=='-') {
     
-#line 69 "main.nw"
+#line 138 "main.nw"
     switch (argv[i][1]) {
         case 't': /* set tab size or turn off */
             if (isdigit(argv[i][2]))
@@ -37,6 +45,11 @@
             else 
                 errormsg(Error, "%s: ill-formed option %s\n", argv[0], argv[i]);
             break;          
+	case 'P': /* enabled special tangled python mode */
+	     pythonweb = argv[i]+2;
+	     if (*pythonweb == 0)
+	        errormsg(Fatal, "-P requires a file name.  No spaces.\n");
+	     break;
         case 'R': /* change name of root module */
             root_options_seen++;
             break;
@@ -47,26 +60,50 @@
         default:
             errormsg(Warning, "Ignoring unknown option -%s", argv[i]);
      }
-#line 55 "main.nw"
+#line 121 "main.nw"
 } else {
     
-#line 90 "main.nw"
+#line 164 "main.nw"
 errormsg(Warning,
     "I can't handle arguments yet, so I'll just ignore `%s'",argv[i]);
 
-#line 57 "main.nw"
+#line 123 "main.nw"
 }
-#line 37 "main.nw"
+#line 95 "main.nw"
     }
 
-    read_defs(stdin);                        /* read all the definitions */
+    
+#line 170 "main.nw"
+if (*pythonweb && (root_options_seen == 0)){
+  errormsg(Fatal, "-P requires the use of -R");
+  }
+if (*pythonweb && (*locformat != 0)) {
+  errormsg(Warning, "-P ignore your -L option and uses its own.");
+  }
+
+#line 98 "main.nw"
+    read_defs(fin);                        /* read all the definitions */
+#line 101 "main.nw"
+    close(fin);
     apply_each_module(remove_final_newline); /* pretty up the module texts */
     if (root_options_seen == 0)
-      emit_module_named(stdout, "*", locformat);
+      emit_module_named(stdout, "*", locformat, pythonweb);
     else
       for (i=1; i<argc; i++) 
-        if (argv[i][0] == '-' && argv[i][1] == 'R')
-	  emit_module_named(stdout, argv[i]+2, locformat);
+        if (argv[i][0] == '-' && argv[i][1] == 'R') {
+	  fout_name = argv[i]+2;
+	  
+#line 179 "main.nw"
+if (*pythonweb) {
+  locformat = Plocformat;
+  fprintf(fout, "from detangle import detangle\n");
+  fprintf(fout, "detangle('%s', '%s')\n\n", fout_name, pythonweb);
+  fprintf(fout, "#if 0:\n");
+  }	      
+#line 110 "main.nw"
+	  emit_module_named(fout, fout_name, locformat, pythonweb);
+	  close(fout);
+	  }
 
     nowebexit(NULL);
     return errorlevel;  /* slay warning */
diff -Naur noweb-2.11b/src/c/mnt.c noweb-2.12rb/src/c/mnt.c
--- noweb-2.11b/src/c/mnt.c	2006-06-12 21:16:23.000000000 +0000
+++ noweb-2.12rb/src/c/mnt.c	2012-02-21 08:01:23.000000000 +0000
@@ -13,47 +13,47 @@
 #include "columns.h"
 #include "strsave.h"
 
-#line 52 "mnt.nw"
+#line 58 "mnt.nw"
 void add_uses_to_usecounts(Module mp);
 void emit_if_unused_and_conforming(Module mp);
-#line 86 "mnt.nw"
-static void emitfile(char *modname);
-#line 180 "mnt.nw"
+#line 92 "mnt.nw"
+static void emitfile(char *modname, char *pythonweb);
+#line 186 "mnt.nw"
 #ifdef TEMPNAM
 extern char *tempnam (const char *dir, const char *pfx);        /* temp file in dir */
 #else
 #define tempnam(DIR,PFX) (strsave(tmpnam(NULL)))
 #endif
 
-#line 25 "mnt.nw"
+#line 31 "mnt.nw"
 #define Clocformat "#line %L \"%F\"%N"
 static char *locformat = Clocformat;
+static char *pythonweb = "";  /* for future expansion */
 
 main(int argc, char **argv) {
     int i;
-
     tabsize = 0;  /* default for nt is not to use tabs */
 
     progname = argv[0];
     finalstage = 1;
     
-#line 46 "mnt.nw"
+#line 52 "mnt.nw"
 read_defs(stdin);
 apply_each_module(remove_final_newline);
-#line 36 "mnt.nw"
+#line 42 "mnt.nw"
     for (i=1; i<argc; i++) 
       switch (*argv[i]) {
         case '-': 
-#line 152 "mnt.nw"
+#line 158 "mnt.nw"
     switch (*++argv[i]) {
         case 'a':
             if (strcmp(argv[i], "all"))
                 errormsg(Warning, "Ignoring unknown option -%s", argv[i]);
             else {
-#line 49 "mnt.nw"
+#line 55 "mnt.nw"
 apply_each_module(add_uses_to_usecounts);
 apply_each_module(emit_if_unused_and_conforming);
-#line 156 "mnt.nw"
+#line 162 "mnt.nw"
                                                     }
             break;
         case 't': /* set tab size or turn off */
@@ -71,14 +71,14 @@
         default:
             errormsg(Warning, "Ignoring unknown option -%s", argv[i]);
      }
-#line 38 "mnt.nw"
+#line 44 "mnt.nw"
                                                                    break;
-        default:  emitfile(argv[i]);                               break;
+        default:  emitfile(argv[i], pythonweb);                    break;
       }
     nowebexit(NULL);
     return errorlevel;          /* slay warning */
 }
-#line 55 "mnt.nw"
+#line 61 "mnt.nw"
 void add_uses_to_usecounts(Module mp) {
     Module used;
     struct modpart *p;
@@ -89,7 +89,7 @@
                 used->usecount++;
         }
 }
-#line 71 "mnt.nw"
+#line 77 "mnt.nw"
 void emit_if_unused_and_conforming(Module mp) {
     char *index;
     if (mp->usecount == 0 && strpbrk(mp->name, " \n\t\v\r\f") == NULL)
@@ -99,19 +99,19 @@
 	        errormsg(Error, "<<*>> is not a good chunk name for noweb; "
 	                        "use notangle instead");
 	    else
-	        emitfile(mp->name);
+	        emitfile(mp->name, pythonweb);
         else 
             errormsg(Error, "<<%s>> cannot be an output chunk; "
                             "it contains a metacharacter", mp->name);
 }
-#line 88 "mnt.nw"
-static void emitfile(char *modname) { 
+#line 94 "mnt.nw"
+static void emitfile(char *modname, char *pythonweb) { 
   Module root = lookup(modname);
   char *tempname = tempnam(".", 0);
   FILE *fp;
   char *lfmt, *filename;
   
-#line 108 "mnt.nw"
+#line 114 "mnt.nw"
 { int n = strlen(modname) - 1;
   if (n >= 0 && modname[n] == '*') {
     lfmt = locformat;
@@ -122,25 +122,25 @@
     filename = modname;
   }
 }
-#line 94 "mnt.nw"
+#line 100 "mnt.nw"
   
-#line 147 "mnt.nw"
+#line 153 "mnt.nw"
 if (root == NULL) {
   errormsg(Error, "Chunk <<%s>> is undefined", filename);
   return;
 }
-#line 95 "mnt.nw"
+#line 101 "mnt.nw"
   fp = fopen(tempname, "w");
   if (fp == NULL) errormsg(Fatal, "Can't open temporary file %s", tempname);
   
-#line 119 "mnt.nw"
+#line 125 "mnt.nw"
 resetloc();
-(void) expand(root, 0, 0, 0, lfmt, fp);
+(void) expand(root, 0, 0, 0, lfmt, pythonweb, fp);
 putc('\n', fp);
 fclose(fp);
-#line 98 "mnt.nw"
+#line 104 "mnt.nw"
   
-#line 125 "mnt.nw"
+#line 131 "mnt.nw"
 { FILE *dest, *tmp;
   dest = fopen(filename, "r");
   if (dest != NULL) {
@@ -159,23 +159,23 @@
     }
   }
 }
-#line 99 "mnt.nw"
+#line 105 "mnt.nw"
   remove(filename);
   if (rename(tempname, filename) != 0) { /* different file systems? (may have to copy) */
     FILE *fp = fopen(filename, "w");
     if (fp == NULL) {remove(tempname); 
-#line 144 "mnt.nw"
+#line 150 "mnt.nw"
 errormsg(Error, "Can't open output file %s", filename);
 return;
-#line 102 "mnt.nw"
+#line 108 "mnt.nw"
                                                                                      }
     
-#line 119 "mnt.nw"
+#line 125 "mnt.nw"
 resetloc();
-(void) expand(root, 0, 0, 0, lfmt, fp);
+(void) expand(root, 0, 0, 0, lfmt, pythonweb, fp);
 putc('\n', fp);
 fclose(fp);
-#line 104 "mnt.nw"
+#line 110 "mnt.nw"
     remove(tempname);
   }
 }
diff -Naur noweb-2.11b/src/c/modules.c noweb-2.12rb/src/c/modules.c
--- noweb-2.11b/src/c/modules.c	2006-06-12 21:16:23.000000000 +0000
+++ noweb-2.12rb/src/c/modules.c	2012-02-21 23:50:43.000000000 +0000
@@ -17,7 +17,7 @@
 
 static
 void append(Module mp, struct modpart *p);
-#line 227 "modules.nw"
+#line 241 "modules.nw"
 static int seekcycle(Module mp, Parent parent);
 #line 132 "modules.nw"
 static char *lastfilename = 0;
@@ -74,19 +74,19 @@
 }
 #line 145 "modules.nw"
 int expand (Module mp, int indent, int partial_distance, Parent parent,  
-            char *locformat, FILE *out) {
+            char *locformat, char *pythonweb, FILE *out) {
     struct modpart *p;
     Module newmod;
     int error=Normal;
     struct parent thismodule; /* the value only matters when we're expanding a module */
 
     
-#line 219 "modules.nw"
+#line 233 "modules.nw"
 thismodule.this = mp;
 thismodule.parent = parent;
 #line 153 "modules.nw"
     
-#line 222 "modules.nw"
+#line 236 "modules.nw"
 if (seekcycle(mp, parent)) {
     errormsg(Error, "<<%s>>", mp->name);
     return Error;
@@ -96,22 +96,31 @@
     for (p=mp->head; p!=NULL; p=p->next) {
         switch (p->ptype) {
             case STRING:  
-#line 176 "modules.nw"
+#line 179 "modules.nw"
 if (*(p->contents) != '\0') {
-    if (*locformat) {
-        if (printloc(out,locformat,p->loc,partial_distance) && (p != mp->head))
-              indent_for(partial_distance, out);
-    } else if (partial_distance == 0) {
-        indent_for(indent, out);
-        partial_distance = indent;
+    if (*pythonweb) {
+      printloc(out, locformat,p->loc,partial_distance);
+      fputc('#', out);
+      indent_for(indent, out);
+      partial_distance = indent;
+      fprintf(out, "%s",p->contents);
+    }
+    else {
+      if (*locformat) {
+	  if (printloc(out,locformat,p->loc,partial_distance) && (p != mp->head))
+		indent_for(partial_distance, out);
+      } else if (partial_distance == 0) {
+	  indent_for(indent, out);
+	  partial_distance = indent;
+      }
+      fprintf(out,"%s",p->contents);
     }
-    fprintf(out,"%s",p->contents);
     partial_distance = limitcolumn(p->contents, partial_distance);
 }
 #line 157 "modules.nw"
                                             ;  break;
             case MODULE:  
-#line 201 "modules.nw"
+#line 215 "modules.nw"
 newmod = lookup(p->contents);
 if (newmod==NULL) {
     errormsg (Error, "undefined chunk name: <<%s>>", p->contents);
@@ -123,7 +132,7 @@
         partial_distance = indent;
     }
     retcode = expand (newmod, partial_distance, partial_distance,
-                      &thismodule, locformat, out);
+                      &thismodule, locformat, pythonweb, out);
     if (retcode > error) error = retcode;
 }
 partial_distance = limitcolumn(p->contents, partial_distance + 2) + 2; 
@@ -131,7 +140,9 @@
 #line 158 "modules.nw"
                                              ; break;
             case NEWLINE: 
-#line 188 "modules.nw"
+#line 200 "modules.nw"
+if (*pythonweb && partial_distance==0)
+   putc('#', out);
 partial_distance = 0;
 putc('\n', out);
 lastlineno++;
@@ -142,7 +153,7 @@
     }
     return error;
 }
-#line 229 "modules.nw"
+#line 243 "modules.nw"
 static int seekcycle(Module mp, Parent parent) {
     if (parent == NULL) {
         return 0;
@@ -155,14 +166,14 @@
         return 0;
     }
 }
-#line 251 "modules.nw"
+#line 265 "modules.nw"
 int printloc(FILE *fp, char *fmt, Location loc, int partial) {
     char *p;
     if (*fmt
     && (loc.filename!=lastfilename || lastlineno != loc.lineno)) {
         if (partial) putc('\n',fp);
         
-#line 263 "modules.nw"
+#line 277 "modules.nw"
 for (p = fmt; *p; p++) {
     if (*p == '%') {
         switch (*++p) {
@@ -170,6 +181,7 @@
             case 'N': putc('\n', fp);                            break;
             case 'F': fprintf(fp, "%s", loc.filename);           break;
             case 'L': fprintf(fp, "%d", loc.lineno);             break;
+	    case 'I': fprintf(fp, "%d", partial);                break;
             case '-': case '+': 
                         if (isdigit(p[1]) && p[2] == 'L') {
                           fprintf(fp, "%d", *p == '+' ? loc.lineno + (p[1] - '0')
@@ -177,35 +189,35 @@
                           p += 2;
                         } else
                           
-#line 283 "modules.nw"
+#line 298 "modules.nw"
 { static int complained = 0;
   if (!complained) {
     errormsg(Error,"Bad format sequence ``%%%c'' in -L%s",*p,fmt);
     complained = 1;
   }
 }
-#line 277 "modules.nw"
+#line 292 "modules.nw"
                       break;            
             default:  
-#line 283 "modules.nw"
+#line 298 "modules.nw"
 { static int complained = 0;
   if (!complained) {
     errormsg(Error,"Bad format sequence ``%%%c'' in -L%s",*p,fmt);
     complained = 1;
   }
 }
-#line 278 "modules.nw"
+#line 293 "modules.nw"
                                                                 break;
         }
     } else putc(*p, fp);
 }
-#line 257 "modules.nw"
+#line 271 "modules.nw"
         lastfilename = loc.filename;
         lastlineno = loc.lineno;
         return 1;
     } else return 0;
 }
-#line 309 "modules.nw"
+#line 324 "modules.nw"
 void remove_final_newline (Module mp) {
         /* remove trailing newline that must be in module */
     if (mp->tail==NULL) /* module has no text */
diff -Naur noweb-2.11b/src/c/modules.h noweb-2.12rb/src/c/modules.h
--- noweb-2.11b/src/c/modules.h	2006-06-12 21:16:23.000000000 +0000
+++ noweb-2.12rb/src/c/modules.h	2012-02-21 23:50:42.000000000 +0000
@@ -29,7 +29,7 @@
 } *Parent;
 
 int expand (Module mp, int indent, int partial_distance, Parent parent, 
-            char *locformat, FILE *out);
+            char *locformat, char *pythonweb, FILE *out);
         /* expand a module, writing to file out */
 void resetloc(void);
 int printloc(FILE *fp, char *fmt, Location loc, int partial);
diff -Naur noweb-2.11b/src/c/notangle.c noweb-2.12rb/src/c/notangle.c
--- noweb-2.11b/src/c/notangle.c	2006-06-12 21:16:23.000000000 +0000
+++ noweb-2.12rb/src/c/notangle.c	2012-02-21 06:50:06.000000000 +0000
@@ -22,7 +22,7 @@
 void insist(char *line, char *keyword, char *msg);
 
 #line 37 "notangle.nw"
-void emit_module_named (FILE *out, char *rootname, char *locformat) {
+void emit_module_named (FILE *out, char *rootname, char *locformat, char *pythonweb) {
     Module root = NULL; /* ptr to root module */
 
     root = lookup(rootname);
@@ -33,7 +33,7 @@
     return;
 }
 #line 42 "notangle.nw"
-    (void) expand(root,0,0,0,locformat,out);
+    (void) expand(root,0,0,0,locformat, pythonweb, out);
     putc('\n',out);                     /* make output end with newline */
 }
 #line 56 "notangle.nw"
diff -Naur noweb-2.11b/src/c/notangle.h noweb-2.12rb/src/c/notangle.h
--- noweb-2.11b/src/c/notangle.h	2006-06-12 21:16:23.000000000 +0000
+++ noweb-2.12rb/src/c/notangle.h	2012-02-21 06:50:06.000000000 +0000
@@ -1,2 +1,2 @@
-void emit_module_named (FILE *out, char *rootname, char *locformat);
+void emit_module_named (FILE *out, char *rootname, char *locformat, char *pythonweb);
 void read_defs(FILE *in);              /* read module definitions */
diff -Naur noweb-2.11b/src/shell/notangle noweb-2.12rb/src/shell/notangle
--- noweb-2.11b/src/shell/notangle	2006-06-12 21:16:23.000000000 +0000
+++ noweb-2.12rb/src/shell/notangle	2012-02-21 20:30:20.000000000 +0000
@@ -5,23 +5,32 @@
 # $Name:  $
 LIB=|LIBDIR|
 markup=$LIB/markup opt= arg= markopt= filters=
+getinput=
 while [ $# -gt 0 ]; do
-        case $1 in
+	case $1 in
         -ml|-m3|-awk|-icn|-icon|-pascal|-c|-c++|-f77|-f90|-tex|-w[0-9][0-9]*) ;; 
-                                        # deliberately ignore nountangle args
-        -t)  ;; # this is default
+					# deliberately ignore nountangle args
+	-t)  ;; # this is default
         -t*) markopt="$markopt -t" ; opt="$opt '$1'" ;;
-                # copy tabs at markup, use width given in notangle
-        -filter) filters="$filters $2 | " ; shift ;;
-        -markup) markup="$2" ; shift ;;
-        -)   arg="$arg '$1'" ;;
-        -L*) opt="$opt -t '$1'" ; markopt="$markopt -t" ;;
-        -*)  opt="$opt '$1'" ;;
-        *)   arg="$arg '$1'" ;;
-        esac
-        shift
+		# copy tabs at markup, use width given in notangle
+	-filter) filters="$filters $2 | " ; shift ;;
+	-markup) markup="$2" ; shift ;;
+	-)   arg="$arg '$1'" ;;
+	-L*) opt="$opt -t '$1'" ; markopt="$markopt -t" ;;
+	-P*) opt="$opt $1"; 
+	     pfn=${1#-P};
+             if [ -z ${pfn##/*} ]; then
+		 getinp="<$pfn";
+	     else
+		 getinput="<${PWD}/${pfn#-P} ";
+	     fi;
+	     ;;
+	-*)  opt="$opt '$1'" ;;
+	*)   arg="$arg '$1'" ;;
+	esac
+	shift
 done
 PATH="$PATH:$LIB" 
 export PATH
-eval "$markup $markopt $arg | $filters $LIB/nt $opt "'; rc=$?'
+eval "$getinput $markup $markopt $arg | $filters $LIB/nt $opt "'; rc=$?'
 exit $rc
diff -Naur noweb-2.11b/src/xdoc/htmltoc.1 noweb-2.12rb/src/xdoc/htmltoc.1
--- noweb-2.11b/src/xdoc/htmltoc.1	2006-06-12 21:16:23.000000000 +0000
+++ noweb-2.12rb/src/xdoc/htmltoc.1	2012-02-22 00:56:41.000000000 +0000
@@ -1,4 +1,4 @@
-.TH NOWEB 1 "local 3/28/2001"
+.TH NOWEB 1 "local 2/21/2012"
 .SH NAME
 htmltoc \- add table of contents to HTML document
 .SH SYNOPSIS
@@ -31,11 +31,13 @@
 .SH VERSION
 This man page is from 
 .I noweb
-version 2.11b.
+version 2.12rb.
 .SH AUTHOR
 Norman Ramsey, Harvard University.
 Internet address \f...@eecs.harvard.edu\fP.
 .br
+Python mode added by Ross Boylan <\fbr...@biostat.ucsf.edu\fP>.
+.br
 Noweb home page at \fBhttp://www.eecs.harvard.edu/~nr/noweb\fP.
 
 .PP
diff -Naur noweb-2.11b/src/xdoc/nodefs.1 noweb-2.12rb/src/xdoc/nodefs.1
--- noweb-2.11b/src/xdoc/nodefs.1	2006-06-12 21:16:23.000000000 +0000
+++ noweb-2.12rb/src/xdoc/nodefs.1	2012-02-22 00:56:41.000000000 +0000
@@ -1,4 +1,4 @@
-.TH NOWEB 1 "local 3/28/2001"
+.TH NOWEB 1 "local 2/21/2012"
 .SH NAME
 nodefs \- find definitions in noweb file
 .SH SYNOPSIS
@@ -45,10 +45,12 @@
 .SH VERSION
 This man page is from 
 .I noweb
-version 2.11b.
+version 2.12rb.
 .SH AUTHOR
 Norman Ramsey, Harvard University.
 Internet address \f...@eecs.harvard.edu\fP.
 .br
+Python mode added by Ross Boylan <\fbr...@biostat.ucsf.edu\fP>.
+.br
 Noweb home page at \fBhttp://www.eecs.harvard.edu/~nr/noweb\fP.
 
diff -Naur noweb-2.11b/src/xdoc/noindex.1 noweb-2.12rb/src/xdoc/noindex.1
--- noweb-2.11b/src/xdoc/noindex.1	2006-06-12 21:16:23.000000000 +0000
+++ noweb-2.12rb/src/xdoc/noindex.1	2012-02-22 00:56:41.000000000 +0000
@@ -1,4 +1,4 @@
-.TH NOWEB 1 "local 3/28/2001"
+.TH NOWEB 1 "local 2/21/2012"
 .SH NAME
 noindex \- build external index for noweb document
 .SH SYNOPSIS
@@ -162,10 +162,12 @@
 .SH VERSION
 This man page is from 
 .I noweb
-version 2.11b.
+version 2.12rb.
 .SH AUTHOR
 Norman Ramsey, Harvard University.
 Internet address \f...@eecs.harvard.edu\fP.
 .br
+Python mode added by Ross Boylan <\fbr...@biostat.ucsf.edu\fP>.
+.br
 Noweb home page at \fBhttp://www.eecs.harvard.edu/~nr/noweb\fP.
 
diff -Naur noweb-2.11b/src/xdoc/noroff.1 noweb-2.12rb/src/xdoc/noroff.1
--- noweb-2.11b/src/xdoc/noroff.1	2006-06-12 21:16:23.000000000 +0000
+++ noweb-2.12rb/src/xdoc/noroff.1	2012-02-22 00:56:41.000000000 +0000
@@ -1,4 +1,4 @@
-.TH NOWEB 1 "local 3/28/2001"
+.TH NOWEB 1 "local 2/21/2012"
 .SH NAME
 noroff \- format woven \fItroff\fP documentation
 .SH SYNOPSIS
@@ -41,11 +41,13 @@
 .SH VERSION
 This man page is from 
 .I noweb
-version 2.11b.
+version 2.12rb.
 .SH AUTHOR
 Norman Ramsey, Harvard University.
 Internet address \f...@eecs.harvard.edu\fP.
 .br
+Python mode added by Ross Boylan <\fbr...@biostat.ucsf.edu\fP>.
+.br
 Noweb home page at \fBhttp://www.eecs.harvard.edu/~nr/noweb\fP.
 
 .PP
diff -Naur noweb-2.11b/src/xdoc/noroots.1 noweb-2.12rb/src/xdoc/noroots.1
--- noweb-2.11b/src/xdoc/noroots.1	2006-06-12 21:16:23.000000000 +0000
+++ noweb-2.12rb/src/xdoc/noroots.1	2012-02-22 00:56:41.000000000 +0000
@@ -1,4 +1,4 @@
-.TH NOWEB 1 "local 3/28/2001"
+.TH NOWEB 1 "local 2/21/2012"
 .SH NAME
 noroots \- print roots of a noweb file
 .SH SYNOPSIS
@@ -21,10 +21,12 @@
 .SH VERSION
 This man page is from 
 .I noweb
-version 2.11b.
+version 2.12rb.
 .SH AUTHOR
 Norman Ramsey, Harvard University.
 Internet address \f...@eecs.harvard.edu\fP.
 .br
+Python mode added by Ross Boylan <\fbr...@biostat.ucsf.edu\fP>.
+.br
 Noweb home page at \fBhttp://www.eecs.harvard.edu/~nr/noweb\fP.
 
diff -Naur noweb-2.11b/src/xdoc/nowebfilters.7 noweb-2.12rb/src/xdoc/nowebfilters.7
--- noweb-2.11b/src/xdoc/nowebfilters.7	2006-06-12 21:16:23.000000000 +0000
+++ noweb-2.12rb/src/xdoc/nowebfilters.7	2012-02-22 00:56:41.000000000 +0000
@@ -1,4 +1,4 @@
-.TH NOWEB 7 "local 3/28/2001"
+.TH NOWEB 7 "local 2/21/2012"
 .SH NAME
 nowebfilters \- filters and parsers for use with noweb
 .SH SYNOPSIS
@@ -199,10 +199,12 @@
 .SH VERSION
 This man page is from 
 .I noweb
-version 2.11b.
+version 2.12rb.
 .SH AUTHOR
 Norman Ramsey, Harvard University.
 Internet address \f...@eecs.harvard.edu\fP.
 .br
+Python mode added by Ross Boylan <\fbr...@biostat.ucsf.edu\fP>.
+.br
 Noweb home page at \fBhttp://www.eecs.harvard.edu/~nr/noweb\fP.
 
diff -Naur noweb-2.11b/src/xdoc/sl2h.1 noweb-2.12rb/src/xdoc/sl2h.1
--- noweb-2.11b/src/xdoc/sl2h.1	2006-06-12 21:16:23.000000000 +0000
+++ noweb-2.12rb/src/xdoc/sl2h.1	2012-02-22 00:56:41.000000000 +0000
@@ -1,4 +1,4 @@
-.TH NOWEB 1 "local 3/28/2001"
+.TH NOWEB 1 "local 2/21/2012"
 .SH NAME
 sl2h \- simple latex to HTML converter
 .SH SYNOPSIS
@@ -28,10 +28,12 @@
 .SH VERSION
 This man page is from 
 .I noweb
-version 2.11b.
+version 2.12rb.
 .SH AUTHOR
 Norman Ramsey, Harvard University.
 Internet address \f...@eecs.harvard.edu\fP.
 .br
+Python mode added by Ross Boylan <\fbr...@biostat.ucsf.edu\fP>.
+.br
 Noweb home page at \fBhttp://www.eecs.harvard.edu/~nr/noweb\fP.

diff -Naur noweb-2.11b/src/xdoc/notangle.1 noweb-2.12rb/src/xdoc/notangle.1
--- noweb-2.11b/src/xdoc/notangle.1	2012-02-27 21:19:19.000000000 +0000
+++ noweb-2.12rb/src/xdoc/notangle.1	2012-02-27 21:26:56.000000000 +0000
@@ -1,9 +1,9 @@
-.TH NOWEB 1 "local 3/28/2001"
+.TH NOWEB 1 "local 2/21/2012"
 .SH NAME
 notangle, noweave, nountangle \- noweb, a literate-programming tool
 .SH SYNOPSIS
 .B notangle
-[\fB\-R\fProotname ...] [\fB\-filter\fP command]
+[\fB\-P\fPwebname] [\fB\-R\fProotname ...] [\fB\-filter\fP command]
 [\fB\-L\fP[format]] [file] ...
 .br
 \fBnountangle\fP 
@@ -132,6 +132,17 @@
 .B \-R
 option is given, expand the chunk named  \fB<<\fP*\fB>>\fP.
 .TP
+.B \-P\fIwebname\fR
+Enter Python mode.  Input will come from the file \fIwebname\fR, not
+the standard input, which will be ignored.  This produces tangled
+output design to work with the python \fBdetangle\fR module so that
+errors and strack traces refer to the correct location in
+\fIwebname\fR rather than in the tangled file.  Ordinarily,
+\fIwebname\fR will have a .py extension.  If you use this option
+\-\fBr\fR is mandatory and \-\fBL\fR will be ignored. The \fIname\fR
+for \-\fBR\fR should match the name of the file the command creates,
+though you still need to specify the output file explicitly.
+.TP
 .B \-L\fIformat\fR
 Emit line number indications at chunk boundaries.
 A line number indication identifies the source of the line that follows it.
@@ -141,6 +152,8 @@
 indicates the name of the source file,
 .B "%L"
 indicates the line number of the source file,
+.B "%I"
+indicates the indentation level of this module.
 .B "%N"
 indicates a newline,
 and 
@@ -228,6 +241,20 @@
 transforms documentation chunks into comments, create comments on lines of width \fIn\fP.
 .I notangle
 ignores this option.
+.TP
+.B Python output format
+.B \-P
+creates a file that imports
+.B detangle
+and then executes it to scan the rest of the file.  Regular code
+begins after
+.nf
+#if 0:
+.fi
+It is the tangled python code with a # before every line.  It also includes special #line directives that
+.B detangle
+uses to map to the proper location in the source noweb file.
+
 .SH WEAVING
 Output from \fInoweave\fP can
 be used in \fITeX\fP documents that 
@@ -632,10 +657,12 @@
 .SH VERSION
 This man page is from 
 .I noweb
-version 2.11b.
+version 2.12rb.
 .SH AUTHOR
 Norman Ramsey, Harvard University.
 Internet address \f...@eecs.harvard.edu\fP.
 .br
+Python mode added by Ross Boylan <\fbr...@biostat.ucsf.edu\fP>.
+.br
 Noweb home page at \fBhttp://www.eecs.harvard.edu/~nr/noweb\fP.
 
#! /usr/bin/python

# File: detangle.py
# Author: Ross Boylan
# Created: 2012-02-20
#
# (c) 2012
# Released under GPL 3 or later.

# Compile and execute a tangled file so that the source
# appears to be from the original file.
#
# Intended Use in a file tangled.py:
# from detangle import detangle
# detangle("tangled.py", "original")
# if 0:
#  def foo(bar):
#     pass
#  #line 45 6
#        class X:
#          "some comment"
#
#
# So the original code is indented one space and disabled by an if 0:
# It may have #line directives interspersed.
# #line lineno [indent [filename]]
# Makes it appear as if following lines are from <filename> (defaults to the 
# 2nd argument of detangle) starting at <lineno>, which will be the apparent 
# line number of the next physical line.  <indent> (default 0) implies the
# following code has been indented relative to the original source.

import pdb

import ast
from bisect import bisect
import re

def detangle(thisfile, originalfile):
    """Compile the code in thisfile <String> making it appear to have
    come from originalfile <String> with line and column position as
    indicated by #line directives"""
    text, linemap = scancode(thisfile, originalfile)
    try:
        tree = ast.parse(text, filename=originalfile)
    except SyntaxError as e:
        raise linemap.revised_exception(e)
    tree = linemap.rewrite_ast(tree)
    code = compile(tree, originalfile, "exec")
    exec code

def scancode(thisfile, originalfile):
    "locate code in thisfile and return text and a line map"
    # skip to start of quoted code
    foundit = False
    map = Linemap(originalfile)
    text = ""
    lineRE = re.compile(r"^#line (?P<line>\d+)( (?P<indent>\d+)( 
(?P<file>.*))?)?(\n)?$")
    with open(thisfile, "r") as inf:
        # locate start of quoted code
        for line in inf:
            if line.startswith("#if 0:"):
                foundit = True
                break
        if not foundit:
            raise Exception("No quoted code in %s"%thisfile)
        lineno = 0
        for line in inf:
            lineno += 1
            m = lineRE.match(line)
            if m:
                d = m.groupdict()
                original_lineno = int(d["line"])
                indent = d["indent"]
                indent = int(indent) if indent else 0
                map.add_map(lineno, original_lineno, indent, d["file"])
                lineno -= 1
            else:
                text = ''.join((text, line[1:]))
    return text, map
                
class Linemap:
    "maps from tangle source to original source"
    def __init__(self, original_file):
        "sets the default original file name"
        self.default_original = original_file
        # self.map[i] is for tangled lines starting at 
        # self.tangled_lines[i]
        self.map = []
        self.tangled_lines = []
        self.last_tangled_lineno = -1
        self.last_tangled_map = None

    def add_map(self, tangled_lineno, original_lineno, indent=0, 
original_file=None):
        """add a remapping that takes effect at tangled_lineno.
        Assumess calling with increaseing tangled_lineno"""
        self.tangled_lines.append(tangled_lineno)
        self.map.append((original_lineno, indent, original_file))

    def map_for(self, tangled_lineno, tangled_column=None):
        "return the map object for the indicated line"
        if tangled_lineno == self.last_tangled_lineno:
            return self.last_tangled_map
        i = bisect(self.tangled_lines, tangled_lineno)
        if i > 0:
            i -= 1
            x = self.map[i]
            file = x[2] if x[2] else self.default_original
            line = x[0]+tangled_lineno-self.tangled_lines[i]
            column = tangled_column-x[1] if tangled_column else None
        else:
            "This really should not happen"
            line = tangled_lineno
            column = tangled_column
            file = "<neverland>"
        self.last_tangled_lineno = tangled_lineno
        self.last_tangled_map = line, column, file
        return self.last_tangled_map

    def revised_exception(self, e):
        line, column, file = self.map_for(e.lineno, e.offset)
        e.lineno = line
        if column:
            indent = e.offset - column
            e.offset = column
            e.text = e.text[indent:]
        e.file = file
        print "Changed except to bet at line %i in %s"%(e.lineno, e.file)
        return e

    def rewrite_ast(self, tree):
        for node in ast.walk(tree):
            if hasattr(node, 'lineno'):
                hasCol = hasattr(node, 'column_offset')
                tangled_column = node.column_offset if hasCol else None
                line, column, file = self.map_for(node.lineno, tangled_column)
                if line == 29: pdb.set_trace()
                node.lineno = line
                if column and hasattr(node, 'column_offset'):
                    node.column_offset = column
        return tree

Reply via email to