1
0
mirror of https://github.com/myhdl/myhdl.git synced 2025-01-24 21:52:56 +08:00
This commit is contained in:
jand 2003-05-20 20:29:52 +00:00
parent 63ae356ec3
commit f4c2a75b76
73 changed files with 16760 additions and 53 deletions

View File

@ -1,18 +1,397 @@
NAME = MyHDL
# Makefile for Python documentation
# ---------------------------------
#
# See also the README file.
#
# This is a bit of a mess. The documents are identified by short names:
# api -- Python/C API Reference Manual
# doc -- Documenting Python
# ext -- Extending and Embedding the Python Interpreter
# lib -- Library Reference Manual
# mac -- Macintosh Library Modules
# ref -- Python Reference Manual
# tut -- Python Tutorial
# inst -- Installing Python Modules
# dist -- Distributing Python Modules
#
# The LaTeX sources for each of these documents are in subdirectories
# with the three-letter designations above as the directory names.
#
# The main target creates HTML for each of the documents. You can
# also do "make lib" (etc.) to create the HTML versions of individual
# documents.
#
# The document classes and styles are in the texinputs/ directory.
# These define a number of macros that are similar in name and intent
# as macros in Texinfo (e.g. \code{...} and \emph{...}), as well as a
# number of environments for formatting function and data definitions.
# Documentation for the macros is included in "Documenting Python"; see
# http://www.python.org/doc/current/doc/doc.html, or the sources for
# this document in the doc/ directory.
#
# Everything is processed by LaTeX. See the file `README' for more
# information on the tools needed for processing.
#
# There's a problem with generating the index which has been solved by
# a sed command applied to the index file. The shell script fix_hack
# does this (the Makefile takes care of calling it).
#
# Additional targets attempt to convert selected LaTeX sources to
# various other formats. These are generally site specific because
# the tools used are all but universal. These targets are:
#
# ps -- convert all documents from LaTeX to PostScript
# pdf -- convert all documents from LaTeX to the
# Portable Document Format
#
# See the README file for more information on these targets.
#
# The formatted output is located in subdirectories. For PDF and
# PostScript, look in the paper-$(PAPER)/ directory. For HTML, look in
# the html/ directory. If you want to fix the GNU info process, look
# in the info/ directory; please send patches to python-docs@python.org.
default: $(NAME).dvi $(NAME).ps $(NAME).pdf
# This Makefile only includes information on how to perform builds; for
# dependency information, see Makefile.deps.
#%.pdf: %.ps
# ps2pdf $<; \
# Customization -- you *may* have to edit this
%.ps: %.dvi
dvips -o $@ -t a4 -Ppdf -G0 $<
# You could set this to a4:
PAPER=a4
%.dvi: %.tex
latex $<; \
python tools/toc2bkm.py -c chapter $(NAME).toc
latex $<; \
pdflatex $<
# Ideally, you shouldn't need to edit beyond this point
INFODIR= info
TOOLSDIR= tools
# This is the *documentation* release, and is used to construct the file
# names of the downloadable tarballs.
RELEASE=0.2
PYTHON= python
DVIPS= dvips -N0 -t $(PAPER)
MKDVI= $(PYTHON) ../tools/mkhowto --paper=$(PAPER) --dvi
MKHTML= $(PYTHON) tools/mkhowto --html --about html/myabout.dat \
--iconserver ../icons --favicon ../icons/pyfav.gif \
--address $(MYHDLDOCS) --up-link ../../Overview.html \
--up-title "Overview" \
--dvips-safe
MKPDF= $(PYTHON) ../tools/mkhowto -l --paper=$(PAPER) --pdf
MKPS= $(PYTHON) ../tools/mkhowto --paper=$(PAPER) --ps
BUILDINDEX=$(TOOLSDIR)/buildindex.py
MYHDLDOCS="<i><a href=\"about.html\">About this document</a></i>"
HTMLBASE= file:`pwd`
# The emacs binary used to build the info docs. GNU Emacs 21 is required.
EMACS= emacs
# The end of this should reflect the major/minor version numbers of
# the release:
WHATSNEW=whatsnew23
# what's what
MANDVIFILES= paper-$(PAPER)/MyHDL.dvi
HOWTODVIFILES= paper-$(PAPER)/doc.dvi
MANPDFFILES= paper-$(PAPER)/MyHDL.pdf
HOWTOPDFFILES= paper-$(PAPER)/doc.pdf
MANPSFILES= paper-$(PAPER)/MyHDL.ps
HOWTOPSFILES= paper-$(PAPER)/doc.ps
DVIFILES= $(MANDVIFILES)
PDFFILES= $(MANPDFFILES)
PSFILES= $(MANPSFILES)
HTMLCSSFILES=html/manual/MyHDL.css
ALLCSSFILES=$(HTMLCSSFILES)
INDEXFILES=html/manual/MyHDL.html
ALLHTMLFILES=$(INDEXFILES)
COMMONPERL= perl/manual.perl perl/python.perl perl/l2hinit.perl
include Makefile.deps
# These must be declared phony since there
# are directories with matching names:
.PHONY: manual
.PHONY: html info
# Main target
default: html
all: html dvi ps pdf
dvi: $(DVIFILES)
pdf: $(PDFFILES)
ps: $(PSFILES)
world: ps pdf html distfiles
# Rules to build PostScript and PDF formats
.SUFFIXES: .dvi .ps
.dvi.ps:
$(DVIPS) -o $@ $<
# Targets for each document:
# Documenting Python
paper-$(PAPER)/doc.dvi: $(DOCFILES)
cd paper-$(PAPER) && $(MKDVI) ../doc/doc.tex
paper-$(PAPER)/doc.pdf: $(DOCFILES)
cd paper-$(PAPER) && $(MKPDF) ../doc/doc.tex
# MyHDL Manual
paper-$(PAPER)/MyHDL.dvi: $(MANUALFILES)
cd paper-$(PAPER) && $(MKDVI) ../manual/MyHDL.tex
paper-$(PAPER)/MyHDL.pdf: $(MANUALFILES)
cd paper-$(PAPER) && $(MKPDF) ../manual/MyHDL.tex
# The remaining part of the Makefile is concerned with various
# conversions, as described above. See also the README file.
info:
cd $(INFODIR) && $(MAKE) EMACS=$(EMACS)
# Targets to convert the manuals to HTML using Nikos Drakos' LaTeX to
# HTML converter. For more info on this program, see
# <URL:http://cbl.leeds.ac.uk/nikos/tex2html/doc/latex2html/latex2html.html>.
# Note that LaTeX2HTML inserts references to an icons directory in
# each page that it generates. I have placed a copy of this directory
# in the distribution to simplify the process of creating a
# self-contained HTML distribution; for this purpose I have also added
# a (trivial) index.html. Change the definition of $ICONSERVER in
# perl/l2hinit.perl to use a different location for the icons directory.
# If you have the standard LaTeX2HTML icons installed, the versions shipped
# with this documentation should be stored in a separate directory and used
# instead. The standard set does *not* include all the icons used in the
# Python documentation.
$(ALLCSSFILES): html/style.css
cp $< $@
$(INDEXFILES): $(COMMONPERL) html/about.dat tools/node2label.pl
html/acks.html: ACKS $(TOOLSDIR)/support.py $(TOOLSDIR)/mkackshtml
$(PYTHON) $(TOOLSDIR)/mkackshtml --address $(PYTHONDOCS) \
--favicon icons/pyfav.gif \
--output html/acks.html <ACKS
# html/index.html is dependent on $(INDEXFILES) since we want the date
# on the front index to be updated whenever any of the child documents
# are updated and boilerplate.tex uses \today as the date. The index
# files are not used to actually generate content.
BOILERPLATE=texinputs/boilerplate.tex
html/index.html: $(INDEXFILES)
html/index.html: html/index.html.in $(BOILERPLATE) tools/rewrite.py
$(PYTHON) tools/rewrite.py $(BOILERPLATE) \
RELEASE=$(RELEASE) WHATSNEW=$(WHATSNEW) \
<$< >$@
html/modindex.html:
touch html/modindex.html
html: $(ALLHTMLFILES) $(HTMLCSSFILES)
doc: html/doc/doc.html html/doc/doc.css
html/doc/doc.html: $(DOCFILES)
$(MKHTML) --dir html/doc doc/doc.tex
manual: html/manual/MyHDL.html html/manual/MyHDL.css
html/manual/MyHDL.html: $(MANUALFILES)
$(MKHTML) --dir html/manual manual/MyHDL.tex
# webchecker needs an extra flag to process the huge index from the libref
WEBCHECKER=$(PYTHON) ../Tools/webchecker/webchecker.py
HTMLBASE= file:`pwd`/html
webcheck: $(ALLHTMLFILES)
$(WEBCHECKER) $(HTMLBASE)/manual/
$(WEBCHECKER) $(HTMLBASE)/doc/
fastwebcheck: $(ALLHTMLFILES)
$(WEBCHECKER) -x $(HTMLBASE)/manual/
$(WEBCHECKER) -x $(HTMLBASE)/doc/
# Release packaging targets:
paper-$(PAPER)/README: $(PSFILES) $(TOOLSDIR)/getpagecounts
cd paper-$(PAPER) && ../$(TOOLSDIR)/getpagecounts -r $(RELEASE) >../$@
info-$(RELEASE).tgz: info
cd $(INFODIR) && tar cf - README python.dir python-*.info* \
| gzip -9 >$@
info-$(RELEASE).tar.bz2: info
cd $(INFODIR) && tar cf - README python.dir python-*.info* \
| bzip2 -9 >$@
latex-$(RELEASE).tgz:
$(PYTHON) $(TOOLSDIR)/mksourcepkg --gzip $(RELEASE)
latex-$(RELEASE).tar.bz2:
$(PYTHON) $(TOOLSDIR)/mksourcepkg --bzip2 $(RELEASE)
latex-$(RELEASE).zip:
rm -f $@
$(PYTHON) $(TOOLSDIR)/mksourcepkg --zip $(RELEASE)
pdf-$(PAPER)-$(RELEASE).tar: $(PDFFILES)
rm -f $@
mkdir Python-Docs-$(RELEASE)
cp paper-$(PAPER)/*.pdf Python-Docs-$(RELEASE)
tar cf $@ Python-Docs-$(RELEASE)
rm -r Python-Docs-$(RELEASE)
pdf-$(PAPER)-$(RELEASE).tgz: pdf-$(PAPER)-$(RELEASE).tar
gzip -9 <$? >$@
pdf-$(PAPER)-$(RELEASE).tar.bz2: pdf-$(PAPER)-$(RELEASE).tar
bzip2 -9 <$? >$@
pdf-$(PAPER)-$(RELEASE).zip: pdf
rm -f $@
mkdir Python-Docs-$(RELEASE)
cp paper-$(PAPER)/*.pdf Python-Docs-$(RELEASE)
zip -q -r -9 $@ Python-Docs-$(RELEASE)
rm -r Python-Docs-$(RELEASE)
postscript-$(PAPER)-$(RELEASE).tar: $(PSFILES) paper-$(PAPER)/README
rm -f $@
mkdir Python-Docs-$(RELEASE)
cp paper-$(PAPER)/*.ps Python-Docs-$(RELEASE)
cp paper-$(PAPER)/README Python-Docs-$(RELEASE)
tar cf $@ Python-Docs-$(RELEASE)
rm -r Python-Docs-$(RELEASE)
postscript-$(PAPER)-$(RELEASE).tar.bz2: postscript-$(PAPER)-$(RELEASE).tar
bzip2 -9 <$< >$@
postscript-$(PAPER)-$(RELEASE).tgz: postscript-$(PAPER)-$(RELEASE).tar
gzip -9 <$< >$@
postscript-$(PAPER)-$(RELEASE).zip: $(PSFILES) paper-$(PAPER)/README
rm -f $@
mkdir Python-Docs-$(RELEASE)
cp paper-$(PAPER)/*.ps Python-Docs-$(RELEASE)
cp paper-$(PAPER)/README Python-Docs-$(RELEASE)
zip -q -r -9 $@ Python-Docs-$(RELEASE)
rm -r Python-Docs-$(RELEASE)
HTMLPKGFILES=*.html */*.css */*.html */*.gif */*.txt
html-$(RELEASE).tar: $(ALLHTMLFILES) $(HTMLCSSFILES)
mkdir Python-Docs-$(RELEASE)
cd html && tar cf ../temp.tar $(HTMLPKGFILES)
cd Python-Docs-$(RELEASE) && tar xf ../temp.tar
rm temp.tar
tar cf html-$(RELEASE).tar Python-Docs-$(RELEASE)
rm -r Python-Docs-$(RELEASE)
html-$(RELEASE).tgz: html-$(RELEASE).tar
gzip -9 <$? >$@
html-$(RELEASE).tar.bz2: html-$(RELEASE).tar
bzip2 -9 <$? >$@
html-$(RELEASE).zip: $(ALLHTMLFILES) $(HTMLCSSFILES)
rm -f $@
mkdir Python-Docs-$(RELEASE)
cd html && tar cf ../temp.tar $(HTMLPKGFILES)
cd Python-Docs-$(RELEASE) && tar xf ../temp.tar
rm temp.tar
zip -q -r -9 $@ Python-Docs-$(RELEASE)
rm -r Python-Docs-$(RELEASE)
# convenience targets:
tarhtml: html-$(RELEASE).tgz
tarinfo: info-$(RELEASE).tgz
tarps: postscript-$(PAPER)-$(RELEASE).tgz
tarpdf: pdf-$(PAPER)-$(RELEASE).tgz
tarlatex: latex-$(RELEASE).tgz
tarballs: tarpdf tarps tarhtml
ziphtml: html-$(RELEASE).zip
zipps: postscript-$(PAPER)-$(RELEASE).zip
zippdf: pdf-$(PAPER)-$(RELEASE).zip
ziplatex: latex-$(RELEASE).zip
zips: zippdf zipps ziphtml
bziphtml: html-$(RELEASE).tar.bz2
bzipinfo: info-$(RELEASE).tar.bz2
bzipps: postscript-$(PAPER)-$(RELEASE).tar.bz2
bzippdf: pdf-$(PAPER)-$(RELEASE).tar.bz2
bziplatex: latex-$(RELEASE).tar.bz2
bzips: bzippdf bzipps bziphtml
disthtml: tarhtml bziphtml ziphtml
distinfo: tarinfo bzipinfo
distps: tarps bzipps zipps
distpdf: tarpdf bzippdf zippdf
distlatex: tarlatex bziplatex ziplatex
paperdist: distpdf distps
edist: disthtml
distfiles: paperdist edist
$(TOOLSDIR)/mksourcepkg --all $(RELEASE)
$(TOOLSDIR)/mkpkglist >pkglist.html
# Housekeeping targets
# Remove temporary files; all except the following:
# - sources: .tex, .bib, .sty, *.cls
# - useful results: .dvi, .pdf, .ps, .texi, .info
clean:
-rm *.dvi *.log *.toc *.ps *.pdf *.aux *.out
rm -f html-$(RELEASE).tar
cd $(INFODIR) && $(MAKE) clean
# Remove temporaries as well as final products
clobber:
rm -f html-$(RELEASE).tar
rm -f html-$(RELEASE).tgz info-$(RELEASE).tgz
rm -f pdf-$(RELEASE).tgz postscript-$(RELEASE).tgz
rm -f latex-$(RELEASE).tgz html-$(RELEASE).zip
rm -f pdf-$(RELEASE).zip postscript-$(RELEASE).zip
rm -f $(DVIFILES) $(PSFILES) $(PDFFILES)
cd $(INFODIR) && $(MAKE) clobber
rm -f paper-$(PAPER)/*.tex paper-$(PAPER)/*.ind paper-$(PAPER)/*.idx
rm -f paper-$(PAPER)/*.l2h paper-$(PAPER)/*.how paper-$(PAPER)/README
rm -rf html/index.html html/modindex.html html/acks.html
rm -rf html/manual/ html/doc/
realclean distclean: clobber

33
doc/Makefile.deps Normal file
View File

@ -0,0 +1,33 @@
# LaTeX source dependencies.
COMMONSTYLES= texinputs/python.sty \
texinputs/pypaper.sty
INDEXSTYLES=texinputs/python.ist
COMMONTEX= texinputs/copyright.tex \
texinputs/license.tex \
texinputs/boilerplate.tex
MANSTYLES= texinputs/fncychap.sty \
texinputs/manual.cls \
$(COMMONSTYLES)
HOWTOSTYLES= texinputs/howto.cls \
$(COMMONSTYLES)
DOCFILES= $(HOWTOSTYLES) \
texinputs/boilerplate.tex \
texinputs/ltxmarkup.sty \
doc/doc.tex
# LaTeX source files for the MyHDL Manual
MANUALFILES= $(MANSTYLES) $(INDEXSTYLES) $(COMMONTEX) \
manual/MyHDL.tex \
manual/background.tex \
manual/informal.tex \
manual/modeling.tex \
manual/unittest.tex \
manual/cosimulation.tex \
manual/reference.tex

231
doc/README Normal file
View File

@ -0,0 +1,231 @@
Python standard documentation -- in LaTeX
-----------------------------------------
This directory contains the LaTeX sources to the Python documentation
and tools required to support the formatting process. The documents
now require LaTeX2e; LaTeX 2.09 compatibility has been dropped.
If you don't have LaTeX, or if you'd rather not format the
documentation yourself, you can ftp a tar file containing HTML, PDF,
or PostScript versions of all documents. Additional formats may be
available. These should be in the same place where you fetched the
main Python distribution (try <http://www.python.org/> or
<ftp://ftp.python.org/pub/python/>).
The following are the LaTeX source files:
api/*.tex Python/C API Reference Manual
doc/*.tex Documenting Python
ext/*.tex Extending and Embedding the Python Interpreter
lib/*.tex Python Library Reference
mac/*.tex Macintosh Library Modules
ref/*.tex Python Reference Manual
tut/*.tex Python Tutorial
inst/*.tex Installing Python Modules
dist/*.tex Distributing Python Modules
Most use the "manual" document class and "python" package, derived from
the old "myformat.sty" style file. The Macintosh Library Modules
document uses the "howto" document class instead. These contains many
macro definitions useful in documenting Python, and set some style
parameters.
There's a Makefile to call LaTeX and the other utilities in the right
order and the right number of times. By default, it will build the
HTML version of the documnetation, but DVI, PDF, and PostScript can
also be made. To view the generated HTML, point your favorite browser
at the top-level index (html/index.html) after running "make".
The Makefile can also produce DVI files for each document made; to
preview them, use xdvi. PostScript is produced by the same Makefile
target that produces the DVI files. This uses the dvips tool.
Printing depends on local conventions; at our site, we use lpr. For
example:
make paper-letter/lib.ps # create lib.dvi and lib.ps
xdvi paper-letter/lib.dvi # preview lib.dvi
lpr paper-letter/lib.ps # print on default printer
What if I find a bug?
---------------------
First, check that the bug is present in the development version of the
documentation at <http://python.sourceforge.net/devel-docs/>; we may
have already fixed it.
If we haven't, tell us about it. We'd like the documentation to be
complete and accurate, but have limited time. If you discover any
inconsistencies between the documentation and implementation, or just
have suggestions as to how to improve the documentation, let is know!
Specific bugs and patches should be reported using our bug & patch
databases at:
http://sourceforge.net/projects/python
Other suggestions or questions should be sent to the Python
Documentation Team:
python-docs@python.org
Thanks!
What happened to the Macintosh chapter of the Python Library Reference?
-----------------------------------------------------------------------
The directory mac/ contains the LaTeX sources for the "Macintosh
Library Modules" manual; this is built using the standard build
targets, so check the proper output directory for your chosen format
and paper size.
What tools do I need?
---------------------
You need to install Python; some of the scripts used to produce the
documentation are written in Python. You don't need this
documentation to install Python; instructions are included in the
README file in the Python distribution.
The simplest way to get the rest of the tools in the configuration we
used is to install the teTeX TeX distribution, versions 0.9 or newer.
More information is available on teTeX at <http://www.tug.org/tetex/>.
This is a Unix-only TeX distribution at this time. This documentation
release was tested with the 1.0.7 release, but there have been no
substantial changes since late in the 0.9 series, which we used
extensively for previous versions without any difficulty.
If you don't want to get teTeX, here is what you'll need:
To create DVI, PDF, or PostScript files:
- LaTeX2e, 1995/12/01 or newer. Older versions are likely to
choke.
- makeindex. This is used to produce the indexes for the
library reference and Python/C API reference.
To create PDF files:
- pdflatex. We used the one in the teTeX distribution (pdfTeX
version 3.14159-13d (Web2C 7.3.1) at the time of this
writing). Versions even a couple of patchlevels earlier are
highly likely to fail due to syntax changes for some of the
pdftex primitives.
To create PostScript files:
- dvips. Most TeX installations include this. If you don't
have one, check CTAN (<ftp://ctan.tug.org/tex-archive/>).
To create info files:
Note that info support is currently being revised using new
conversion tools by Michael Ernst <mernst@cs.washington.edu>.
- makeinfo. This is available from any GNU mirror.
- emacs or xemacs. Emacs is available from the same place as
makeinfo, and xemacs is available from ftp.xemacs.org.
- Perl. Find the software at
<http://language.perl.com/info/software.html>.
- HTML::Element. If you don't have this installed, you can get
this from CPAN. Use the command:
perl -e 'use CPAN; CPAN::install("HTML::Element");'
You may need to be root to do this.
To create HTML files:
- Perl 5.004_04 or newer. Find the software at
<http://language.perl.com/info/software.html>.
- LaTeX2HTML 99.2b8 or newer. Older versions are not
supported; each version changes enough that supporting
multiple versions is not likely to work. Many older
versions don't work with Perl 5.6 as well. This also screws
up code fragments. ;-( Releases are available at:
<http://www.latex2html.org/>.
What if Times fonts are not available?
--------------------------------------
As distributed, the LaTeX documents use PostScript Times fonts. This
is done since they are much better looking and produce smaller
PostScript files. If, however, your TeX installation does not support
them, they may be easily disabled. Edit the file
texinputs/pypaper.sty and comment out the line that starts
"\RequirePackage{times}" by inserting a "%" character at the beginning
of the line. If you're formatting the docs for A4 paper instead of
US-Letter paper, change paper-a4/pypaper.sty instead. An alternative
is to install the right fonts and LaTeX style file.
What if I want to use A4 paper?
-------------------------------
Instead of building the PostScript by giving the command "make ps",
give the command "make PAPER=a4 ps"; the output will be produced in
the paper-a4/ subdirectory. (You can use "make PAPER=a4 pdf" if you'd
rather have PDF output.)
Making HTML files
-----------------
The LaTeX documents can be converted to HTML using Nikos Drakos'
LaTeX2HTML converter. See the Makefile; after some twiddling, "make"
should do the trick.
What else is in here?
---------------------
There is a new LaTeX document class called "howto". This is used for
the new series of Python HOWTO documents which is being coordinated by
Andrew Kuchling <akuchlin@mems-exchange.org>. The file
templates/howto.tex is a commented example which may be used as a
template. A Python script to "do the right thing" to format a howto
document is included as tools/mkhowto. These documents can be
formatted as HTML, PDF, PostScript, or ASCII files. Use "mkhowto
--help" for information on using the formatting tool.
For authors of module documentation, there is a file
templates/module.tex which may be used as a template for a module
section. This may be used in conjunction with either the howto or
manual document class. Create the documentation for a new module by
copying the template to lib<mymodule>.tex and editing according to the
instructions in the comments.
Documentation on the authoring Python documentation, including
information about both style and markup, is available in the
"Documenting Python" manual.
Copyright notice
================
The Python source is copyrighted, but you can freely use and copy it
as long as you don't change or remove the copyright notice:
----------------------------------------------------------------------
Copyright (c) 2000-2002 Python Software Foundation.
All rights reserved.
Copyright (c) 2000 BeOpen.com.
All rights reserved.
Copyright (c) 1995-2000 Corporation for National Research Initiatives.
All rights reserved.
Copyright (c) 1991-1995 Stichting Mathematisch Centrum.
All rights reserved.
See the file "texinputs/license.tex" for information on usage and
redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
----------------------------------------------------------------------

38
doc/html/myabout.dat Normal file
View File

@ -0,0 +1,38 @@
<p> This document was generated using the <a
href="http://saftsack.fs.uni-bayreuth.de/;SPMtilde;latex2ht/">
<strong>LaTeX</strong>2<tt>HTML</tt></a> translator.
</p>
<p> <a
href="http://saftsack.fs.uni-bayreuth.de/;SPMtilde;latex2ht/">
<strong>LaTeX</strong>2<tt>HTML</tt></a> is Copyright &copy;
1993, 1994, 1995, 1996, 1997, <a
href="http://cbl.leeds.ac.uk/nikos/personal.html">Nikos
Drakos</a>, Computer Based Learning Unit, University of
Leeds, and Copyright &copy; 1997, 1998, <a
href="http://www.maths.mq.edu.au/;SPMtilde;ross/">Ross
Moore</a>, Mathematics Department, Macquarie University,
Sydney.
</p>
<p> The application of <a
href="http://saftsack.fs.uni-bayreuth.de/;SPMtilde;latex2ht/">
<strong>LaTeX</strong>2<tt>HTML</tt></a> to the Python
documentation has been heavily tailored by Fred L. Drake,
Jr. Original navigation icons were contributed by Christopher
Petrilli.
</p>
<p> Jan Decaluwe reused the Python documentation flow to generate
the MyHDL manual as much as he could; his sincere thanks and
appreciation go out to all who contributed to this flow and
made this part of his life so much easier.
</p>
<hr>
<h2>Comments and Questions</h2>
<p> General comments, questions and bug reports regarding this document
may be sent to <a href="mailto:jan@jandecaluwe.com">Jan Decaluwe</a>.
</p>

135
doc/html/style.css Normal file
View File

@ -0,0 +1,135 @@
/*
* The first part of this is the standard CSS generated by LaTeX2HTML,
* with the "empty" declarations removed.
*/
/* Century Schoolbook font is very similar to Computer Modern Math: cmmi */
.math { font-family: "Century Schoolbook", serif; }
.math i { font-family: "Century Schoolbook", serif;
font-weight: bold }
.boldmath { font-family: "Century Schoolbook", serif;
font-weight: bold }
/*
* Implement both fixed-size and relative sizes.
*
* I think these can be safely removed, as it doesn't appear that
* LaTeX2HTML ever generates these, even though these are carried
* over from the LaTeX2HTML stylesheet.
*/
small.xtiny { font-size : xx-small; }
small.tiny { font-size : x-small; }
small.scriptsize { font-size : smaller; }
small.footnotesize { font-size : small; }
big.xlarge { font-size : large; }
big.xxlarge { font-size : x-large; }
big.huge { font-size : larger; }
big.xhuge { font-size : xx-large; }
/*
* Document-specific styles come next;
* these are added for the Python documentation.
*
* Note that the size specifications for the H* elements are because
* Netscape on Solaris otherwise doesn't get it right; they all end up
* the normal text size.
*/
body { color: #000000;
background-color: #ffffff; }
a:active { color: #ff0000; }
a[href]:hover { background-color: #bbeeff; }
a:visited { color: #551a8b; }
a:link { color: #0000bb; }
h1, h2, h3, h4, h5, h6 { font-family: avantgarde, sans-serif;
font-weight: bold; }
h1 { font-size: 180%; }
h2 { font-size: 150%; }
h3, h4 { font-size: 120%; }
code, tt { font-family: lucida typewriter, lucidatypewriter,
monospace; }
var { font-family: times, serif;
font-style: italic;
font-weight: normal; }
.typelabel { font-family: lucida, sans-serif; }
.navigation td { background-color: #99ccff;
font-weight: bold;
font-family: avantgarde, sans-serif;
font-size: 110%; }
div.warning { background-color: #fffaf0;
border: thin solid black;
padding: 0.5em;
margin-left: 2em;
margin-right: 2em; }
div.warning .label { font-family: sans-serif;
font-size: 110%;
margin-right: 0.5em; }
div.note { background-color: #fffaf0;
border: thin solid black;
padding: 0.5em;
margin-left: 2em;
margin-right: 2em; }
div.note .label { margin-right: 0.5em;
font-family: sans-serif; }
.release-info { font-style: italic; }
.titlegraphic { vertical-align: top; }
.verbatim pre { color: #00008b;
font-family: lucida typewriter, lucidatypewriter,
monospace;
font-size: 100%}
.verbatim { margin-left: 2em; }
.verbatim .footer { padding: 0.05in;
font-size: 85%;
background-color: #99ccff;
margin-right: 0.5in; }
.grammar { background-color: #99ccff;
margin-right: 0.5in;
padding: 0.05in; }
.productions { background-color: #bbeeff; }
.productions a:hover { background-color: #99ccff; }
.productions table { vertical-align: baseline; }
.grammar-footer { padding: 0.05in;
font-size: 85%; }
.email { font-family: avantgarde, sans-serif; }
.mailheader { font-family: avantgarde, sans-serif; }
.mimetype { font-family: avantgarde, sans-serif; }
.newsgroup { font-family: avantgarde, sans-serif; }
.url { font-family: avantgarde, sans-serif; }
.file { font-family: avantgarde, sans-serif; }
.tableheader { background-color: #99ccff;
font-family: avantgarde, sans-serif; }
.refcount-info { font-style: italic; }
.refcount-info .value { font-weight: bold;
color: #006600; }
/*
* Some decoration for the "See also:" blocks, in part inspired by some of
* the styling on Lars Marius Garshol's XSA pages.
* (The blue in the navigation bars is #99CCFF.)
*/
.seealso { background-color: #fffaf0;
border: thin solid black;
padding: 4pt; }
.seealso .heading { font-size: 110%; }
/*
* Class 'availability' is used for module availability statements at
* the top of modules.
*/
.availability .platform { font-weight: bold; }

56
doc/manual/MyHDL.tex Normal file
View File

@ -0,0 +1,56 @@
\documentclass{manual}
\usepackage{palatino}
\renewcommand{\ttdefault}{cmtt}
\renewcommand{\sfdefault}{cmss}
\newcommand{\myhdl}{\protect \mbox{MyHDL}}
\title{The \myhdl\ manual}
\input{boilerplate}
\begin{document}
\maketitle
\input{copyright}
\begin{abstract}
\noindent
\myhdl\ is a Python package for using Python as a hardware description
language. Popular hardware description languages, like Verilog and
VHDL, are compiled languages. Python with \myhdl\ with can be viewed as a
"scripting language" counterpart of such languages. However, Python is
more accurately described as a very high level language
(VHLL). \myhdl\ users have access to the amazing power and elegance of
Python for their modeling work.
The key idea behind \myhdl\ is to use Python generators to model the
concurrency required in hardware descriptions. As generators are a
recent Python feature, \myhdl\ requires Python 2.2.2 or higher.
\myhdl\ can be used to experiment with high level modeling, and with
verification techniques such as unit testing. The most important
practical application however, is to use it as a hardware verification
language by co-simulation with Verilog and VHDL.
The present release, \myhdl\ 0.2, enables \myhdl\ for
co-simulation. The \myhdl\ side is designed to work with any simulator
that has a PLI. For each simulator, an appropriate PLI module in C
needs to be provided. The release contains such a module for the
Icarus Verilog simulator.
\end{abstract}
\tableofcontents
\input{background.tex}
\input{informal.tex}
\input{modeling.tex}
\input{unittest.tex}
\input{cosimulation.tex}
\input{reference.tex}
\end{document}

137
doc/manual/background.tex Normal file
View File

@ -0,0 +1,137 @@
\chapter{Background information}
\section{Prerequisites}
You need a basic understanding of Python to use \myhdl{}.
If you don't know Python, you will take comfort in knowing
that it is probably one of the easiest programming languages to
learn~\footnote{You must be bored by such claims, but in Python's
case it's true.}. Learning Python is also one of the better time
investments that engineering professionals can make~\footnote{I am not
biased.}.
For beginners, \url{http://www.python.org/doc/current/tut/tut.html} is
probably the best choice for an on-line tutorial. For alternatives,
see \url{http://www.python.org/doc/Newbies.html}.
A working knowledge of a hardware description language such as Verilog
or VHDL is helpful.
\section{A small tutorial on generators}
Generators are a recent feature in Python. They were introduced in
Python 2.2, which is the most recent stable version at the time of
this writing. Therefore, there isn't a lot of tutorial material
available yet. Because generators are the key concept in
\myhdl{}, I include a small tutorial here.
Consider the following nonsensical function:
\begin{verbatim}
def function():
for i in range(5):
return i
\end{verbatim}
You can see why it doesn't make a lot of sense. As soon as the first
loop iteration is entered, the function returns:
\begin{verbatim}
>>> function()
0
\end{verbatim}
Returning is fatal for the function call. Further loop iterations
never get a chance, and nothing is left over from the function call
when it returns.
To change the function into a generator function, we replace
\keyword{return} with \keyword{yield}:
\begin{verbatim}
def generator():
for i in range(5):
yield i
\end{verbatim}
Now we get:
\begin{verbatim}
>>> generator()
<generator object at 0x815d5a8>
\end{verbatim}
When a generator function is called, it returns a generator object. A
generator object supports the iterator protocol, which is an expensive
way of saying that you can let it generate subsequent values by
calling its \function{next()} method:
\begin{verbatim}
>>> g = generator()
>>> g.next()
0
>>> g.next()
1
>>> g.next()
2
>>> g.next()
3
>>> g.next()
4
>>> g.next()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
StopIteration
\end{verbatim}
Now we can generate the subsequent values from the for loop on demand,
until they are exhausted. What happens is that the
\keyword{yield} statement is like a
\keyword{return}, except that it is non-fatal: the generator remembers
its state and the point in the code when it yielded. A higher order
agent can decide when to get a further value by calling the
generator's \function{next()} method. We can say that generators are
\dfn{resumable functions}.
If you are familiar with hardware description languages, this may ring
a bell. In hardware simulations, there is also a higher order agent,
the Simulator, that interacts with such resumable functions; they are
called \dfn{processes} in VHDL and \dfn{always blocks} in
Verilog. Like in those languages, Python generators provide an elegant
and efficient method to model concurrency, without having to resort to
some form of threading.
The use of generators to model concurrency is the first key concept in
\myhdl{}. The second key concept is a related one: in \myhdl{}, the
yielded values are used to define the condition upon which the
generator should resume. In other words, \keyword{yield}
statements work as generalized sensitivity lists.
If you want to know more about generators, consult the on-line Python
documentation, e.g. at \url{http://www.python.org/doc/2.2.2/whatsnew}.
\begin{notice}[warning]
At the beginning of this section I said that generators were
introduced in Python 2.2. This is not entirely correct: in fact,
generators will only be enabled as a standard feature in Python 2.3.
However, a stable version of Python 2.3 has not been released yet at
the time of this writing. So, what to do?
Fortunately, Python lets you import features from its future releases
(provided that the future is not too distant). So, until you use
Python 2.3 or higher, you have to include the following line at the
start of all code that defines generator functions:
\begin{verbatim}
from __future__ import generators
\end{verbatim}
From Python 2.3 on, this line may still be in the code, though it
will not have an effect anymore.
\end{notice}

413
doc/manual/cosimulation.tex Normal file
View File

@ -0,0 +1,413 @@
\chapter{Co-simulation with Verilog and VHDL}
\section{Introduction}
One of the most exciting possibilities of \myhdl\
is to use it as a hardware verification language (HVL).
A HVL is a language used to write test benches and
verification environments, and to control simulations.
Nowadays, it is generally acknowledged that HVLs should be equipped
with modern software techniques, such as object orientation. The
reason is that verification it the most complex and time-consuming
task of the design process: consequently every useful technique is
welcome. Moreover, test benches are not required to be
implementable. Therefore, unlike synthesizable code, there
are no constraints on creativity.
Technically, verification of a design implemented in
another language requires co-simulation. \myhdl\ is
enabled for co-simulation with any HDL simulator that
has a procedural language interface (PLI). The \myhdl\
side is designed to be independent of a particular
simulator, On the other hand, for each HDL simulator a specific
PLI module will have to be written in C. Currently,
the \myhdl\ release contains a PLI module to interface
to the Icarus Verilog simulator. This interface will
be used in the examples.
\section{The HDL side}
To introduce co-simulation, we will continue to use the Gray encoder
example from the previous chapters. Suppose that we want to
synthesize it and write it in Verilog for that purpose. Clearly we would
like to reuse our unit test verification environment.
To start, let's recall how the Gray encoder in \myhdl{} looks like:
\begin{verbatim}
def bin2gray(B, G, width):
""" Gray encoder.
B -- input intbv signal, binary encoded
G -- output intbv signal, gray encoded
width -- bit width
"""
while 1:
yield B
for i in range(width):
G.next[i] = B[i+1] ^ B[i]
\end{verbatim}
To show the co-simulation flow, we don't need the Verilog
implementation yet, but only the interface. Our Gray encoder in
Verilog would have the following interface:
\begin{verbatim}
module bin2gray(B, G);
parameter width = 8;
input [width-1:0] B;
output [width-1:0] G;
....
\end{verbatim}
To write a test bench, one creates a new module that instantiates the
design under test (DUT). The test bench declares nets and
regs (or signals in VHDL) that are attached to the DUT, and to
stimulus generators and response checkers. In an all-HDL flow, the
generators and checkers are written in the HDL itself, but we will
want to write them in \myhdl{}. To make the connection, we need to
declare which regs \& nets are driven and read by the \myhdl\
simulator. For our example, this is done as follows:
\begin{verbatim}
module dut_bin2gray;
reg [`width-1:0] B;
wire [`width-1:0] G;
initial begin
$from_myhdl(B);
$to_myhdl(G);
end
bin2gray dut (.B(B), .G(G));
defparam dut.width = `width;
endmodule
\end{verbatim}
The \code{\$from_myhdl} task call declares which regs are driven by
\myhdl{}, and the \code{\$to_myhdl} task call which regs \& nets are read
by it. These tasks take an arbitrary number of arguments. They are
defined in a PLI module written in C and made available in a
simulator-dependent manner. In Icarus Verilog, the tasks are defined
in a \code{myhdl.vpi} module that is compiled from C source code.
\section{The \myhdl\ side}
\myhdl\ supports co-simulation by a \code{Cosimulation} object.
A \code{Cosimulation} object must know how to run a HDL simulation.
Therefore, the first argument to its constructor is a command string
to execute a simulation.
The way to generate and run an simulation executable is simulator
dependent. For example, in Icarus Verilog, a simulation executable
for our example can be obtained obtained by running the
\code{iverilog} compiler as follows:
\begin{verbatim}
% iverilog -o bin2gray -Dwidth=4 bin2gray.v dut_bin2gray.v
\end{verbatim}
This generates a \code{bin2gray} executable for a parameter \code{width}
of 4, by compiling the contributing verilog files.
The simulation itself is run by the \code{vvp} command:
\begin{verbatim}
% vvp -m ./myhdl.vpi bin2gray
\end{verbatim}
This runs the \code{bin2gray} simulation, and specifies to use the
\code{myhdl.vpi} PLI module present in the current directory. (This is
just a command line usage example; actually simulating with the
\code{myhdl.vpi} module is only meaningful from a
\code{Cosimulation} object.)
We can use a \code{Cosimulation} object to provide a HDL
version of a design to the \myhdl\ simulator. Instead of a generator
function, we write a function that returns a \code{Cosimulation}
object. For our example and the Icarus Verilog simulator, this is done
as follows:
\begin{verbatim}
import os
from myhdl import Cosimulation
cmd = "iverilog -o bin2gray -Dwidth=%s bin2gray.v dut_bin2gray.v"
def bin2gray(B, G, width):
os.system(cmd % width)
return Cosimulation("vvp -m ./myhdl.vpi bin2gray", B=B, G=G)
\end{verbatim}
After the executable command argument, the \code{Cosimulation}
constructor takes an arbitrary number of keyword arguments. Those
arguments make the link between \myhdl\ Signals and HDL nets, regs, or
signals, by named association. The keyword is the name of an argument
in a \code{\$to_myhdl} or \code{\$from_myhdl} call; the argument is
a \myhdl\ Signal.
With all this in place, we can now use the existing unit test
to verify the Verilog implementation. Note that we kept the
same name and parameters for the the \code{bin2gray} function:
all we need to do is to provide this alternative definition
to the existing unit test.
Let's try it on the Verilog design:
\begin{verbatim}
module bin2gray(B, G);
parameter width = 8;
input [width-1:0] B;
output [width-1:0] G;
reg [width-1:0] G;
integer i;
wire [width:0] extB;
assign extB = {1'b0, B}; // zero-extend input
always @(extB) begin
for (i=0; i < width; i=i+1)
G[i] <= extB[i+1] ^ extB[i];
end
endmodule
\end{verbatim}
When we run our unit test, we get:
\begin{verbatim}
% python test_bin2gray.py
Check that only one bit changes in successive codewords ... ok
Check that all codewords occur exactly once ... ok
Check that the code is an original Gray code ... ok
----------------------------------------------------------------------
Ran 3 tests in 2.729s
OK
\end{verbatim}
\section{Restrictions}
In the ideal case, it should be possible to simulate
any HDL description seamlessly with \myhdl{}. Moreover
the communicating signals at each side should act
transparently as a single one, enabling fully race free
operation.
For various reasons, it may not be possible or desirable
to achieve full generality. As anyone that has developed
applications with the Verilog PLI can testify, the
restrictions in a particular simulator, and the
differences over various simulators, can be quite
frustrating. Moreover, full generality may require
a disproportionate amount of development work compared
to a slightly less general solution that may
be sufficient for the target application.
Consequently, I have tried to achieve a solution
which is simple enough so that one can reasonably
expect that any PLI-enabled simulator can support it,
and that is relatively easy to verify and maintain.
At the same time, the solution is sufficiently general
to cover the target application space.
The result is a compromise that places certain restrictions
on the HDL code. In this section, these restrictions
are presented.
\subsection{Only passive HDL can be co-simulated}
The most important restriction of the \myhdl\ co-simulation solution is
that only ``passive'' HDL can be co-simulated. This means that the HDL
code should not contain any statements with time delays. In other
words, the \myhdl\ simulator should be the master of time; in
particular, any clock signal should be generated at the \myhdl\ side.
At first this may seem like an important restriction, but if one
considers the target application for co-simulation, it probably
isn't.
\myhdl\ supports co-simulation so that test benches for HDL
designs can be written in Python. Let's consider the nature of the
target HDL designs. For high-level, behavioral models that are not
intended for implementation, it should come as no surprise that I
would recommend to write them in \myhdl\ directly; that is one of the
goals of the \myhdl\ effort. Likewise, gate level designs with
annotated timing are not the target application: static timing
analysis is a much better verification method for such designs.
Rather, the targeted HDL designs are naturally models that are
intended for implementation, most likely through synthesis. As time
delays are meaningless in synthesizable code, the restriction is
compatible with the target application.
\subsection{Race sensitivity issues}
In a typical RTL code, some events cause other events to occur in the
same time step. For example, when a clock signal triggers some signals
may change in the same time step. For race-free operation, an HDL
must differentiate between such events within a time step. This is done
by the concept of ``delta'' cycles. In a fully general, race free
co-simulation, the co-simulators would communicate at the level of delta
cycles. However, in \myhdl\ co-simulation, this is not entirely the
case.
Delta cycles from the \myhdl\ simulator toward the HDL co-simulator are
preserved. However, in the opposite direction, they are not. The
signals changes are only returned to the \myhdl\ simulator after all delta
cycles have been performed in the HDL co-simulator.
What does this mean? Let's start with the good news. As explained in
the previous section, the concept behind \myhdl\ co-simulation implies
that clocks are generated at the \myhdl\ side. \emph{When using a
\myhdl\ clock and its corresponding HDL signal directly as a clock,
co-simulation is race free.} In other words, the case
that most closely reflects the \myhdl\ co-simulation approach, is race free.
The situation is different when you want to use a signal driven by the
HDL (and the corresponding MyHDL signal) as a clock.
Communication triggered by such a clock is not race free. The solution
is to treat such an interface as a chip interface instead of an RTL
interface. For example, when data is triggered at positive clock
edges, it can safely be sampled at negative clock edges.
Alternatively, the \myhdl\ data signals can be declared with a delay
value, so that they are guaranteed to change after the clock
edge.
\section{Implementation notes}
\begin{quote}
\em
This section requires some knowledge of PLI terminology.
\end{quote}
Enabling a simulator for co-simulation requires a PLI module written
in C. In Verilog, the PLI is part of the ``standard''. However,
different simulators implement different versions and portions of the
standard. Worse yet, the behavior of certain PLI callbacks is not
defined on some essential points. As a result, one should plan to
write a specific PLI module for any simulator.
The present release contains a PLI module for the open source Icarus
simulator. I would like to add modules for other simulators in the
future, either from external contributions, or by getting access to
them myself.
This section documents the current approach and status of the PLI
module implementation in Icarus, and some reflections on future
implementations in other simulators.
\subsection{Icarus Verilog}
\subsubsection{Delta cycle implementation}
\label{icarus-delta-cycles}
To make co-simulation work, a specific type of PLI callback is
needed. The callback should be run when all pending events have been
processed, while allowing the creation of new events in the current
time step (e.g. by the \myhdl\ simulator). In some Verilog
simulators, the \code{cbReadWriteSync} callback does exactly
that. However, in others, including Icarus, it does not. The
callback's behavior is not fully standardized; some simulators run the
callback before non-blocking assignment events have been processed.
Consequently, I had to look for a workaround. One half of the solution
is to use the \code{cbReadOnlySync} callback. This callback runs
after all pending events have been processed. However, it does not
permit to create new events in the current time step. The second half
of the solution is to map \myhdl\ delta cycles onto real Verilog time
steps. Note that fortunately I had some freedom here because of the
restriction that only passive HDL code can be co-simulated.
I chose to make the time granularity in the Verilog simulator a 1000
times finer than in the \myhdl{} simulator. For each \myhdl\ time
step, 1000 Verilog time steps are available for \myhdl\ delta
cycles. In practice, only a few delta cycles per time step should be
needed. Exceeding this limit almost certainly indicates a design error;
the limit is checked at run-time. The factor 1000 also makes it
easy to distinguish ``real'' time from delta cycle time when printing
out the Verilog time.
\subsubsection{Passive Verilog check}
As explained before, co-simulated Verilog should not contain delay
statements. Ideally, there should be a run-time check to flag
non-compliant code. However, there is currently no such check in the
Icarus module.
The check can be written using the \code{cbNextSimTime} VPI callback
in Verilog. However, Icarus 0.7 doesn't support this callback. In the
meantime, support for it has been added to the Icarus development
branch. When Icarus 0.8 is released, a check will be added.
In the mean time, just don't do this. It may appear to ``work'' but it
really won't as events will be missed over the co-simulation
interface.
\subsubsection{Interrupted system calls}
The PLI module uses \code{read} and \code{write} system calls to
communicate between the co-simulators. The implementation assumes that
these calls are restarted automatically by the operating system when
interrupted. This is apparently what happens on my Linux box and on
other operating systems I have used before.
I know how to handle non-restarted interrupted system calls, but I
cannot test the code! Also, I don't know whether this is still a
relevant issue with modern operating systems. So I left it
at that for the moment, and added assertions that should trigger
when this situation occurs.
Whenever an assertion fires in the PLI module, let me
know. The same holds for Python exceptions that you cannot
easily explain.
\subsection{Other Verilog simulators}
The Icarus module is written with VPI calls, which are provided by the
most recent generation of the Verilog PLI. Some simulators may only
support TF/ACC calls, requiring a complete redesign of the interface
module.
If the simulator supports VPI, the Icarus module should be reusable to
a large extent. However, it may be possible to improve on it. The
workaround to support delta cycles described in
Section~\ref{icarus-delta-cycles} may not be necessary. In some
simulators, the \code{cbReadWriteSync} callback occurs after all
events (including non-blocking assignments) have been processed. In
that case, the functionality can be supported without a finer time
granularity in the Verilog simulator.
There are also Verilog standardization efforts underway to resolve the
ambiguity of the \code{cbReadWriteSync} callback. The solution will be
to introduce new, well defined callbacks. From reading some proposals,
I conclude that the \code{cbEndOfSimTime} callback would provide the
required functionality.
\subsection{VHDL}
It would be great to have an interface to VHDL simulators such as the
Modelsim VHDL simulator. This will require a PLI module using the
PLI of the VHDL simulator. One feature which I would
like to keep if possible is the way to declare the communicating
signals. In the Verilog solution, it is not necessary to define and
instantiate a special HDL module (entity). Rather, the participating
signals can be declared directly in the \code{\$to_myhdl} and
\code{\$from_myhdl} task calls.

729
doc/manual/informal.tex Normal file
View File

@ -0,0 +1,729 @@
\chapter{Introduction to \myhdl\ }
\section{A basic \myhdl\ simulation}
We will introduce \myhdl\ with a classic \code{Hello World} style
example. Here are the contents of a \myhdl\ simulation script called
\file{hello1.py}:
\begin{verbatim}
from myhdl import delay, now, Simulation
def sayHello():
while 1:
yield delay(10)
print "%s Hello World!" % now()
gen = sayHello()
sim = Simulation(gen)
sim.run(30)
\end{verbatim}
All example code can be found in the distribution directory under
\file{example/manual}. When we run this simulation, we get the
following output:
\begin{verbatim}
% python hello1.py
10 Hello World!
20 Hello World!
30 Hello World!
StopSimulation: Simulated for duration 30
\end{verbatim}
The first line of the script imports a
number of objects from the \code{myhdl} package. In good Python style, and
unlike most other languages, we can only use identifiers that are
\emph{literally} defined in the source file \footnote{I don't want to
explain the \samp{import *} syntax}.
Next, we define a generator function called
\code{sayHello}. This is a generator function (as opposed to
a classic Python function) because it contains a \keyword{yield}
statement (instead of \keyword{return} statement). In \myhdl{}, a
\keyword{yield} statement has a similar purpose as a \keyword{wait}
statement in VHDL: the statement suspends execution of the function,
while its clauses specify when the function should resume. In this case,
there is a \code{delay} clause, that specifies the required delay.
To make sure that the generator runs ``forever'', we wrap its behavior
in a \code{while 1} loop. This is a standard Python idiom, and it is
the \myhdl\ equivalent of the implicit looping behavior of a Verilog
\keyword{always} block and a VHDL \keyword{process}.
In \myhdl{}, the basic simulation objects are generators. Generators
are created by calling generator functions. For example, variable
\code{gen} refers to a generator. To simulate this generator, we pass
it as an argument to a \class{Simulation} object constructor. We then
run the simulation for the desired amount of time. In \myhdl{}, time
is modeled as a natural integer.
\section{Concurrent generators and signals}
In the previous section, we simulated a single generator. Of course,
real hardware descriptions are not like that: in fact, they are
typically massively concurrent. \myhdl\ supports this by allowing an
arbitrary number of concurrent generators. More specifically, a
\class{Simulation} constructor can take an arbitrary number of
arguments, each of which can be a generator or a nested sequence of
generators.
With concurrency comes the problem of deterministic
communication. Hardware languages use special objects to
support deterministic communication between concurrent code. \myhdl\
has a \class{Signal} object which is roughly modeled after VHDL
signals.
We will demonstrate these concepts by extending and modifying our
first example. We introduce a clock signal, driven by a second
generator:
\begin{verbatim}
clk = Signal(0)
def clkGen():
while 1:
yield delay(10)
clk.next = 1
yield delay(10)
clk.next = 0
\end{verbatim}
The \code{clk} signal is constructed with an initial value
\code{0}. In the clock generator function \code{clkGen}, it is
continuously assigned a new value after a certain delay. In \myhdl{},
the new value of a signal is specified by assigning to its
\code{next} attribute. This is the \myhdl\ equivalent of VHDL signal
assignments and Verilog's nonblocking assignments.
The \code{sayHello} generator function is modified to wait for a
rising edge of the clock instead of a delay:
\begin{verbatim}
def sayHello():
while 1:
yield posedge(clk)
print "%s Hello World!" % now()
\end{verbatim}
Waiting for a clock edge is achieved with a second form of the
\keyword{yield} statement: \samp{yield posedge(\var{signal})}.
A \class{Simulation} object will suspend the generator as that point,
and resume it when there is a rising edge on the signal.
The \class{Simulation} is now constructed with 2 generator arguments:
\begin{verbatim}
sim = Simulation(clkGen(), sayHello())
sim.run(50)
\end{verbatim}
When we run this simulation, we get:
\begin{verbatim}
% python hello2.py
10 Hello World!
30 Hello World!
50 Hello World!
StopSimulation: Simulated for duration 50
\end{verbatim}
\section{Parameters, instantiations and hierarchy}
So far, the generator function examples had no parameters. For
example, the \code{clk} signal was defined in the enclosing scope of
the generator functions. To make code reusable we will
want to pass arguments through a parameter list. For example, we can
change the clock generator function to make it more general
and reusable, as follows:
\begin{verbatim}
def clkGen(clock, period=20):
lowTime = int(period / 2)
highTime = period - lowTime
while 1:
yield delay(lowTime)
clock.next = 1
yield delay(highTime)
clock.next = 0
\end{verbatim}
The clock signal is now a parameter of the function. Also, the clock
\var{period} is a parameter with a default value of \code{20}.
This makes \var{period} an \dfn{optional} parameter; if it is not
specified in a call, the default value will be used.
Similarly, the \code{sayHello} function can be made more general as follows:
\begin{verbatim}
def sayHello(clock, to="World!"):
while 1:
yield posedge(clock)
print "%s Hello %s" % (now(), to)
\end{verbatim}
We can create any number of generators by calling generator functions
with the appropriate parameters. This is very similar to the concept of
\dfn{instantiation} in hardware description languages and we will use
the same terminology in \myhdl{}. Hierarchy can be modeled by defining
the instances in a higher-level function, and returning them. For
example:
\begin{verbatim}
def greetings():
clk1 = Signal(0)
clk2 = Signal(0)
clkGen1 = clkGen(clk1)
clkGen2 = clkGen(clock=clk2, period=19)
sayHello1 = sayHello(clock=clk1)
sayHello2 = sayHello(to="MyHDL", clock=clk2)
return clkGen1, clkGen2, sayHello1, sayHello2
\end{verbatim}
As in standard Python, positional or named parameter association can
be used in instantiations, or a mix of both\footnote{All positional
parameters have to go before any named parameter.}. All these styles
are demonstrated in the example above. As in hardware description
languages, named association can be very useful if there are a lot of
parameters, as the parameter order in the call does not matter in that
case.
To support hierarchy, \class{Simulation} constructor arguments can be
sequences of generators. More specifically, the return value of a
higher-level instantiating function can be used as an argument of the
constructor. For example:
\begin{verbatim}
sim = Simulation(greetings())
sim.run(50)
\end{verbatim}
This produces the following output:
\begin{verbatim}
% python greetings.py
9 Hello MyHDL
10 Hello World!
28 Hello MyHDL
30 Hello World!
47 Hello MyHDL
50 Hello World!
StopSimulation: Simulated for duration 50
\end{verbatim}
\section{Bit-oriented operations}
Hardware design involves dealing with bits and bit-oriented
operations. The standard Python type \class{int} has most of the
desired features, but lacks support for indexing and slicing. For this
reason, \myhdl\ provides the \class{intbv} class. The name was chosen
to suggest an integer with bit vector flavor.
Class \class{intbv} works transparently as an integer and with other
integer-like types. Like class \class{int}, it provides access to the
underlying two's complement representation for bitwise
operations. In addition, it is a mutable type that provides indexing
and slicing operations, and some additional bit-oriented support such
as concatenation.
\subsection{Indexing operations}
\label{gray}
As an example, we will consider the design of a Gray encoder. The
following code is a Gray encoder modeled in \myhdl{}:
\begin{verbatim}
def bin2gray(B, G, width):
""" Gray encoder.
B -- input intbv signal, binary encoded
G -- output intbv signal, Gray encoded
width -- bit width
"""
while 1:
yield B
for i in range(width):
G.next[i] = B[i+1] ^ B[i]
\end{verbatim}
This code introduces a few new concepts. The string in triple quotes
at the start of the function is a \dfn{doc string}. This is standard
Python practice for structured documentation of code. Moreover, we
use a third form of the \keyword{yield} statement:
\samp{yield \var{signal}}. This specifies that the generator should
resume whenever \var{signal} changes value. This is typically used to
describe combinatorial logic.
Finally, the code contains bit indexing operations and an exclusive-or
operator as required for a Gray encoder. By convention, the lsb of an
\class{intbv} object has index~\code{0}.
To verify the Gray encoder, we write a test bench that prints input
and output for all possible input values:
\begin{verbatim}
def testBench(width):
B = Signal(intbv(0))
G = Signal(intbv(0))
dut = bin2gray(B, G, width)
def stimulus():
for i in range(2**width):
B.next = intbv(i)
yield delay(10)
print "B: " + bin(B, width) + "| G: " + bin(G, width)
return (dut, stimulus())
\end{verbatim}
We use the conversion function \code{bin} to get a binary
string representation of the signal values. This function is exported
by the \code{myhdl} package and complements the standard Python
\code{hex} and \code{oct} conversion functions.
To demonstrate, we set up a simulation for a small width:
\begin{verbatim}
Simulation(testBench(width=3)).run()
\end{verbatim}
The simulation produces the following output:
\begin{verbatim}
% python bin2gray.py
B: 000 | G: 000
B: 001 | G: 001
B: 010 | G: 011
B: 011 | G: 010
B: 100 | G: 110
B: 101 | G: 111
B: 110 | G: 101
B: 111 | G: 100
StopSimulation: No more events
\end{verbatim}
\subsection{Slicing operations}
For a change, we will use a plain function as an example to illustrate
slicing. The following function calculates the HEC byte of an ATM
header.
\begin{verbatim}
from myhdl import intbv
concat = intbv.concat # shorthand alias
COSET = 0x55
def calculateHec(header):
""" Return hec for an ATM header, represented as an intbv.
The hec polynomial is 1 + x + x**2 + x**8.
"""
hec = intbv(0)
for bit in header[32:]:
hec[8:] = concat(hec[7:2],
bit ^ hec[1] ^ hec[7],
bit ^ hec[0] ^ hec[7],
bit ^ hec[7]
)
return hec ^ COSET
\end{verbatim}
The code shows how slicing access and assignment is supported on the
\class{intbv} data type. In accordance with the most common hardware
convention, and unlike standard Python, slicing ranges are
downward. The code also demonstrates concatenation of \class{intbv}
objects.
As in standard Python, the slicing range is half-open: the highest
index bit is not included. Unlike standard Python however, this index
corresponds to the \emph{leftmost} item. The rightmost index is
\code{0} by default, and can be omitted from the slice.
The half-openness of a slice may seem awkward at first, but it helps
to avoid one-off count issues in practice. For example, the slice
\code{hex[8:]} has exactly \code{8} bits. Likewise, the slice
\code{hex[7:2]} has \code{7-2=5} bits. You can think about it as
follows: for a slice \code{[i:j]}, only bits below index \code{i} are
included, and the bit with index \code{j} is the last bit included.
\section{Bus-functional procedures}
A \dfn{bus-functional procedure} is a reusable encapsulation of the
low-level operations needed to implement some abstract transaction on
a physical interface. Bus-functional procedures are typically used in
flexible verification environments.
Once again, \myhdl\ supports bus-functional procedures by generator
functions. In \myhdl\, the difference between instances and
bus-functional procedure calls comes from the way in which a generator
function is used.
As an example, we will design a bus-functional procedure of a
simplified UART transmitter. We assume 8 data bits, no parity bit, and
a single stop bit, and we add print statements to follow the
simulation behavior:
\begin{verbatim}
T_9600 = int(1e9 / 9600)
def rs232_tx(tx, data, duration=T_9600):
""" Simple rs232 transmitter procedure.
tx -- serial output data
data -- input data byte to be transmitted
duration -- transmit bit duration
"""
print "-- Transmitting %s --" % hex(data)
print "TX: start bit"
tx.next = 0
yield delay(duration)
for i in range(8):
print "TX: %s" % data[i]
tx.next = data[i]
yield delay(duration)
print "TX: stop bit"
tx.next = 1
yield delay(duration)
\end{verbatim}
This looks exactly like the generator functions in previous sections. It
becomes a bus functional procedure when we use it differently. Suppose
that in a test bench, we want to generate a number of data bytes to be
transmitted. This can be modeled as follows:
\begin{verbatim}
testvals = (0xc5, 0x3a, 0x4b)
def stimulus():
tx = Signal(1)
for val in testvals:
txData = intbv(val)
yield rs232_tx(tx, txData)
\end{verbatim}
We use the bus functional procedure call as a clause in a
\code{yield} statement. This introduces a fourth form of the
\code{yield} statement: using a generator as a clause. Although this is
a more dynamic usage than in the previous cases, the meaning is
actually very similar. When the calling generator \code{stimulus()}
encounters the \code{yield} statement, it suspends execution, and the
clause specifies when it should resume. In this case, the generator
\code{rs232_tx(tx, txData)} is \dfn{forked}, and the caller resumes
when the forked generator returns.
When simulating this, we get:
\begin{verbatim}
-- Transmitting 0xc5 --
TX: start bit
TX: 1
TX: 0
TX: 1
TX: 0
TX: 0
TX: 0
TX: 1
TX: 1
TX: stop bit
-- Transmitting 0x3a --
TX: start bit
TX: 0
TX: 1
TX: 0
TX: 1
...
\end{verbatim}
We will continue with this example by designing the corresponding UART
receiver bus-functional procedure. This will allow us to introduce
further capabilities of \myhdl\ and its use of the \code{yield}
statement.
Until now, the \code{yield} statements had a single clause. However,
they can have multiple clauses as well. In that case, the calling
generator is triggered as soon as the condition corresponding to one
of the clauses is satisfied. This corresponds to the functionality of
sensitivity lists in Verilog and VHDL.
For example, suppose we want to design an UART receive procedure with
a timeout. We can specify the timeout condition while waiting for the
start bit, as in the following generator function:
\begin{verbatim}
def rs232_rx(rx, data, duration=T_9600, timeout=MAX_TIMEOUT):
""" Simple rs232 receiver procedure.
rx -- serial input data
data -- data received
duration -- receive bit duration
"""
# wait on start bit until timeout
yield negedge(rx), delay(timeout)
if rx == 1:
raise StopSimulation, "RX time out error"
# sample in the middle of the bit duration
yield delay(duration // 2)
print "RX: start bit"
for i in range(8):
yield delay(duration)
print "RX: %s" % rx
data[i] = rx
yield delay(duration)
print "RX: stop bit"
print "-- Received %s --" % hex(data)
\end{verbatim}
If the timeout condition is triggered, the receive bit \code{rx}
will still be \code{1}. In that case, we raise an exception to stop
the simulation. The \code{StopSimulation} exception is predefined in
\myhdl\ for such purposes. In the other case, we proceed by
positioning the sample point in the middle of the bit duration, and
sampling the received data bits.
When a \code{yield} statement has multiple clauses, they can be of any
type that is supported as a single clause, including generators. For
example, we can verify the transmitter and receiver generator against
each other by yielding them together, as follows:
\begin{verbatim}
def test():
tx = Signal(1)
rx = tx
rxData = intbv(0)
for val in testvals:
txData = intbv(val)
yield rs232_rx(rx, rxData), rs232_tx(tx, txData)
\end{verbatim}
Both forked generators will run concurrently, and the calling
generator will resume as soon as one of them finishes (which will be
the transmitter in this case). The simulation output shows how
the UART procedures run in lockstep:
\begin{verbatim}
-- Transmitting 0xc5 --
TX: start bit
RX: start bit
TX: 1
RX: 1
TX: 0
RX: 0
TX: 1
RX: 1
TX: 0
RX: 0
TX: 0
RX: 0
TX: 0
RX: 0
TX: 1
RX: 1
TX: 1
RX: 1
TX: stop bit
RX: stop bit
-- Received 0xc5 --
-- Transmitting 0x3a --
TX: start bit
RX: start bit
TX: 0
RX: 0
...
\end{verbatim}
For completeness, we will verify the timeout behavior with a test
bench that disconnects the \code{rx} from the \code{tx} signal, and we
specify a small timeout for the receive procedure:
\begin{verbatim}
def testTimeout():
tx = Signal(1)
rx = Signal(1)
rxData = intbv(0)
for val in testvals:
txData = intbv(val)
yield rs232_rx(rx, rxData, timeout=4*T_9600-1), rs232_tx(tx, txData)
\end{verbatim}
The simulation now stops with a timeout exception after a few
transmit cycles:
\begin{verbatim}
-- Transmitting 0xc5 --
TX: start bit
TX: 1
TX: 0
TX: 1
StopSimulation: RX time out error
\end{verbatim}
Recall that the calling generator resumes as soon as one of the
forked generators returns. In the previous cases, this is just fine,
as the transmitter and receiver generators run in lockstep. However,
it may be desirable to resume the caller only when \emph{all} of the
forked generators have finished. For example, suppose that we want to
characterize the robustness of the transmitter and receiver design to
bit duration differences. We can adapt our test bench as follows, to
run the transmitter at a faster rate:
\begin{verbatim}
T_10200 = int(1e9 / 10200)
def testNoJoin():
tx = Signal(1)
rx = tx
rxData = intbv(0)
for val in testvals:
txData = intbv(val)
yield rs232_rx(rx, rxData), rs232_tx(tx, txData, duration=T_10200)
\end{verbatim}
Simulating this shows how the transmission of the new byte starts
before the previous one is received, potentially creating additional
transmission errors:
\begin{verbatim}
-- Transmitting 0xc5 --
TX: start bit
RX: start bit
...
TX: 1
RX: 1
TX: 1
TX: stop bit
RX: 1
-- Transmitting 0x3a --
TX: start bit
RX: stop bit
-- Received 0xc5 --
RX: start bit
TX: 0
\end{verbatim}
It is more likely that we want to characterize the design on a byte
by byte basis, and align the two generators before transmitting each
byte. In \myhdl{}, this is done with the \function{join} function. By
joining clauses together in a \code{yield} statement, we create a new
clause that triggers only when all of its clause arguments have
triggered. For example, we can adapt the test bench as follows:
\begin{verbatim}
def testJoin():
tx = Signal(1)
rx = tx
rxData = intbv(0)
for val in testvals:
txData = intbv(val)
yield join(rs232_rx(rx, rxData), rs232_tx(tx, txData, duration=T_10200))
\end{verbatim}
Now, transmission of a new byte only starts when the previous one is received:
\begin{verbatim}
-- Transmitting 0xc5 --
TX: start bit
RX: start bit
...
TX: 1
RX: 1
TX: 1
TX: stop bit
RX: 1
RX: stop bit
-- Received 0xc5 --
-- Transmitting 0x3a --
TX: start bit
RX: start bit
TX: 0
RX: 0
\end{verbatim}
\section{\myhdl\ and Python}
To conclude this introductory chapter, it is useful to stress that
\myhdl\ is not a language in itself. The underlying language is Python,
and \myhdl\ is implemented as a Python package called \code{myhdl}.
Moreover, it is a design goal to keep the \code{myhdl} package as
minimalistic as possible, so that \myhdl\ descriptions are very much
``pure Python''.
To have Python as the underlying language is significant in several
ways:
\begin{itemize}
\item Python is a very powerful high level language. This translates
into high productivity and elegant solutions to complex problems.
\item Python is continuously improved by some very clever
minds, supported by a large and fast growing user base. Python profits
fully from the open source development model.
\item Python comes with an extensive standard library. Some
functionality is likely to be of direct interest to \myhdl\ users:
examples include string handling, regular expressions, random number
generation, unit test support, operating system interfacing and GUI
development. In addition, there are modules for mathematics, database
connections, networking programming, internet data handling, and so
on.
\item Python has a powerful C extension model. All built-in types are
written with the same C API that is available for custom
extensions. To a module user, there is no difference between a
standard Python module and a C extension module --- except
performance. The typical Python development model is to prototype
everything in Python until the application is stable, and (only) then
rewrite performance critical modules in C if necessary.
\end{itemize}

407
doc/manual/modeling.tex Normal file
View File

@ -0,0 +1,407 @@
\chapter{Modeling techniques}
\section{RTL modeling}
The present section describes how \myhdl\ supports RTL style modeling
as is typically used for synthesizable models in Verilog or VHDL. This
section is included mainly for illustrative purposes, as this modeling
style is well known and understood.
\subsection{Combinatorial logic}
\subsubsection{Template}
Combinatorial logic is described with a generator function code template as
follows:
\begin{verbatim}
def combinatorialLogic(<arguments>)
while 1:
yield <input signal arguments>
<functional code>
\end{verbatim}
The overall code is wrapped in a \code{while 1} statement to keep the
generator alive. All input signals are clauses in the \code{yield}
statement, so that the generator resumes whenever one of the inputs
changes.
\subsubsection{Example}
The following is an example of a combinatorial multiplexer:
\begin{verbatim}
def mux(z, a, b, sel):
""" Multiplexer.
z -- mux output
a, b -- data inputs
sel -- control input: select a if asserted, otherwise b
"""
while 1:
yield a, b, sel
if sel == 1:
z.next = a
else:
z.next = b
\end{verbatim}
To verify, let's simulate this logic with some random patterns. The
\code{random} module in Python's standard library comes in handy for
such purposes. The function \code{randrange(\var{n})} returns a random
natural integer smaller than \var{n}. It is used in the test bench
code to produce random input values:
\begin{verbatim}
from random import randrange
(z, a, b, sel) = [Signal(0) for i in range(4)]
MUX_1 = mux(z, a, b, sel)
def test():
print "z a b sel"
for i in range(8):
a.next, b.next, sel.next = randrange(8), randrange(8), randrange(2)
yield delay(10)
print "%s %s %s %s" % (z, a, b, sel)
Simulation(MUX_1, test()).run()
\end{verbatim}
Because of the randomness, the simulation output varies between runs
\footnote{It also possible to have a reproducible random output, by
explicitly providing a seed value. See the documentation of the
\code{random} module.}. One particular run produced the following
output:
\begin{verbatim}
% python mux.py
z a b sel
6 6 1 1
7 7 1 1
7 3 7 0
1 2 1 0
7 7 5 1
4 7 4 0
4 0 4 0
3 3 5 1
StopSimulation: No more events
\end{verbatim}
\subsection{Sequential logic}
\subsubsection{Template}
Sequential RTL models are sensitive to a clock edge. In addition, they
may be sensitive to a reset signal. We will describe one of the most
common patterns: a template with a rising clock edge and an
asynchronous reset signal. Other templates are similar.
\begin{verbatim}
def sequentialLogic(<arguments>, clock, ..., reset, ...)
while 1:
yield posedge(clock), negedge(reset)
if reset == <active level>:
<reset code>
else:
<functional code>
\end{verbatim}
\subsubsection{Example}
The following code is a description of an incrementer with enable, and
an asynchronous power-up reset.
\begin{verbatim}
ACTIVE_LOW, INACTIVE_HIGH = 0, 1
def Inc(count, enable, clock, reset, n):
""" Incrementer with enable.
count -- output
enable -- control input, increment when 1
clock -- clock input
reset -- asynchronous reset input
n -- counter max value
"""
while 1:
yield posedge(clock), negedge(reset)
if reset == ACTIVE_LOW:
count.next = 0
else:
if enable:
count.next = (count + 1) % n
\end{verbatim}
For the test bench, we will use an independent clock generator, stimulus
generator, and monitor. After applying enough stimulus patterns, we
can raise the \code{myhdl.StopSimulation} exception to stop the
simulation run. The test bench for a small incrementer and a small
number of patterns is a follows:
\begin{verbatim}
count, enable, clock, reset = [Signal(intbv(0)) for i in range(4)]
INC_1 = Inc(count, enable, clock, reset, n=4)
def clockGen():
while 1:
yield delay(10)
clock.next = not clock
def stimulus():
reset.next = ACTIVE_LOW
yield negedge(clock)
reset.next = INACTIVE_HIGH
for i in range(12):
enable.next = min(1, randrange(3))
yield negedge(clock)
raise StopSimulation
def monitor():
print "enable count"
yield posedge(reset)
while 1:
yield posedge(clock)
yield delay(1)
print " %s %s" % (enable, count)
Simulation(clockGen(), stimulus(), monitor(), INC_1).run()
\end{verbatim}
The simulation produces the following output:
\begin{verbatim}
% python inc.py
enable count
0 0
1 1
0 1
1 2
1 3
1 0
0 0
1 1
0 1
0 1
0 1
1 2
StopSimulation
\end{verbatim}
\section{High level modeling}
\begin{quote}
\em
This section on high level modeling should become an exciting part of
the manual, but it doesn't contain a lot of material just yet. One
reason is that I concentrated on the groundwork first. Moreover,
though I expect Python to offer some very powerful capabilities in
this domain, I'm just starting to experiment and learn myself. For
example, note that so far we haven't used classes (nor meta-classes
:-)) yet, even though Python has a very powerful object-oriented
model.
\end{quote}
\subsection{Modeling memories with built-in types}
Python has powerful built-in data types that can be useful to model
hardware memories. This can be merely a matter of putting an interface
around some data type operations.
For example, a \dfn{dictionary} comes in handy to model sparse memory
structures. (In other languages, this data type is called
\dfn{associative array}, or \dfn{hash table}.) A sparse memory is one in
which only a small part of the addresses is used in a particular
application or simulation. Instead of statically allocating the full
address space, which can be large, it is better to dynamically
allocate the needed storage space. This is exactly what a dictionary
provides. The following is an example of a sparse memory model:
\begin{verbatim}
def sparseMemory(dout, din, addr, we, en, clk):
""" Sparse memory model based on a dictionary.
Ports:
dout -- data out
din -- data in
addr -- address bus
we -- write enable: write if 1, read otherwise
en -- interface enable: enabled if 1
clk -- clock input
"""
memory = {}
while 1:
yield posedge(clk)
if not en:
continue
if we:
memory[addr] = din.val
else:
dout.next = memory[addr]
\end{verbatim}
Note how we use the \code{val} attribute of the \code{din} signal, as
we don't want to store the signal object itself, but its current
value. (In many cases, \myhdl\ can use a signal's value automatically
when there is no ambiguity: for example, this happens whenever a
signal is used in expressions. When in doubt, you can always use the
\code{val} attribute explicitly.)
As a second example, we will demonstrate how to use a list to model a
synchronous fifo:
\begin{verbatim}
def fifo(dout, din, re, we, empty, full, clk, maxFilling=sys.maxint):
""" Synchronous fifo model based on a list.
Ports:
dout -- data out
din -- data in
re -- read enable
we -- write enable
empty -- empty indication flag
full -- full indication flag
clk -- clock input
Optional parameter:
maxFilling -- maximum fifo filling, "infinite" by default
"""
memory = []
while 1:
yield posedge(clk)
if we:
memory.insert(0, din.val)
if re:
dout.next = memory.pop()
filling = len(memory)
empty.next = (filling == 0)
full.next = (filling == maxFilling)
\end{verbatim}
Again, the model is merely a \myhdl\ interface around some operations
on a list: \function{insert()} to insert entries, \function{pop()} to
retrieve them, and \function{len()} to get the size of a Python
object.
\subsection{Modeling errors using exceptions}
In the previous section, we used Python data types for modeling. If
such a type is used inappropriately, Python's run time error system
will come into play. For example, if we access an address in the
\function{sparseMemory} model that was not initialized before, we will
get a traceback similar to the following (some lines omitted for
clarity):
\begin{verbatim}
Traceback (most recent call last):
...
File "sparseMemory.py", line 30, in sparseMemory
dout.next = memory[addr]
KeyError: 51
\end{verbatim}
Similarly, if the \code{fifo} is empty, and we attempt to read from
it, we get:
\begin{verbatim}
Traceback (most recent call last):
...
File "fifo.py", line 34, in fifo
dout.next = memory.pop()
IndexError: pop from empty list
\end{verbatim}
Instead of these low level errors, it may be preferable to define
errors at the functional level. In Python, this is typically done by
defining a custom \code{Error} exception, by subclassing the standard
\code{Exception} class. This exception is then raised explicitly when
an error condition occurs.
For example, we can change the \function{sparseMemory} function as
follows (with the doc string is omitted for brevity):
\begin{verbatim}
class Error(Exception):
pass
def sparseMemory(dout, din, addr, we, en, clk):
memory = {}
while 1:
yield posedge(clk)
if not en:
continue
if we:
memory[addr] = din.val
else:
try:
dout.next = memory[addr]
except KeyError:
raise Error, "Uninitialized address %s" % hex(addr)
\end{verbatim}
This works by catching the low level data type exception, and raising
the custom exception with an appropriate error message instead. If
the \function{sparseMemory} function is defined in a module with the
same name, an access error is now reported as follows:
\begin{verbatim}
Traceback (most recent call last):
...
File "sparseMemory.py", line 57, in sparseMemory
raise Error, "Uninitialized address %s" % hex(addr)
__main__.Error: Uninitialized address 0x33
\end{verbatim}
Likewise, the \function{fifo} function can be adapted as follows, to
report underflow and overflow errors:
\begin{verbatim}
class Error(Exception):
pass
def fifo(dout, din, re, we, empty, full, clk, maxFilling=sys.maxint):
memory = []
while 1:
yield posedge(clk)
if we:
memory.insert(0, din.val)
if re:
try:
dout.next = memory.pop()
except IndexError:
raise Error, "Underflow -- Read from empty fifo"
filling = len(memory)
empty.next = (filling == 0)
full.next = (filling == maxFilling)
if filling > maxFilling:
raise Error, "Overflow -- Max filling %s exceeded" % maxFilling
\end{verbatim}
In this case, the underflow error is detected as before, by catching a
low level exception on the list data type. On the other hand, the
overflow error is detected by a regular check on the length of the
list.
\begin{quote}
\em
A lot to be added ...
\end{quote}

308
doc/manual/reference.tex Normal file
View File

@ -0,0 +1,308 @@
\chapter{Reference}
\myhdl\ is implemented as a Python package called \code{myhdl}. This
chapter describes the objects that are exported by this package.
\section{The \class{Simulation} class}
\begin{classdesc}{Simulation}{arg \optional{, arg \moreargs}}
Class to construct a new simulation. Each argument is either be a
\myhdl\ generator, or a nested sequence of such generators. (A nested
sequence is defined as a sequence in which each item may itself be a
nested sequence.) See section~\ref{myhdl-generators} for the
definition of \myhdl\ generators and their interaction with a
\class{Simulation} object.
As a special case, exactly one of the ``generators'' may be
a \class{Cosimulation} object (see Section~\ref{cosimulation}).
\end{classdesc}
A \class{Simulation} object has the following method:
\begin{methoddesc}[Simulation]{run}{\optional{duration}}
Run the simulation forever (by default) or for a specified duration.
\end{methoddesc}
\section{The \class{Signal} class}
\label{signal}
\begin{classdesc}{Signal}{val \optional{, delay}}
This class is used to construct a new signal and to initialize its
value to \var{val}. Optionally, a delay can be specified.
\end{classdesc}
A \class{Signal} object has the following attributes:
\begin{memberdesc}[Signal]{next}
Read-write attribute that represents the next value of the signal.
\end{memberdesc}
\begin{memberdesc}[Signal]{val}
Read-only attribute that represents the current value of the signal.
This attribute is always available to access the current value;
however in many practical case it will not be needed. Whenever there
is no ambiguity, the Signal object's current value is used
implicitly. In particular, all Python's standard numeric, bit-wise,
logical and comparison operators are implemented on a Signal object by
delegating to its current value. The exception is augmented
assignment. These operators are not implemented as they would break
the rule that the current value should be a read-only attribute. In
addition, when a Signal object is directly assigned to the \code{next}
attribute of another Signal object, its current value is assigned
instead.
\end{memberdesc}
\section{\myhdl\ generators and trigger objects}
\label{myhdl-generators}
\myhdl\ generators are standard Python generators with specialized
\keyword{yield} statements. In hardware description languages, the equivalent
statements are called \emph{sensitivity lists}. The general format
of \keyword{yield} statements in in \myhdl\ generators is:
\hspace{\leftmargin}\keyword{yield} \var{clause \optional{, clause ...}}
After a simulation object executes a \keyword{yield} statement, it
suspends execution of the generator. At the same time, each
\var{clause} is a \emph{trigger object} which defines the condition
upon which the generator should be resumed. However, per invocation of a
\keyword{yield} statement, the generator is resumed exactly once,
regardless of the number of clauses. This happens as soon as one
of the objects triggers; subsequent triggers are
neglected. (However, as a result of the resumption, it is possible
that the same \keyword{yield} statement is invoked again, and that a
subsequent trigger still triggers the generator.)
In this section, the trigger objects and their functionality will be
described.
\begin{funcdesc}{posedge}{signal}
Return a trigger object that specifies that the generator should
resume on a rising edge on the signal. A rising edge means a change
from false to true.
\end{funcdesc}
\begin{funcdesc}{negedge}{signal}
Return a trigger object that specifies that the generator should
resume on a falling edge on the signal. A falling edge means a change
from true to false.
\end{funcdesc}
\begin{funcdesc}{delay}{t}
Return a trigger object that specifies that the generator should
resume after a delay \var{t}.
\end{funcdesc}
\begin{funcdesc}{join}{arg \optional{, arg \moreargs}}
Join a number of trigger objects together and return a joined
trigger object. The effect is that the joined trigger object will
trigger when \emph{all} of its arguments have triggered.
\end{funcdesc}
In addition, some objects can directly be used as trigger
objects. These are the objects of the following types:
\begin{datadesc}{Signal}
For the full description of the \class{Signal} class, see
section~\ref{signal}.
A signal is a trigger object. Whenever a signal changes value, the
generator is triggered.
\end{datadesc}
\begin{datadesc}{GeneratorType}
\myhdl\ generators can be used as clauses in \code{yield}
statements. Such a generator is forked, while the original generator
waits for it to complete. The original generator resumes when the
forked generator returns.
\end{datadesc}
In addition, as a special case, the Python \code{None} object can be
present in a \code{yield} statement:
\begin{datadesc}{None}
This is the do-nothing trigger object. The generator immediately
resumes, as if no \code{yield} statement were present. This can be
useful if the \code{yield} statement also has generator clauses: those
generators are forked, while the original generator resumes
immediately.
\end{datadesc}
\section{Miscellaneous objects}
The following objects can be convenient in \myhdl\ modeling.
\begin{excclassdesc}{StopSimulation}{}
Base exception that is caught by the \code{Simulation.run} method to
stop a simulation. Can be subclassed and raised in generator code.
\end{excclassdesc}
\begin{funcdesc}{now}{}
Return the current simulation time.
\end{funcdesc}
\begin{funcdesc}{downrange}{high \optional{, low=0}}
Generate a downward range list of integers.
This function is modeled after the standard \code{range} function, but
works in the downward direction. The returned interval is half-open,
with the \var{high} index not included. \var{low} is optional and
defaults to zero. This function is especially useful in conjunction
with the \class{intbv} class, that also works with downward indexing.
\end{funcdesc}
\begin{funcdesc}{bin}{num \optional{, width}}
Return a representation as a bit string. If the optional \var{width}
is provided, and if it is larger than the width of the default
representation, the bit string is padded with the sign bit.
This function complements the standard Python conversion functions
\code{hex} and \code{oct}. A binary string representation is often
needed in hardware design.
\end{funcdesc}
\section{The \class{intbv} class}
\begin{classdesc}{intbv}{arg}
This class represents \class{int}-like objects with some additional
features that make it suitable for hardware design. The constructor
argument can be an \class{int}, a \class{long}, an \class{intbv} or a
bit string (a string with only '0's or '1's). For a bit string
argument, the value is calculated as in \code{int(\var{bitstring}, 2)}.
\end{classdesc}
Unlike \class{int} objects, \class{intbv} objects are mutable; this is
also the reason for their existence. Mutability is needed to support
assignment to indexes and slices, as is common in hardware design. For
the same reason, \class{intbv} is not a subclass from \class{int},
even though \class{int} provides most of the desired
functionality. (It is not possible to derive a mutable subtype from
an immutable base type.)
An \class{intbv} object supports the same comparison, numeric,
bitwise, logical, and conversion operations as \class{int} objects. See
\url{http://www.python.org/doc/current/lib/typesnumeric.html} for more
information on such operations. In all binary operations,
\class{intbv} objects can work together with \class{int} objects; in
those cases the return type is an \class{intbv} object.
In addition, \class{intbv} objects support indexing and slicing
operations:
\begin{tableiii}{clc}{code}{Operation}{Result}{Notes}
\lineiii{\var{bv}[\var{i}]}
{item \var{i} of \var{bv}}
{(1)}
\lineiii{\var{bv}[\var{i}] = \var{x}}
{item \var{i} of \var{bv} is replaced by \var{x}}
{(1)}
\lineiii{\var{bv}[\var{i}:\var{j}]}
{slice of \var{bv} from \var{i} downto \var{j}}
{(2)(3)}
\lineiii{\var{bv}[\var{i}:\var{j}] = \var{t}}
{slice of \var{bv} from \var{i} downto \var{j} is replaced
by \var{t}}
{(2)(4)}
\end{tableiii}
\begin{description}
\item[(1)] Indexing follows the most common hardware design
conventions: the lsb bit is the rightmost bit, and it has
index 0. This has the following desirable property: if the
\class{intbv} value is decomposed as a sum of powers of 2,
the bit with index \var{i} corresponds to the term
\code{2**i}.
\item[(2)] In contrast to standard Python sequencing conventions,
slicing range are downward. This is a consequence of the
indexing convention, combined with the common convention
that the most significant digits of a number are the
leftmost ones. The Python convention of half-open ranges is
followed: the bit with the highest index is not
included. However, it is the \emph{leftmost} bit in this
case. As in standard Python, this takes care of one-off
issues in many practical cases: in particular,
\code{bv[\var{i}:]} returns \var{i} bits;
\code{bv[\var{i}:\var{j}]} has \code{\var{i}-\var{j}}
bits. As \class{intbv} objects have no explicitly defined
bit width, the high index \var{j} has no default value and
cannot be omitted, while the low index \var{j} defaults to
\code{0}.
\item[(3)] The value returned from a slicing operation is always
positive; higher order bits are implicitly assumed to be
zero. The bit width is implicitly stored in the returned bit
width, so that the returned object can be used in
concatenations and as an iterator.
\item[(4)] In setting a slice, it is checked whether the slice is wide
enough to accept all significant bits of the value.
\end{description}
In addition, \class{intbv} objects support a concatenation method:
\begin{methoddesc}[intbv]{concat}{\optional{arg \moreargs}}
Concatenate the arguments to an \class{intbv} object. Naturally, the
concatenation arguments need to have a defined bit width. Therefore,
if they are \class{intbv} objects, they have to be the return values
of a slicing operation. Alternatively, they may be bit strings.
In contrast to all other arguments, the implicit \var{self} argument
doesn't need to have a defined bit with. This is due to the fact that
concatenation occurs at the lsb (rightmost) side.
It may be clearer to call this method as an unbound method with an
explicit first \class{intbv} argument.
\end{methoddesc}
In addition, an \class{intbv} object supports the iterator protocol. This
makes it possible to iterate over all its bits, from the high index to
index 0. This is only possible for \class{intbv} objects with a
defined bit width.
\section{Co-simulation support}
\subsection{\myhdl\ }
\label{cosimulation}
\begin{classdesc}{Cosimulation}{exe, **kwargs}
Class to construct a new Cosimulation object.
The \var{exe} argument is a command string to
execute an HDL simulation. The \var{kwargs} keyword
arguments provide a named association between signals
(regs \& nets) in the HDL simulator and signals in the
\myhdl\ simulator. Each keyword should be a name listed
in a \code{\$to_myhdl} or \code{\$from_myhdl} call in
the HDL code. Each argument should be a \class{Signal}
declared in the \myhdl\ code.
\end{classdesc}
\subsection{Verilog}
\begin{funcdesc}{\$to_myhdl}{arg, \optional{, arg \moreargs}}
Task that defines which signals (regs \& nets) should be
read by the \myhdl\ simulator.
This task should be called at the start of the simulation.
\end{funcdesc}
\begin{funcdesc}{\$from_myhdl}{arg, \optional{, arg \moreargs}}
Task that defines which signals should be
driven by the \myhdl\ simulator. In Verilog, only regs
can be specified.
This task should be called at the start of the simulation.
\end{funcdesc}
\subsection{VHDL}
Not implemented yet.

387
doc/manual/unittest.tex Normal file
View File

@ -0,0 +1,387 @@
\chapter{Unit testing}
\section{Introduction}
Many aspects in the design flow of modern digital hardware design can
be viewed as a special kind of software development. From that
viewpoint, it is a natural question whether advances in software
design techniques can not also be applied to hardware design.
One software design approach that gets a lot of attention recently is
\emph{Extreme Programming} (XP). It is a fascinating set of techniques and
guidelines that often seems to go against the conventional wisdom. On
other occasions, XP just seems to emphasize the common sense, which
doesn't always coincide with common practice. For example, XP stresses
the importance of normal workweeks, if we are to have the
fresh mind needed for good software development.
It is not my intention nor qualification to present a tutorial on
Extreme Programming. Instead, in this section I will highlight one XP
concept which I think is very relevant to hardware design: the
importance and methodology of unit testing.
\section{The importance of unit tests}
Unit testing is one of the corner stones of Extreme Programming. Other
XP concepts, such as collective ownership of code and continuous
refinement, are only possible by having unit tests. Moreover, XP
emphasizes that writing unit tests should be automated, that they should
test everything in every class, and that they should run perfectly all
the time.
I believe that these concepts apply directly to hardware design. In
addition, unit tests are a way to manage simulation time. For example,
a state machine that runs very slowly on infrequent events may be
impossible to verify at the system level, even on the fastest
simulator. On the other hand, it may be easy to verify it exhaustively
in a unit test, even on the slowest simulator.
It is clear that unit tests have compelling advantages. On the other
hand, if we need to test everything, we have to write
lots of unit tests. So it should be easy and pleasant
to create, manage and run them. Therefore, XP emphasizes the need for
a unit test framework that supports these tasks. In this chapter,
we will explore the use of the \code{unittest} module from
the standard Python library for creating unit tests for hardware
designs.
\section{Unit test development}
In this section, we will informally explore the application of unit
test techniques to hardware design. We will do so by a (small)
example: testing a binary to Gray encoder as introduced in
section~\ref{gray}.
\subsection{Defining the requirements}
We start by defining the requirements. For a Gray encoder, we want to
the output to comply with Gray code characteristics. Let's define a
\dfn{code} as a list of \dfn{codewords}, where a codeword is a bit
string. A code of order \code{n} has \code{2**n} codewords.
A well-known characteristic is the one that Gray codes are all about:
\newtheorem{reqGray}{Requirement}
\begin{reqGray}
Consecutive codewords in a Gray code should differ in a single bit.
\end{reqGray}
Is this sufficient? Not quite: suppose for example that an
implementation returns the lsb of each binary input. This would comply
with the requirement, but is obviously not what we want. Also, we don't
want the bit width of Gray codewords to exceed the bit width of the
binary codewords.
\begin{reqGray}
Each codeword in a Gray code of order n must occur exactly once in the
binary code of the same order.
\end{reqGray}
With the requirements written down we can proceed.
\subsection{Writing the test first}
A fascinating guideline in the XP world is to write the unit test
first. That is, before implementing something, first write the test
that will verify it. This seems to go against our natural inclination,
and certainly against common practices. Many engineers like to
implement first and think about verification afterwards.
But if you think about it, it makes a lot of sense to deal with
verification first. Verification is about the requirements only --- so
your thoughts are not yet cluttered with implementation details. The
unit tests are an executable description of the requirements, so they
will be better understood and it will be very clear what needs to be
done. Consequently, the implementation should go smoother. Perhaps
most importantly, the test is available when you are done
implementing, and can be run anytime by anybody to verify changes.
Python has a standard \code{unittest} module that facilitates writing,
managing and running unit tests. With \code{unittest}, a test case is
written by creating a class that inherits from
\code{unittest.TestCase}. Individual tests are created by methods of
that class: all method names that start with \code{test} are
considered to be tests of the test case.
We will define a test case for the Gray code properties, and then
write a test for each of the requirements. The outline of the test
case class is as follows:
\begin{verbatim}
from unittest import TestCase
class TestGrayCodeProperties(TestCase):
def testSingleBitChange(self):
""" Check that only one bit changes in successive codewords """
....
def testUniqueCodeWords(self):
""" Check that all codewords occur exactly once """
....
\end{verbatim}
Each method will be a small test bench that tests a single
requirement. To write the tests, we don't need an implementation of
the Gray encoder, but we do need the interface of the design. We can
specify this by a dummy implementation, as follows:
\begin{verbatim}
def bin2gray(B, G, width):
### NOT IMPLEMENTED YET! ###
yield None
\end{verbatim}
For the first requirement, we will write a test bench that applies all
consecutive input numbers, and compares the current output with the
previous one for each input. Then we check that the difference is a
single bit. We will test all Gray codes up to a certain order
\code{MAX_WIDTH}.
\begin{verbatim}
def testSingleBitChange(self):
""" Check that only one bit changes in successive codewords """
def test(B, G, width):
B.next = intbv(0)
yield delay(10)
for i in range(1, 2**width):
G_Z.next = G
B.next = intbv(i)
yield delay(10)
diffcode = bin(G ^ G_Z)
self.assertEqual(diffcode.count('1'), 1)
for width in range(1, MAX_WIDTH):
B = Signal(intbv(-1))
G = Signal(intbv(0))
G_Z = Signal(intbv(0))
dut = bin2gray(B, G, width)
check = test(B, G, width)
sim = Simulation(dut, check)
sim.run(quiet=1)
\end{verbatim}
Note how the actual check is performed by a \code{self.assertEqual}
method, defined by the \code{unittest.TestCase} class.
Similarly, we write a test bench for the second requirement. Again, we
simulate all numbers, and put the result in a list. The requirement
implies that if we sort the result list, we should get a range of
numbers:
\begin{verbatim}
def testUniqueCodeWords(self):
""" Check that all codewords occur exactly once """
def test(B, G, width):
actual = []
for i in range(2**width):
B.next = intbv(i)
yield delay(10)
actual.append(int(G))
actual.sort()
expected = range(2**width)
self.assertEqual(actual, expected)
for width in range(1, MAX_WIDTH):
B = Signal(intbv(-1))
G = Signal(intbv(0))
dut = bin2gray(B, G, width)
check = test(B, G, width)
sim = Simulation(dut, check)
sim.run(quiet=1)
\end{verbatim}
\subsection{Test-driven implementation}
With the test written, we begin with the implementation. For
illustration purposes, we will intentionally write some incorrect
implementations to see how the test behaves.
The easiest way to run tests defined with the \code{unittest}
framework, is to put a call to its \code{main} method at the end of
the test module:
\begin{verbatim}
unittest.main()
\end{verbatim}
Let's run the test using the dummy Gray encoder shown earlier:
\begin{verbatim}
% python test_gray.py -v
Check that only one bit changes in successive codewords ... FAIL
Check that all codewords occur exactly once ... FAIL
<trace backs not shown>
\end{verbatim}
As expected, this fails completely. Let us try an incorrect
implementation, that puts the lsb of in the input on the output:
\begin{verbatim}
def bin2gray(B, G, width):
### INCORRECT - DEMO PURPOSE ONLY! ###
while 1:
yield B
G.next = B[0]
\end{verbatim}
Running the test produces:
\begin{verbatim}
% python test_gray.py -v
Check that only one bit changes in successive codewords ... ok
Check that all codewords occur exactly once ... FAIL
======================================================================
FAIL: Check that all codewords occur exactly once
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_gray.py", line 109, in testUniqueCodeWords
sim.run(quiet=1)
...
File "test_gray.py", line 104, in test
self.assertEqual(actual, expected)
File "/usr/local/lib/python2.2/unittest.py", line 286, in failUnlessEqual
raise self.failureException, \
AssertionError: [0, 0, 1, 1] != [0, 1, 2, 3]
----------------------------------------------------------------------
Ran 2 tests in 0.785s
\end{verbatim}
Now the test passes the first requirement, as expected, but fails the
second one. After the test feedback, a full traceback is shown that
can help to debug the test output.
Finally, if we use the correct implementation as in
section~\ref{gray}, the output is:
\begin{verbatim}
% python test_gray.py -v
Check that only one bit changes in successive codewords ... ok
Check that all codewords occur exactly once ... ok
----------------------------------------------------------------------
Ran 2 tests in 6.364s
OK
\end{verbatim}
\subsection{Changing requirements}
In the previous section, we concentrated on the general requirements
of a Gray code. It is possible to specify these without specifying the
actual code. It is easy to see that there are several codes
that satisfy these requirements. In good XP style, we only tested
the requirements and nothing more.
It may be that more control is needed. For example, the requirement
may be for a particular code, instead of compliance with general
properties. As an illustration, we will show how to test for
\emph{the} original Gray code, which is one specific instance that
satisfies the requirements of the previous section. In this particular
case, this test will actually be easier than the previous one.
We denote the original Gray code of order \code{n} as \code{Ln}. Some
examples:
\begin{verbatim}
L1 = ['0', '1']
L2 = ['00', '01', '11', '10']
L3 = ['000', '001', '011', '010', '110', '111', '101', 100']
\end{verbatim}
It is possible to specify these codes by a recursive algorithm, as
follows:
\begin{enumerate}
\item L1 = ['0', '1']
\item Ln+1 can be obtained from Ln as follows. Create a new code Ln0 by
prefixing all codewords of Ln with '0'. Create another new code Ln1 by
prefixing all codewords of Ln with '1', and reversing their
order. Ln+1 is the concatenation of Ln0 and Ln1.
\end{enumerate}
Python is well-known for its elegant algorithmic
descriptions, and this is a good example. We can write the algorithm
in Python as follows:
\begin{verbatim}
def nextLn(Ln):
""" Return Gray code Ln+1, given Ln. """
Ln0 = ['0' + codeword for codeword in Ln]
Ln1 = ['1' + codeword for codeword in Ln]
Ln1.reverse()
return Ln0 + Ln1
\end{verbatim}
The code \samp{['0' + codeword for ...]} is called a \dfn{list
comprehension}. It is a concise way to describe lists built by short
computations in a for loop.
The requirement is now that the output code matches the
expected code Ln. We use the \code{nextLn} function to compute the
expected result. The new test case code is as follows:
\begin{verbatim}
class TestOriginalGrayCode(TestCase):
def testOriginalGrayCode(self):
""" Check that the code is an original Gray code """
Rn = []
def stimulus(B, G, n):
for i in range(2**n):
B.next = intbv(i)
yield delay(10)
Rn.append(bin(G, width=n))
Ln = ['0', '1'] # n == 1
for n in range(2, MAX_WIDTH):
Ln = nextLn(Ln)
del Rn[:]
B = Signal(intbv(-1))
G = Signal(intbv(0))
dut = bin2gray(B, G, n)
stim = stimulus(B, G, n)
sim = Simulation(dut, stim)
sim.run(quiet=1)
self.assertEqual(Ln, Rn)
\end{verbatim}
As it happens, our implementation is apparently an original Gray code:
\begin{verbatim}
% python test_gray.py -v TestOriginalGrayCode
Check that the code is an original Gray code ... ok
----------------------------------------------------------------------
Ran 1 tests in 3.091s
OK
\end{verbatim}

18
doc/paper-a4/pypaper.sty Normal file
View File

@ -0,0 +1,18 @@
%
% Change this to say a4paper instead of letterpaper if you want A4. These
% are the latex defaults.
%
\newcommand{\py@paper}{a4paper}
\newcommand{\py@ptsize}{11pt}
% These set up the fonts for the documents.
%
% The "times" package makes the default font the PostScript Times
% font, which makes for smaller PostScript and a font that more people
% like.
%
% The "avant" package causes the AvantGarde font to be used for
% sans-serif text, instead of the uglier Helvetica set up by the "times"
% package.
%
% \RequirePackage{times}\typeout{Using Times instead of Computer Modern.}

View File

@ -0,0 +1,18 @@
%
% Change this to say a4paper instead of letterpaper if you want A4. These
% are the latex defaults.
%
\newcommand{\py@paper}{letterpaper}
\newcommand{\py@ptsize}{11pt}
% These set up the fonts for the documents.
%
% The "times" package makes the default font the PostScript Times
% font, which makes for smaller PostScript and a font that more people
% like.
%
% The "avant" package causes the AvantGarde font to be used for
% sans-serif text, instead of the uglier Helvetica set up by the "times"
% package.
%
% \RequirePackage{times}\typeout{Using Times instead of Computer Modern.}

95
doc/perl/SynopsisTable.pm Normal file
View File

@ -0,0 +1,95 @@
package SynopsisTable;
sub new{
return bless {names=>'', info=>{}, file=>''};
}
sub declare{
my($self,$name,$key,$type) = @_;
if ($self->{names}) {
$self->{names} .= ",$name";
}
else {
$self->{names} .= "$name";
}
$self->{info}{$name} = "$key,$type,";
}
# The 'file' attribute is used to store the filename of the node in which
# the table will be presented; this assumes that each table will be presented
# only once, which works for the current use of this object.
sub set_file{
my($self, $filename) = @_;
$self->{file} = "$filename";
}
sub get_file{
my $self = shift;
return $self->{file};
}
sub set_synopsis{
my($self,$name,$synopsis) = @_;
my($key,$type,$unused) = split ',', $self->{info}{$name}, 3;
$self->{info}{$name} = "$key,$type,$synopsis";
}
sub get{
my($self,$name) = @_;
return split /,/, $self->{info}{$name}, 3;
}
sub show{
my $self = shift;
my $name;
print "names: ", $self->{names}, "\n\n";
foreach $name (split /,/, $self->{names}) {
my($key,$type,$synopsis) = $self->get($name);
print "$name($key) is $type: $synopsis\n";
}
}
sub tohtml{
my $self = shift;
my $oddrow = 1;
my $data = "<table class='synopsistable' valign='baseline'>\n";
my $name;
foreach $name (split /,/, $self->{names}) {
my($key,$type,$synopsis) = $self->get($name);
my $link = "<a href='module-$key.html'>";
$synopsis =~ s/<tex2html_percent_mark>/%/g;
$synopsis =~ s/<tex2html_ampersand_mark>/\&amp;/g;
$data .= (' <tr'
. ($oddrow ? " class='oddrow'>\n " : '>')
. "<td><b><tt class='module'>$link$name</a></tt></b></td>\n"
. " <td>\&nbsp;</td>\n"
. " <td class='synopsis'>$synopsis</td></tr>\n");
$oddrow = !$oddrow;
}
$data .= "</table>\n";
$data;
}
package testSynopsisTable;
sub test{
# this little test is mostly to debug the stuff above, since this is
# my first Perl "object".
my $st = SynopsisTable->new();
$st->declare("sample", "sample", "standard");
$st->set_synopsis("sample", "This is a little synopsis....");
$st->declare("copy_reg", "copyreg", "standard");
$st->set_synopsis("copy_reg", "pickle support stuff");
$st->show();
print "\n\n";
my $st2 = SynopsisTable->new();
$st2->declare("st2module", "st2module", "built-in");
$st2->set_synopsis("st2module", "silly little synopsis");
$st2->show();
}
1; # This must be the last line -- Perl is bogus!

21
doc/perl/distutils.perl Normal file
View File

@ -0,0 +1,21 @@
# LaTeX2HTML support for distutils.sty.
package main;
sub do_cmd_command {
return use_wrappers(@_[0], '<code>', '</code>');
}
sub do_cmd_option {
return use_wrappers(@_[0], '<font face="sans-serif">', '</font>');
}
sub do_cmd_filevar {
return use_wrappers(@_[0], '<font face="sans-serif"><i>', '</i></font>');
}
sub do_cmd_XXX {
return use_wrappers(@_[0], '<b>** ', ' **</b>');
}
1;

12
doc/perl/howto.perl Normal file
View File

@ -0,0 +1,12 @@
# -*- perl -*-
#
# This implements the Python howto class. All it really needs to do it
# load the "python" style.
package main;
do_require_package("article");
do_require_package("alltt");
do_require_package("python");
1; # sheesh....

12
doc/perl/isilo.perl Normal file
View File

@ -0,0 +1,12 @@
package main;
$USING_STYLES = 0;
$NO_NAVIGATION = 1;
$INDEX_COLUMNS = 1;
$MODULE_INDEX_COLUMNS = 1;
sub child_line {
return '';
}
1; # stupid perl...

672
doc/perl/l2hinit.perl Normal file
View File

@ -0,0 +1,672 @@
# LaTeX2HTML support base for use with Python documentation.
package main;
use L2hos;
$HTML_VERSION = 4.0;
$MAX_LINK_DEPTH = 2;
$ADDRESS = '';
$NO_FOOTNODE = 1;
$NUMBERED_FOOTNOTES = 1;
# Python documentation uses section numbers to support references to match
# in the printed and online versions.
#
$SHOW_SECTION_NUMBERS = 1;
$ICONSERVER = '.';
$IMAGE_TYPE = 'gif';
# Control where the navigation bars should show up:
$TOP_NAVIGATION = 1;
$BOTTOM_NAVIGATION = 1;
$AUTO_NAVIGATION = 0;
$BODYTEXT = '';
$CHILDLINE = "\n<p><hr>\n";
$VERBOSITY = 0;
# default # of columns for the indexes
$INDEX_COLUMNS = 2;
$MODULE_INDEX_COLUMNS = 4;
$HAVE_MODULE_INDEX = 0;
$HAVE_GENERAL_INDEX = 0;
$HAVE_TABLE_OF_CONTENTS = 0;
$AESOP_META_TYPE = 'information';
# A little painful, but lets us clean up the top level directory a little,
# and not be tied to the current directory (as far as I can tell). Testing
# an existing definition of $mydir is needed since it cannot be computed when
# run under mkhowto with recent versions of LaTeX2HTML, since this file is
# not read directly by LaTeX2HTML any more. mkhowto is required to prepend
# the required definition at the top of the actual input file.
#
if (!defined $mydir) {
use Cwd;
use File::Basename;
($myname, $mydir, $myext) = fileparse(__FILE__, '\..*');
chop $mydir; # remove trailing '/'
$mydir = getcwd() . "$dd$mydir"
unless $mydir =~ s|^/|/|;
}
$LATEX2HTMLSTYLES = "$mydir$envkey$LATEX2HTMLSTYLES";
push (@INC, $mydir);
($myrootname, $myrootdir, $myext) = fileparse($mydir, '\..*');
chop $myrootdir;
# Hackish way to get the appropriate paper-*/ directory into $TEXINPUTS;
# pass in the paper size (a4 or letter) as the environment variable PAPER
# to add the right directory. If not given, the current directory is
# added instead for use with HOWTO processing.
#
if (defined $ENV{'PAPER'}) {
$mytexinputs = "$myrootdir${dd}paper-$ENV{'PAPER'}$envkey";
}
else {
$mytexinputs = getcwd() . $envkey;
}
$mytexinputs .= "$myrootdir${dd}texinputs";
# Change this variable to change the text added in "About this document...";
# this should be an absolute pathname to get it right.
#
$ABOUT_FILE = "$myrootdir${dd}html${dd}stdabout.dat";
sub custom_driver_hook {
#
# This adds the directory of the main input file to $TEXINPUTS; it
# seems to be sufficiently general that it should be fine for HOWTO
# processing.
#
my $file = $_[0];
my($jobname, $dir, $ext) = fileparse($file, '\..*');
$dir = L2hos->Make_directory_absolute($dir);
$dir =~ s/$dd$//;
$TEXINPUTS = "$dir$envkey$mytexinputs";
print "\nAdding $dir to \$TEXINPUTS\n";
}
$CUSTOM_BUTTONS = '';
sub make_nav_sectref($$) {
my($label, $title) = @_;
if ($title) {
if ($title =~ /\<[aA] /) {
$title =~ s/\<[aA] /<a class="sectref" /;
}
else {
$title = "<span class=\"sectref\">$title</span>";
}
return "<b class=\"navlabel\">$label:</b> $title\n";
}
return '';
}
@my_icon_tags = ();
$my_icon_tags{'next'} = 'Next Page';
$my_icon_tags{'next_page'} = 'Next Page';
$my_icon_tags{'previous'} = 'Previous Page';
$my_icon_tags{'previous_page'} = 'Previous Page';
$my_icon_tags{'up'} = 'Up One Level';
$my_icon_tags{'contents'} = 'Contents';
$my_icon_tags{'index'} = 'Index';
$my_icon_tags{'modules'} = 'Module Index';
@my_icon_names = ();
$my_icon_names{'previous_page'} = 'previous';
$my_icon_names{'next_page'} = 'next';
sub get_my_icon($) {
my $name = $_[0];
my $text = $my_icon_tags{$name};
if ($my_icon_names{$name}) {
$name = $my_icon_names{$name};
}
if ($text eq '') {
$name = 'blank';
}
my $iconserver = ($ICONSERVER eq '.') ? '' : "$ICONSERVER/";
return "<img src='$iconserver$name.$IMAGE_TYPE'\n border='0'"
. " height='32' alt='$text' width='32'>";
}
sub use_my_icon($) {
my $s = $_[0];
if ($s =~ /\<tex2html_([a-z_]+)_visible_mark\>/) {
my $r = get_my_icon($1);
$s =~ s/\<tex2html_[a-z_]+_visible_mark\>/$r/;
}
return $s;
}
sub make_nav_panel() {
my $s;
my $BLANK_ICON = get_my_icon('blank');
$NEXT = $NEXT_TITLE ? use_my_icon("$NEXT") : $BLANK_ICON;
$UP = $UP_TITLE ? use_my_icon("$UP") : $BLANK_ICON;
$PREVIOUS = $PREVIOUS_TITLE ? use_my_icon("$PREVIOUS") : $BLANK_ICON;
$CONTENTS = use_my_icon("$CONTENTS");
$INDEX = $INDEX ? use_my_icon("$INDEX") : $BLANK_ICON;
if (!$CUSTOM_BUTTONS) {
$CUSTOM_BUTTONS = $BLANK_ICON;
}
$s = ('<table align="center" width="100%" cellpadding="0" cellspacing="2">'
. "\n<tr>"
# left-hand side
. "\n<td>$PREVIOUS</td>"
. "\n<td>$UP</td>"
. "\n<td>$NEXT</td>"
# title box
. "\n<td align=\"center\" width=\"100%\">$t_title</td>"
# right-hand side
. "\n<td>$CONTENTS</td>"
. "\n<td>$CUSTOM_BUTTONS</td>" # module index
. "\n<td>$INDEX</td>"
. "\n</tr></table>\n"
# textual navigation
. make_nav_sectref("Previous", $PREVIOUS_TITLE)
. make_nav_sectref("Up", $UP_TITLE)
. make_nav_sectref("Next", $NEXT_TITLE)
);
# remove these; they are unnecessary and cause errors from validation
$s =~ s/ NAME="tex2html\d+"\n */ /g;
return $s;
}
sub add_child_links {
my $toc = add_real_child_links(@_);
$toc =~ s|\s*</[aA]>|</a>|g;
$toc =~ s/ NAME=\"tex2html\d+\"\s*href=/ href=/gi;
$toc =~ s|</UL>(\s*<BR>)?|</ul>|gi;
return $toc;
}
sub get_version_text() {
if ($PACKAGE_VERSION ne '' && $t_date) {
return ("<span class=\"release-info\">"
. "Release $PACKAGE_VERSION$RELEASE_INFO,"
. " documentation updated on $t_date.</span>");
}
if ($PACKAGE_VERSION ne '') {
return ("<span class=\"release-info\">"
. "Release $PACKAGE_VERSION$RELEASE_INFO.</span>");
}
if ($t_date) {
return ("<span class=\"release-info\">Documentation released on "
. "$t_date.</span>");
}
return '';
}
sub top_navigation_panel() {
return "\n"
. make_nav_panel()
. "<br><hr>\n";
}
sub bot_navigation_panel() {
return "\n<p><hr>\n"
. make_nav_panel()
. "<hr>\n"
. get_version_text()
. "\n";
}
sub add_link {
# Returns a pair (iconic link, textual link)
my($icon, $current_file, @link) = @_;
my($dummy, $file, $title) = split($delim,
$section_info{join(' ',@link)});
if ($icon =~ /\<tex2html_([_a-z]+)_visible_mark\>/) {
my $r = get_my_icon($1);
$icon =~ s/\<tex2html_[_a-z]+_visible_mark\>/$r/;
}
if ($title && ($file ne $current_file)) {
$title = purify($title);
$title = get_first_words($title, $WORDS_IN_NAVIGATION_PANEL_TITLES);
return (make_href($file, $icon), make_href($file, "$title"))
}
elsif ($icon eq get_my_icon('up') && $EXTERNAL_UP_LINK) {
return (make_href($EXTERNAL_UP_LINK, $icon),
make_href($EXTERNAL_UP_LINK, "$EXTERNAL_UP_TITLE"))
}
elsif ($icon eq get_my_icon('previous')
&& $EXTERNAL_PREV_LINK && $EXTERNAL_PREV_TITLE) {
return (make_href($EXTERNAL_PREV_LINK, $icon),
make_href($EXTERNAL_PREV_LINK, "$EXTERNAL_PREV_TITLE"))
}
elsif ($icon eq get_my_icon('next')
&& $EXTERNAL_DOWN_LINK && $EXTERNAL_DOWN_TITLE) {
return (make_href($EXTERNAL_DOWN_LINK, $icon),
make_href($EXTERNAL_DOWN_LINK, "$EXTERNAL_DOWN_TITLE"))
}
return (&inactive_img($icon), "");
}
sub add_special_link($$$) {
my($icon, $file, $current_file) = @_;
if ($icon =~ /\<tex2html_([_a-z]+)_visible_mark\>/) {
my $r = get_my_icon($1);
$icon =~ s/\<tex2html_[_a-z]+_visible_mark\>/$r/;
}
return (($file && ($file ne $current_file))
? make_href($file, $icon)
: undef)
}
# The img_tag() function seems only to be called with the parameter
# 'anchor_invisible_mark', which we want to turn into ''. Since
# replace_icon_marks() is the only interesting caller, and all it really
# does is call img_tag(), we can just define the hook alternative to be
# a no-op instead.
#
sub replace_icons_hook {}
sub do_cmd_arabic {
# get rid of that nasty <SPAN CLASS="arabic">...</SPAN>
my($ctr, $val, $id, $text) = &read_counter_value($_[0]);
return ($val ? farabic($val) : "0") . $text;
}
sub gen_index_id($$) {
# this is used to ensure common index key generation and a stable sort
my($str, $extra) = @_;
sprintf('%s###%s%010d', $str, $extra, ++$global{'max_id'});
}
sub insert_index($$$$$) {
my($mark, $datafile, $columns, $letters, $prefix) = @_;
my $prog = "$myrootdir/tools/buildindex.py";
my $index;
if ($letters) {
$index = `$prog --columns $columns --letters $datafile`;
}
else {
$index = `$prog --columns $columns $datafile`;
}
if (!s/$mark/$prefix$index/) {
print "\nCould not locate index mark: $mark";
}
}
sub add_idx() {
print "\nBuilding HTML for the index ...";
close(IDXFILE);
insert_index($idx_mark, 'index.dat', $INDEX_COLUMNS, 1, '');
}
$idx_module_mark = '<tex2html_idx_module_mark>';
$idx_module_title = 'Module Index';
sub add_module_idx() {
print "\nBuilding HTML for the module index ...";
my $key;
my $first = 1;
my $prevplat = '';
my $allthesame = 1;
my $prefix = '';
foreach $key (keys %Modules) {
$key =~ s/<tt>([a-zA-Z0-9._]*)<\/tt>/$1/;
my $plat = "$ModulePlatforms{$key}";
$plat = ''
if ($plat eq $IGNORE_PLATFORM_ANNOTATION);
if (!$first) {
$allthesame = 0
if ($prevplat ne $plat);
}
else { $first = 0; }
$prevplat = $plat;
}
open(MODIDXFILE, '>modindex.dat') || die "\n$!\n";
foreach $key (keys %Modules) {
# dump the line in the data file; just use a dummy seqno field
my $nkey = $1;
my $moditem = "$Modules{$key}";
my $plat = '';
$key =~ s/<tt>([a-zA-Z0-9._]*)<\/tt>/$1/;
if ($ModulePlatforms{$key} && !$allthesame) {
$plat = (" <em>(<span class=\"platform\">$ModulePlatforms{$key}"
. '</span>)</em>');
}
print MODIDXFILE $moditem . $IDXFILE_FIELD_SEP
. "<tt class=\"module\">$key</tt>$plat###\n";
}
close(MODIDXFILE);
if ($GLOBAL_MODULE_INDEX) {
$prefix = <<MODULE_INDEX_PREFIX;
<p> This index only lists modules documented in this manual.
The <em class="citetitle"><a href="$GLOBAL_MODULE_INDEX">Global Module
Index</a></em> lists all modules that are documented in this set
of manuals.</p>
MODULE_INDEX_PREFIX
}
if (!$allthesame) {
$prefix .= <<PLAT_DISCUSS;
<p> Some module names are followed by an annotation indicating what
platform they are available on.</p>
PLAT_DISCUSS
}
insert_index($idx_module_mark, 'modindex.dat', $MODULE_INDEX_COLUMNS, 0,
$prefix);
}
# replace both indexes as needed:
sub add_idx_hook {
add_idx() if (/$idx_mark/);
process_python_state();
if ($MODULE_INDEX_FILE) {
local ($_);
open(MYFILE, "<$MODULE_INDEX_FILE");
sysread(MYFILE, $_, 1024*1024);
close(MYFILE);
add_module_idx();
open(MYFILE,">$MODULE_INDEX_FILE");
print MYFILE $_;
close(MYFILE);
}
}
# In addition to the standard stuff, add label to allow named node files and
# support suppression of the page complete (for HTML Help use).
sub do_cmd_tableofcontents {
local($_) = @_;
$TITLE = $toc_title;
$tocfile = $CURRENT_FILE;
my($closures, $reopens) = preserve_open_tags();
anchor_label('contents', $CURRENT_FILE, $_); # this is added
join('', "<BR>\n\\tableofchildlinks[off]", $closures
, make_section_heading($toc_title, 'H2'), $toc_mark
, $reopens, $_);
}
# In addition to the standard stuff, add label to allow named node files.
sub do_cmd_listoffigures {
local($_) = @_;
$TITLE = $lof_title;
$loffile = $CURRENT_FILE;
my($closures, $reopens) = preserve_open_tags();
anchor_label('lof', $CURRENT_FILE, $_); # this is added
join('', "<BR>\n", $closures
, make_section_heading($lof_title, 'H2'), $lof_mark
, $reopens, $_);
}
# In addition to the standard stuff, add label to allow named node files.
sub do_cmd_listoftables {
local($_) = @_;
$TITLE = $lot_title;
$lotfile = $CURRENT_FILE;
my($closures, $reopens) = preserve_open_tags();
anchor_label('lot', $CURRENT_FILE, $_); # this is added
join('', "<BR>\n", $closures
, make_section_heading($lot_title, 'H2'), $lot_mark
, $reopens, $_);
}
# In addition to the standard stuff, add label to allow named node files.
sub do_cmd_textohtmlinfopage {
local($_) = @_;
if ($INFO) { #
anchor_label("about",$CURRENT_FILE,$_); # this is added
} #
my $the_version = ''; # and the rest is
if ($t_date) { # mostly ours
$the_version = ",\n$t_date";
if ($PACKAGE_VERSION) {
$the_version .= ", Release $PACKAGE_VERSION$RELEASE_INFO";
}
}
my $about;
open(ABOUT, "<$ABOUT_FILE") || die "\n$!\n";
sysread(ABOUT, $about, 1024*1024);
close(ABOUT);
$_ = (($INFO == 1)
? join('',
$close_all,
"<strong>$t_title</strong>$the_version\n",
$about,
$open_all, $_)
: join('', $close_all, $INFO,"\n", $open_all, $_));
$_;
}
# $idx_mark will be replaced with the real index at the end
sub do_cmd_textohtmlindex {
local($_) = @_;
$TITLE = $idx_title;
$idxfile = $CURRENT_FILE;
if (%index_labels) { make_index_labels(); }
if (($SHORT_INDEX) && (%index_segment)) { make_preindex(); }
else { $preindex = ''; }
my $heading = make_section_heading($idx_title, 'h2') . $idx_mark;
my($pre, $post) = minimize_open_tags($heading);
anchor_label('genindex',$CURRENT_FILE,$_); # this is added
return "<br>\n" . $pre . $_;
}
$MODULE_INDEX_FILE = '';
# $idx_module_mark will be replaced with the real index at the end
sub do_cmd_textohtmlmoduleindex {
local($_) = @_;
$TITLE = $idx_module_title;
anchor_label('modindex', $CURRENT_FILE, $_);
$MODULE_INDEX_FILE = "$CURRENT_FILE";
$_ = ('<p>' . make_section_heading($idx_module_title, 'h2')
. $idx_module_mark . $_);
return $_;
}
# The bibliography and the index should be treated as separate
# sections in their own HTML files. The \bibliography{} command acts
# as a sectioning command that has the desired effect. But when the
# bibliography is constructed manually using the thebibliography
# environment, or when using the theindex environment it is not
# possible to use the normal sectioning mechanism. This subroutine
# inserts a \bibliography{} or a dummy \textohtmlindex command just
# before the appropriate environments to force sectioning.
# XXX This *assumes* that if there are two {theindex} environments,
# the first is the module index and the second is the standard
# index. This is sufficient for the current Python documentation,
# but that's about it.
sub add_bbl_and_idx_dummy_commands {
my $id = $global{'max_id'};
if (/[\\]tableofcontents/) {
$HAVE_TABLE_OF_CONTENTS = 1;
}
s/([\\]begin\s*$O\d+$C\s*thebibliography)/$bbl_cnt++; $1/eg;
s/([\\]begin\s*$O\d+$C\s*thebibliography)/$id++; "\\bibliography$O$id$C$O$id$C $1"/geo;
my(@parts) = split(/\\begin\s*$O\d+$C\s*theindex/);
if (scalar(@parts) == 3) {
# Be careful to re-write the string in place, since $_ is *not*
# returned explicity; *** nasty side-effect dependency! ***
print "\nadd_bbl_and_idx_dummy_commands ==> adding general index";
print "\nadd_bbl_and_idx_dummy_commands ==> adding module index";
my $rx = "([\\\\]begin\\s*$O\\d+$C\\s*theindex[\\s\\S]*)"
. "([\\\\]begin\\s*$O\\d+$C\\s*theindex)";
s/$rx/\\textohtmlmoduleindex $1 \\textohtmlindex $2/o;
# Add a button to the navigation areas:
$CUSTOM_BUTTONS .= ('<a href="modindex.html" title="Module Index">'
. get_my_icon('modules')
. '</a>');
$HAVE_MODULE_INDEX = 1;
$HAVE_GENERAL_INDEX = 1;
}
elsif (scalar(@parts) == 2) {
print "\nadd_bbl_and_idx_dummy_commands ==> adding general index";
my $rx = "([\\\\]begin\\s*$O\\d+$C\\s*theindex)";
s/$rx/\\textohtmlindex $1/o;
$HAVE_GENERAL_INDEX = 1;
}
elsif (scalar(@parts) == 1) {
print "\nadd_bbl_and_idx_dummy_commands ==> no index found";
$CUSTOM_BUTTONS .= get_my_icon('blank');
$global{'max_id'} = $id; # not sure why....
s/([\\]begin\s*$O\d+$C\s*theindex)/\\textohtmlindex $1/o;
s/[\\]printindex/\\textohtmlindex /o;
}
else {
die "\n\nBad number of index environments!\n\n";
}
#----------------------------------------------------------------------
lib_add_bbl_and_idx_dummy_commands()
if defined(&lib_add_bbl_and_idx_dummy_commands);
}
# The bibliographic references, the appendices, the lists of figures
# and tables etc. must appear in the contents table at the same level
# as the outermost sectioning command. This subroutine finds what is
# the outermost level and sets the above to the same level;
sub set_depth_levels {
# Sets $outermost_level
my $level;
#RRM: do not alter user-set value for $MAX_SPLIT_DEPTH
foreach $level ("part", "chapter", "section", "subsection",
"subsubsection", "paragraph") {
last if (($outermost_level) = /\\($level)$delimiter_rx/);
}
$level = ($outermost_level ? $section_commands{$outermost_level} :
do {$outermost_level = 'section'; 3;});
#RRM: but calculate value for $MAX_SPLIT_DEPTH when a $REL_DEPTH was given
if ($REL_DEPTH && $MAX_SPLIT_DEPTH) {
$MAX_SPLIT_DEPTH = $level + $MAX_SPLIT_DEPTH;
} elsif (!($MAX_SPLIT_DEPTH)) { $MAX_SPLIT_DEPTH = 1 };
%unnumbered_section_commands = ('tableofcontents' => $level,
'listoffigures' => $level,
'listoftables' => $level,
'bibliography' => $level,
'textohtmlindex' => $level,
'textohtmlmoduleindex' => $level);
$section_headings{'textohtmlmoduleindex'} = 'h1';
%section_commands = (%unnumbered_section_commands,
%section_commands);
make_sections_rx();
}
# This changes the markup used for {verbatim} environments, and is the
# best way I've found that ensures the <dl> goes on the outside of the
# <pre>...</pre>.
#
# Note that this *must* be done in the init file, not the python.perl
# style support file. The %declarations must be set before
# initialize() is called in the main LaTeX2HTML script (which happens
# before style files are loaded).
#
%declarations = ('preform' => '<div class="verbatim"><pre></pre></div>',
%declarations);
# This is added to get rid of the long comment that follows the
# doctype declaration; MSIE5 on NT4 SP4 barfs on it and drops the
# content of the page.
$MY_PARTIAL_HEADER = '';
sub make_head_and_body($$) {
my($title, $body) = @_;
$body = " $body" unless ($body eq '');
my $DTDcomment = '';
my($version, $isolanguage) = ($HTML_VERSION, 'EN');
my %isolanguages = ( 'english', 'EN' , 'USenglish', 'EN.US'
, 'original', 'EN' , 'german' , 'DE'
, 'austrian', 'DE.AT', 'french' , 'FR'
, 'spanish', 'ES');
$isolanguage = $isolanguages{$default_language};
$isolanguage = 'EN' unless $isolanguage;
$title = &purify($title,1);
eval("\$title = ". $default_title ) unless ($title);
# allow user-modification of the <TITLE> tag; thanks Dan Young
if (defined &custom_TITLE_hook) {
$title = &custom_TITLE_hook($title, $toc_sec_title);
}
if ($DOCTYPE =~ /\/\/[\w\.]+\s*$/) { # language spec included
$DTDcomment = "<!DOCTYPE html PUBLIC \"$DOCTYPE\">\n";
} else {
$DTDcomment = "<!DOCTYPE html PUBLIC \"$DOCTYPE//"
. ($ISO_LANGUAGE ? $ISO_LANGUAGE : $isolanguage) . "\">\n";
}
if ($MY_PARTIAL_HEADER eq '') {
$STYLESHEET = $FILE.".css" unless $STYLESHEET;
$MY_PARTIAL_HEADER = join('',
($DOCTYPE ? $DTDcomment : ''),
"<html>\n<head>\n",
($BASE ? "<base href=\"$BASE\">\n" : ''),
"<link rel=\"STYLESHEET\" href=\"$STYLESHEET\" type='text/css'>\n",
($FAVORITES_ICON
? ('<link rel="SHORTCUT ICON" href="' . "$FAVORITES_ICON\">\n")
: ''),
($EXTERNAL_UP_LINK
? ('<link rel="start" href="' . "$EXTERNAL_UP_LINK\""
. ($EXTERNAL_UP_TITLE ? " title='$EXTERNAL_UP_TITLE'" : '')
. ">\n")
: ''),
"<link rel=\"first\" href=\"$FILE.html\"",
($t_title ? " title='$t_title'" : ''),
">\n",
($HAVE_TABLE_OF_CONTENTS
? ('<link rel="contents" href="contents.html" title="Contents">'
. ($HAVE_GENERAL_INDEX ? "\n" : ''))
: ''),
($HAVE_GENERAL_INDEX
? '<link rel="index" href="genindex.html" title="Index">' . "\n"
: ''),
# disable for now -- Mozilla doesn't do well with multiple indexes
# ($HAVE_MODULE_INDEX
# ? '<link rel="index" href="modindex.html" title="Module Index">'
# . "\n"
# : ''),
($INFO
# XXX We can do this with the Python tools since the About...
# page always gets copied to about.html, even when we use the
# generated node###.html page names. Won't work with the
# rest of the Python doc tools.
? ("<link rel='last' href='about.html'"
. " title='About this document...'>\n"
. "<link rel='help' href='about.html'"
. " title='About this document...'>\n")
: ''),
$more_links_mark,
"\n",
($CHARSET && $HTML_VERSION ge "2.1"
? ('<meta http-equiv="Content-Type" content="text/html; '
. "charset=$CHARSET\">\n")
: ''),
($AESOP_META_TYPE
? "<meta name='aesop' content='$AESOP_META_TYPE'>\n" : ''));
}
if (!$charset && $CHARSET) {
$charset = $CHARSET;
$charset =~ s/_/\-/go;
}
join('',
$MY_PARTIAL_HEADER,
&meta_information($title),
"<title>", $title, "</title>\n</head>\n<body$body>");
}
1; # This must be the last line

67
doc/perl/ltxmarkup.perl Normal file
View File

@ -0,0 +1,67 @@
# LaTeX2HTML support for the ltxmarkup package. Doesn't do indexing.
package main;
sub ltx_next_argument{
my $param;
$param = missing_braces()
unless ((s/$next_pair_pr_rx/$param=$2;''/eo)
||(s/$next_pair_rx/$param=$2;''/eo));
return $param;
}
sub do_cmd_macro{
local($_) = @_;
my $macro = ltx_next_argument();
return "<tt class='macro'>&#92;$macro</tt>" . $_;
}
sub do_cmd_env{
local($_) = @_;
my $env = ltx_next_argument();
return "<tt class='environment'>&#92;$env</tt>" . $_;
}
sub ltx_process_params{
# Handle processing of \p and \op for parameter specifications for
# envdesc and macrodesc. It's done this way to avoid defining do_cmd_p()
# and do_cmd_op() functions, which would be interpreted outside the context
# in which these commands are legal, and cause LaTeX2HTML to think they're
# defined. This way, other uses of \p and \op are properly flagged as
# unknown macros.
my $s = @_[0];
$s =~ s%\\op<<(\d+)>>(.+)<<\1>>%<tt>[</tt><var>$2</var><tt>]</tt>%;
while ($s =~ /\\p<<(\d+)>>(.+)<<\1>>/) {
$s =~ s%\\p<<(\d+)>>(.+)<<\1>>%<tt>{</tt><var>$2</var><tt>}</tt>%;
}
return $s;
}
sub do_env_macrodesc{
local($_) = @_;
my $macro = ltx_next_argument();
my $params = ltx_process_params(ltx_next_argument());
return "\n<dl class='macrodesc'>"
. "\n<dt><b><tt class='macro'>&#92;$macro</tt></b>"
. "\n $params"
. "\n<dd>"
. $_
. "</dl>";
}
sub do_env_envdesc{
local($_) = @_;
my $env = ltx_next_argument();
my $params = ltx_process_params(ltx_next_argument());
return "\n<dl class='envdesc'>"
. "\n<dt><tt>&#92;begin{<b class='environment'>$env</b>}</tt>"
. "\n $params"
. "\n<br /><tt>&#92;end{<b class='environment'>$env</b>}</tt>"
. "\n<dd>"
. $_
. "</dl>";
}
1; # Must end with this, because Perl is bogus.

15
doc/perl/manual.perl Normal file
View File

@ -0,0 +1,15 @@
# -*- perl -*-
#
# This implements the Python manual class. All it really needs to do it
# load the "python" style. The style code is not moved into the class code
# at this time, since we expect additional document class to be developed
# for the Python documentation in the future. Appropriate relocations will
# be made at that time.
package main;
do_require_package("report");
do_require_package("alltt");
do_require_package("python");
1; # sheesh....

2123
doc/perl/python.perl Normal file

File diff suppressed because it is too large Load Diff

View File

@ -12,8 +12,8 @@ software.
In May 2000, Guido and the Python core development team moved to
BeOpen.com to form the BeOpen PythonLabs team. In October of the same
year, the PythonLabs team moved to Zope Corporation (then Digital
Creations; see \url{http://www.zope.com/}). In 2001, the Python
year, the PythonLabs team moved to Digital Creations (now Zope
Corporation; see \url{http://www.zope.com/}). In 2001, the Python
Software Foundation (PSF, see \url{http://www.python.org/psf/}) was
formed, a non-profit organization created specifically to own
Python-related Intellectual Property. Zope Corporation is a
@ -51,7 +51,7 @@ direction to make these releases possible.
\section{Terms and conditions for accessing or otherwise using Python}
\centerline{\strong{PSF LICENSE AGREEMENT FOR PYTHON 2.2}}
\centerline{\strong{PSF LICENSE AGREEMENT FOR PYTHON 2.3}}
\begin{enumerate}
\item

View File

@ -21,8 +21,8 @@
\let\moreargs=\py@moreargs%
#2}}]
\item[\code{\e end\{{\bfseries #1}\}}]
\index{#1 environment@\idxcode{#1} environment}
\index{environments!#1@\idxcode{#1}}
\index{#1 environment@\py@idxcode{#1} environment}
\index{environments!#1@\py@idxcode{#1}}
}{\end{fulllineitems}}
\newenvironment{macrodesc}[2]{
@ -33,7 +33,7 @@
\let\unspecified=\py@unspecified%
\let\moreargs=\py@moreargs%
#2}}]
\index{#1@\idxcode{\e #1}}
\index{#1@\py@idxcode{#1}}
}{\end{fulllineitems}}
\newcommand{\env}[1]{\code{#1}}

View File

@ -2,16 +2,14 @@
% Change this to say a4paper instead of letterpaper if you want A4. These
% are the latex defaults.
%
%\newcommand{\py@paper}{letterpaper}
%\newcommand{\py@ptsize}{10pt}
\newcommand{\py@paper}{a4paper}
\newcommand{\py@paper}{letterpaper}
\newcommand{\py@ptsize}{11pt}
% These set up the fonts for the documents.
%
% The "times" package makes the default font the PostScript Times
% font, which makes for smaller PostScript and a font that more people
% like.g
% like.
%
% The "avant" package causes the AvantGarde font to be used for
% sans-serif text, instead of the uglier Helvetica set up by the "times"

View File

@ -0,0 +1,18 @@
%
% Change this to say a4paper instead of letterpaper if you want A4. These
% are the latex defaults.
%
\newcommand{\py@paper}{letterpaper}
\newcommand{\py@ptsize}{11pt}
% These set up the fonts for the documents.
%
% The "times" package makes the default font the PostScript Times
% font, which makes for smaller PostScript and a font that more people
% like.
%
% The "avant" package causes the AvantGarde font to be used for
% sans-serif text, instead of the uglier Helvetica set up by the "times"
% package.
%
% \RequirePackage{times}\typeout{Using Times instead of Computer Modern.}

View File

@ -134,6 +134,23 @@
% Use this to set the font family for headers and other decor:
\newcommand{\py@HeaderFamily}{\sffamily}
% Set up abstract ways to get the normal and smaller font sizes that
% work even in footnote context.
\newif\ifpy@infootnote \py@infootnotefalse
\let\py@oldmakefntext\@makefntext
\def\@makefntext#1{%
\bgroup%
\py@infootnotetrue
\py@oldmakefntext{#1}%
\egroup%
}
\def\py@defaultsize{%
\ifpy@infootnote\footnotesize\else\normalsize\fi%
}
\def\py@smallsize{%
\ifpy@infootnote\scriptsize\else\small\fi%
}
% Redefine the 'normal' header/footer style when using "fancyhdr" package:
\@ifundefined{fancyhf}{}{
% Use \pagestyle{normal} as the primary pagestyle for text.
@ -181,19 +198,27 @@
\newlength{\py@codewidth}
\renewcommand{\verbatim}{%
\begingroup%
\small%
\begin{list}{}{\setlength{\leftmargin}{1cm}}
\item%
\setlength{\parindent}{1cm}%
% Calculate the text width for the minipage:
\setlength{\py@codewidth}{\linewidth}%
\addtolength{\py@codewidth}{-\parindent}%
%
\par\indent%
\begin{minipage}[t]{\py@codewidth}%
\small%
\py@OldVerbatim%
}
\renewcommand{\endverbatim}{%
\py@OldEndVerbatim%
\end{list}%
\endgroup
\end{minipage}%
}
\renewcommand{\verbatiminput}[1]{%
{\small%
{\setlength{\parindent}{1cm}%
% Calculate the text width for the minipage:
\setlength{\py@codewidth}{\linewidth}%
\addtolength{\py@codewidth}{-\parindent}%
%
\small%
\begin{list}{}{\setlength{\leftmargin}{1cm}}
\item%
\py@OldVerbatimInput{#1}%
@ -573,13 +598,27 @@
% something else.
\newcommand{\py@unspecified}{...}
\newlength{\py@argswidth}
\newcommand{\py@sigparams}[1]{%
\parbox[t]{\py@argswidth}{\py@varvars{#1}\code{)}}}
\newcommand{\py@sigline}[2]{%
\settowidth{\py@argswidth}{#1\code{(}}%
\addtolength{\py@argswidth}{-2\py@argswidth}%
\addtolength{\py@argswidth}{\textwidth}%
\item[#1\code{(}\py@sigparams{#2}]}
% C functions ------------------------------------------------------------
% \begin{cfuncdesc}[refcount]{type}{name}{arglist}
% Note that the [refcount] slot should only be filled in by
% tools/anno-api.py; it pulls the value from the refcounts database.
\newcommand{\cfuncline}[3]{
\py@sigline{\code{#1 \bfcode{#2}}}{#3}%
\index{#2@{\py@idxcode{#2()}}}
}
\newenvironment{cfuncdesc}[4][\py@badkey]{
\begin{fulllineitems}
\item[\code{#2 \bfcode{#3}(\py@varvars{#4})}\index{#3@{\py@idxcode{#3()}}}]
\cfuncline{#2}{#3}{#4}
\ifx#1\@undefined\else%
\emph{Return value: \textbf{#1}.}\\
\fi
@ -604,8 +643,19 @@
\fi]
}{\end{fulllineitems}}
% C type fields ----------------------------------------------------------
% \begin{cmemberdesc}{container type}{ctype}{membername}
\newcommand{\cmemberline}[3]{
\item[\code{#2 \bfcode{#3}}]
\index{#3@{\py@idxcode{#3}} (#1 member)}
}
\newenvironment{cmemberdesc}[3]{
\begin{fulllineitems}
\cmemberline{#1}{#2}{#3}
}{\end{fulllineitems}}
% Funky macros -----------------------------------------------------------
% \begin{csimplemacro}{name}
% \begin{csimplemacrodesc}{name}
% -- "simple" because it has no args; NOT for constant definitions!
\newenvironment{csimplemacrodesc}[1]{
\begin{fulllineitems}
@ -623,7 +673,8 @@
}{\end{fulllineitems}}
% similar to {funcdesc}, but doesn't add to the index
\newcommand{\funclineni}[2]{\item[\code{\bfcode{#1}(\py@varvars{#2})}]}
\newcommand{\funclineni}[2]{%
\py@sigline{\bfcode{#1}}{#2}}
\newenvironment{funcdescni}[2]{
\begin{fulllineitems}
\funclineni{#1}{#2}
@ -635,8 +686,8 @@
% Using \renewcommand doesn't work for this, for unknown reasons:
\global\def\py@thisclass{#1}
\begin{fulllineitems}
\item[\strong{class }\code{\bfcode{#1}(\py@varvars{#2})}%
\index{#1@{\py@idxcode{#1}} (class in \py@thismodule)}]
\py@sigline{\strong{class }\bfcode{#1}}{#2}%
\index{#1@{\py@idxcode{#1}} (class in \py@thismodule)}
}{\end{fulllineitems}}
% \begin{classdesc*}{name}
@ -654,8 +705,8 @@
% Using \renewcommand doesn't work for this, for unknown reasons:
\global\def\py@thisclass{#1}
\begin{fulllineitems}
\item[\strong{exception }\code{\bfcode{#1}(\py@varvars{#2})}%
\index{#1@{\py@idxcode{#1}} (exception in \py@thismodule)}]
\py@sigline{\strong{exception }\bfcode{#1}}{#2}%
\index{#1@{\py@idxcode{#1}} (exception in \py@thismodule)}
}{\end{fulllineitems}}
% There is no corresponding {excclassdesc*} environment. To describe
@ -687,7 +738,7 @@
% similar to {methoddesc}, but doesn't add to the index
% (never actually uses the optional argument)
\newcommand{\methodlineni}[3][\py@classbadkey]{%
\item[\code{\bfcode{#2}(\py@varvars{#3})}]}
\py@sigline{\bfcode{#2}}{#3}}
\newenvironment{methoddescni}[3][\py@classbadkey]{
\begin{fulllineitems}
\methodlineni{#2}{#3}
@ -770,6 +821,7 @@
\newcommand{\NULL}{\constant{NULL}}
\newcommand{\infinity}{\ensuremath{\infty}}
\newcommand{\plusminus}{\ensuremath{\pm}}
% \menuselection{Start \sub Programs \sub Python}
\newcommand{\menuselection}[1]{{\def\sub{ \ensuremath{>} }#1}}
% Also for consistency: spell Python "Python", not "python"!
@ -779,24 +831,22 @@
\texttt{#1}}}
\newcommand{\bfcode}[1]{\code{\bfseries#1}} % bold-faced code font
\newcommand{\csimplemacro}[1]{\code{#1}}
\newcommand{\kbd}[1]{\code{#1}}
\newcommand{\samp}[1]{`\code{#1}'}
% This weird definition of \var{} allows it to always appear in roman
% italics, and won't get funky in code fragments when we play around
% with fonts. This also works directly in math mode.
\newcommand{\var}[1]{%
\ifmmode%
\hbox{\normalsize\textrm{\textit{#1\/}}}%
\hbox{\py@defaultsize\textrm{\textit{#1\/}}}%
\else%
\normalsize\textrm{\textit{#1\/}}%
\py@defaultsize\textrm{\textit{#1\/}}%
\fi%
}
\renewcommand{\emph}[1]{{\em #1}}
\newcommand{\dfn}[1]{\emph{#1}}
\newcommand{\strong}[1]{{\bf #1}}
% let's experiment with a new font:
\newcommand{\file}[1]{`{\textsf{#1}}'}
\newcommand{\filenq}[1]{{\textsf{#1}}}
\newcommand{\file}[1]{`\filenq{#1}'}
\newcommand{\filenq}[1]{{\py@smallsize\textsf{#1}}}
% Use this def/redef approach for \url{} since hyperref defined this already,
% but only if we actually used hyperref:
@ -805,15 +855,15 @@
\noindent%
\pdfstartlink attr{/Border [0 0 0]} user{/S /URI /URI (#1)}%
\py@LinkColor% color of the link text
\small\sf #1%
\py@smallsize\sf #1%
\py@NormalColor% Turn it back off; these are declarative
\pdfendlink}% and don't appear bound to the current
}% formatting "box".
\else
\newcommand{\url}[1]{\mbox{\small\textsf{#1}}}
\newcommand{\url}[1]{\mbox{\py@smallsize\textsf{#1}}}
\fi
\newcommand{\email}[1]{{\small\textsf{#1}}}
\newcommand{\newsgroup}[1]{{\small\textsf{#1}}}
\newcommand{\email}[1]{{\py@smallsize\textsf{#1}}}
\newcommand{\newsgroup}[1]{{\py@smallsize\textsf{#1}}}
\newcommand{\py@varvars}[1]{{%
{\let\unspecified=\py@unspecified%
@ -849,12 +899,12 @@
\newcommand{\ctype}[1]{\texttt{#1}} % C struct or typedef name
\newcommand{\cdata}[1]{\texttt{#1}} % C variable, typically global
\newcommand{\mailheader}[1]{{\small\textsf{#1:}}}
\newcommand{\mimetype}[1]{{\small\textsf{#1}}}
\newcommand{\mailheader}[1]{{\py@smallsize\textsf{#1:}}}
\newcommand{\mimetype}[1]{{\py@smallsize\textsf{#1}}}
% The \! is a "negative thin space" in math mode.
\newcommand{\regexp}[1]{%
{\tiny$^{^\lceil}\!\!$%
{\normalsize\code{#1}}%
{\py@defaultsize\code{#1}}%
$\!\rfloor\!$%
}}
\newcommand{\envvar}[1]{%
@ -1053,6 +1103,27 @@
\end{center}%
}
% XXX Don't think we can use this yet, though it cleans up some
% tedious markup. There's no equivalent for the HTML transform yet,
% and that needs to exist. I don't know how to write it.
%
% This should really have something that makes it easier to bind a
% table's ``Notes'' column and an associated tablenotes environment,
% and generates the right magic for getting the numbers right in the
% table.
%
% So this is quite incomplete.
%
\newcounter{py@tablenotescounter}
\newenvironment{tablenotes}{%
\noindent Notes:
\par
\setcounter{py@tablenotescounter}{0}
\begin{list}{(\arabic{py@tablenotescounter})}%
{\usecounter{py@tablenotescounter}}
}{\end{list}}
% Cross-referencing (AMK, new impl. FLD)
% Sample usage:
% \begin{seealso}

171
doc/texinputs/tmp Normal file
View File

@ -0,0 +1,171 @@
137,153d136
< % Set up abstract ways to get the normal and smaller font sizes that
< % work even in footnote context.
< \newif\ifpy@infootnote \py@infootnotefalse
< \let\py@oldmakefntext\@makefntext
< \def\@makefntext#1{%
< \bgroup%
< \py@infootnotetrue
< \py@oldmakefntext{#1}%
< \egroup%
< }
< \def\py@defaultsize{%
< \ifpy@infootnote\footnotesize\else\normalsize\fi%
< }
< \def\py@smallsize{%
< \ifpy@infootnote\scriptsize\else\small\fi%
< }
<
201,208c184,187
< \setlength{\parindent}{1cm}%
< % Calculate the text width for the minipage:
< \setlength{\py@codewidth}{\linewidth}%
< \addtolength{\py@codewidth}{-\parindent}%
< %
< \par\indent%
< \begin{minipage}[t]{\py@codewidth}%
< \small%
---
> \begingroup%
> \small%
> \begin{list}{}{\setlength{\leftmargin}{1cm}}
> \item%
213c192,193
< \end{minipage}%
---
> \end{list}%
> \endgroup
216,221c196
< {\setlength{\parindent}{1cm}%
< % Calculate the text width for the minipage:
< \setlength{\py@codewidth}{\linewidth}%
< \addtolength{\py@codewidth}{-\parindent}%
< %
< \small%
---
> {\small%
601,610d575
<
< \newlength{\py@argswidth}
< \newcommand{\py@sigparams}[1]{%
< \parbox[t]{\py@argswidth}{\py@varvars{#1}\code{)}}}
< \newcommand{\py@sigline}[2]{%
< \settowidth{\py@argswidth}{#1\code{(}}%
< \addtolength{\py@argswidth}{-2\py@argswidth}%
< \addtolength{\py@argswidth}{\textwidth}%
< \item[#1\code{(}\py@sigparams{#2}]}
<
615,618d579
< \newcommand{\cfuncline}[3]{
< \py@sigline{\code{#1 \bfcode{#2}}}{#3}%
< \index{#2@{\py@idxcode{#2()}}}
< }
621c582
< \cfuncline{#2}{#3}{#4}
---
> \item[\code{#2 \bfcode{#3}(\py@varvars{#4})}\index{#3@{\py@idxcode{#3()}}}]
646,656d606
< % C type fields ----------------------------------------------------------
< % \begin{cmemberdesc}{container type}{ctype}{membername}
< \newcommand{\cmemberline}[3]{
< \item[\code{#2 \bfcode{#3}}]
< \index{#3@{\py@idxcode{#3}} (#1 member)}
< }
< \newenvironment{cmemberdesc}[3]{
< \begin{fulllineitems}
< \cmemberline{#1}{#2}{#3}
< }{\end{fulllineitems}}
<
658c608
< % \begin{csimplemacrodesc}{name}
---
> % \begin{csimplemacro}{name}
676,677c626
< \newcommand{\funclineni}[2]{%
< \py@sigline{\bfcode{#1}}{#2}}
---
> \newcommand{\funclineni}[2]{\item[\code{\bfcode{#1}(\py@varvars{#2})}]}
689,690c638,639
< \py@sigline{\strong{class }\bfcode{#1}}{#2}%
< \index{#1@{\py@idxcode{#1}} (class in \py@thismodule)}
---
> \item[\strong{class }\code{\bfcode{#1}(\py@varvars{#2})}%
> \index{#1@{\py@idxcode{#1}} (class in \py@thismodule)}]
708,709c657,658
< \py@sigline{\strong{exception }\bfcode{#1}}{#2}%
< \index{#1@{\py@idxcode{#1}} (exception in \py@thismodule)}
---
> \item[\strong{exception }\code{\bfcode{#1}(\py@varvars{#2})}%
> \index{#1@{\py@idxcode{#1}} (exception in \py@thismodule)}]
741c690
< \py@sigline{\bfcode{#2}}{#3}}
---
> \item[\code{\bfcode{#2}(\py@varvars{#3})}]}
824d772
< % \menuselection{Start \sub Programs \sub Python}
834d781
< \newcommand{\csimplemacro}[1]{\code{#1}}
836a784,786
> % This weird definition of \var{} allows it to always appear in roman
> % italics, and won't get funky in code fragments when we play around
> % with fonts. This also works directly in math mode.
839c789
< \hbox{\py@defaultsize\textrm{\textit{#1\/}}}%
---
> \hbox{\normalsize\textrm{\textit{#1\/}}}%
841c791
< \py@defaultsize\textrm{\textit{#1\/}}%
---
> \normalsize\textrm{\textit{#1\/}}%
848,849c798,799
< \newcommand{\file}[1]{`\filenq{#1}'}
< \newcommand{\filenq}[1]{{\py@smallsize\textsf{#1}}}
---
> \newcommand{\file}[1]{`{\textsf{#1}}'}
> \newcommand{\filenq}[1]{{\textsf{#1}}}
858c808
< \py@smallsize\sf #1%
---
> \small\sf #1%
863c813
< \newcommand{\url}[1]{\mbox{\py@smallsize\textsf{#1}}}
---
> \newcommand{\url}[1]{\mbox{\small\textsf{#1}}}
865,866c815,816
< \newcommand{\email}[1]{{\py@smallsize\textsf{#1}}}
< \newcommand{\newsgroup}[1]{{\py@smallsize\textsf{#1}}}
---
> \newcommand{\email}[1]{{\small\textsf{#1}}}
> \newcommand{\newsgroup}[1]{{\small\textsf{#1}}}
902,903c852,853
< \newcommand{\mailheader}[1]{{\py@smallsize\textsf{#1:}}}
< \newcommand{\mimetype}[1]{{\py@smallsize\textsf{#1}}}
---
> \newcommand{\mailheader}[1]{{\small\textsf{#1:}}}
> \newcommand{\mimetype}[1]{{\small\textsf{#1}}}
907c857
< {\py@defaultsize\code{#1}}%
---
> {\normalsize\code{#1}}%
1105,1125d1054
<
< % XXX Don't think we can use this yet, though it cleans up some
< % tedious markup. There's no equivalent for the HTML transform yet,
< % and that needs to exist. I don't know how to write it.
< %
< % This should really have something that makes it easier to bind a
< % table's ``Notes'' column and an associated tablenotes environment,
< % and generates the right magic for getting the numbers right in the
< % table.
< %
< % So this is quite incomplete.
< %
< \newcounter{py@tablenotescounter}
< \newenvironment{tablenotes}{%
< \noindent Notes:
< \par
< \setcounter{py@tablenotescounter}{0}
< \begin{list}{(\arabic{py@tablenotescounter})}%
< {\usecounter{py@tablenotescounter}}
< }{\end{list}}
<

View File

@ -0,0 +1,76 @@
typedef struct _typeobject {
PyObject_VAR_HEAD
char *tp_name; /* For printing, in format "<module>.<name>" */
int tp_basicsize, tp_itemsize; /* For allocation */
/* Methods to implement standard operations */
destructor tp_dealloc;
printfunc tp_print;
getattrfunc tp_getattr;
setattrfunc tp_setattr;
cmpfunc tp_compare;
reprfunc tp_repr;
/* Method suites for standard classes */
PyNumberMethods *tp_as_number;
PySequenceMethods *tp_as_sequence;
PyMappingMethods *tp_as_mapping;
/* More standard operations (here for binary compatibility) */
hashfunc tp_hash;
ternaryfunc tp_call;
reprfunc tp_str;
getattrofunc tp_getattro;
setattrofunc tp_setattro;
/* Functions to access object as input/output buffer */
PyBufferProcs *tp_as_buffer;
/* Flags to define presence of optional/expanded features */
long tp_flags;
char *tp_doc; /* Documentation string */
/* Assigned meaning in release 2.0 */
/* call function for all accessible objects */
traverseproc tp_traverse;
/* delete references to contained objects */
inquiry tp_clear;
/* Assigned meaning in release 2.1 */
/* rich comparisons */
richcmpfunc tp_richcompare;
/* weak reference enabler */
long tp_weaklistoffset;
/* Added in release 2.2 */
/* Iterators */
getiterfunc tp_iter;
iternextfunc tp_iternext;
/* Attribute descriptor and subclassing stuff */
struct PyMethodDef *tp_methods;
struct PyMemberDef *tp_members;
struct PyGetSetDef *tp_getset;
struct _typeobject *tp_base;
PyObject *tp_dict;
descrgetfunc tp_descr_get;
descrsetfunc tp_descr_set;
long tp_dictoffset;
initproc tp_init;
allocfunc tp_alloc;
newfunc tp_new;
freefunc tp_free; /* Low-level free-memory routine */
inquiry tp_is_gc; /* For PyObject_IS_GC */
PyObject *tp_bases;
PyObject *tp_mro; /* method resolution order */
PyObject *tp_cache;
PyObject *tp_subclasses;
PyObject *tp_weaklist;
} PyTypeObject;

71
doc/tools/anno-api.py Executable file
View File

@ -0,0 +1,71 @@
#! /usr/bin/env python
"""Add reference count annotations to the Python/C API Reference."""
__version__ = '$Revision$'
import getopt
import os
import sys
import refcounts
PREFIX_1 = r"\begin{cfuncdesc}{PyObject*}{"
PREFIX_2 = r"\begin{cfuncdesc}{PyVarObject*}{"
def main():
rcfile = os.path.join(os.path.dirname(refcounts.__file__), os.pardir,
"api", "refcounts.dat")
outfile = "-"
opts, args = getopt.getopt(sys.argv[1:], "o:r:", ["output=", "refcounts="])
for opt, arg in opts:
if opt in ("-o", "--output"):
outfile = arg
elif opt in ("-r", "--refcounts"):
rcfile = arg
rcdict = refcounts.load(rcfile)
if outfile == "-":
output = sys.stdout
else:
output = open(outfile, "w")
if not args:
args = ["-"]
for infile in args:
if infile == "-":
input = sys.stdin
else:
input = open(infile)
while 1:
line = input.readline()
if not line:
break
prefix = None
if line.startswith(PREFIX_1):
prefix = PREFIX_1
elif line.startswith(PREFIX_2):
prefix = PREFIX_2
if prefix:
s = line[len(prefix):].split('}', 1)[0]
try:
info = rcdict[s]
except KeyError:
sys.stderr.write("No refcount data for %s\n" % s)
else:
if info.result_type in ("PyObject*", "PyVarObject*"):
if info.result_refs is None:
rc = "Always \NULL{}"
else:
rc = info.result_refs and "New" or "Borrowed"
rc = rc + " reference"
line = (r"\begin{cfuncdesc}[%s]{%s}{"
% (rc, info.result_type)) \
+ line[len(prefix):]
output.write(line)
if infile != "-":
input.close()
if outfile != "-":
output.close()
if __name__ == "__main__":
main()

377
doc/tools/buildindex.py Executable file
View File

@ -0,0 +1,377 @@
#! /usr/bin/env python
__version__ = '$Revision$'
import os.path
import re
import string
import sys
bang_join = "!".join
null_join = "".join
class Node:
__rmjunk = re.compile("<#\d+#>")
continuation = 0
def __init__(self, link, str, seqno):
self.links = [link]
self.seqno = seqno
# remove <#\d+#> left in by moving the data out of LaTeX2HTML
str = self.__rmjunk.sub('', str)
# build up the text
self.text = split_entry_text(str)
self.key = split_entry_key(str)
def __cmp__(self, other):
"""Comparison operator includes sequence number, for use with
list.sort()."""
return self.cmp_entry(other) or cmp(self.seqno, other.seqno)
def cmp_entry(self, other):
"""Comparison 'operator' that ignores sequence number."""
c = 0
for i in range(min(len(self.key), len(other.key))):
c = (cmp_part(self.key[i], other.key[i])
or cmp_part(self.text[i], other.text[i]))
if c:
break
return c or cmp(self.key, other.key) or cmp(self.text, other.text)
def __repr__(self):
return "<Node for %s (%s)>" % (bang_join(self.text), self.seqno)
def __str__(self):
return bang_join(self.key)
def dump(self):
return "%s\1%s###%s\n" \
% ("\1".join(self.links),
bang_join(self.text),
self.seqno)
def cmp_part(s1, s2):
result = cmp(s1, s2)
if result == 0:
return 0
l1 = s1.lower()
l2 = s2.lower()
minlen = min(len(s1), len(s2))
if len(s1) < len(s2) and l1 == l2[:len(s1)]:
result = -1
elif len(s2) < len(s1) and l2 == l1[:len(s2)]:
result = 1
else:
result = cmp(l1, l2) or cmp(s1, s2)
return result
def split_entry(str, which):
stuff = []
parts = str.split('!')
parts = [part.split('@') for part in parts]
for entry in parts:
if len(entry) != 1:
key = entry[which]
else:
key = entry[0]
stuff.append(key)
return stuff
_rmtt = re.compile(r"""(.*)<tt(?: class=['"][a-z0-9]+["'])?>(.*)</tt>(.*)$""",
re.IGNORECASE)
_rmparens = re.compile(r"\(\)")
def split_entry_key(str):
parts = split_entry(str, 1)
for i in range(len(parts)):
m = _rmtt.match(parts[i])
if m:
parts[i] = null_join(m.group(1, 2, 3))
else:
parts[i] = parts[i].lower()
# remove '()' from the key:
parts[i] = _rmparens.sub('', parts[i])
return map(trim_ignored_letters, parts)
def split_entry_text(str):
if '<' in str:
m = _rmtt.match(str)
if m:
str = null_join(m.group(1, 2, 3))
return split_entry(str, 1)
def load(fp):
nodes = []
rx = re.compile("(.*)\1(.*)###(.*)$")
while 1:
line = fp.readline()
if not line:
break
m = rx.match(line)
if m:
link, str, seqno = m.group(1, 2, 3)
nodes.append(Node(link, str, seqno))
return nodes
def trim_ignored_letters(s):
# ignore $ to keep environment variables with the
# leading letter from the name
if s.startswith("$"):
return s[1:].lower()
else:
return s.lower()
def get_first_letter(s):
if s.startswith("<tex2html_percent_mark>"):
return "%"
else:
return trim_ignored_letters(s)[0]
def split_letters(nodes):
letter_groups = []
if nodes:
group = []
append = group.append
letter = get_first_letter(nodes[0].text[0])
letter_groups.append((letter, group))
for node in nodes:
nletter = get_first_letter(node.text[0])
if letter != nletter:
letter = nletter
group = []
letter_groups.append((letter, group))
append = group.append
append(node)
return letter_groups
def group_symbols(groups):
entries = []
ident_letters = string.ascii_letters + "_"
while groups[0][0] not in ident_letters:
entries += groups[0][1]
del groups[0]
if entries:
groups.insert(0, ("Symbols", entries))
# need a function to separate the nodes into columns...
def split_columns(nodes, columns=1):
if columns <= 1:
return [nodes]
# This is a rough height; we may have to increase to avoid breaks before
# a subitem.
colheight = int(len(nodes) / columns)
numlong = int(len(nodes) % columns)
if numlong:
colheight = colheight + 1
else:
numlong = columns
cols = []
for i in range(numlong):
start = i * colheight
end = start + colheight
cols.append(nodes[start:end])
del nodes[:end]
colheight = colheight - 1
try:
numshort = int(len(nodes) / colheight)
except ZeroDivisionError:
cols = cols + (columns - len(cols)) * [[]]
else:
for i in range(numshort):
start = i * colheight
end = start + colheight
cols.append(nodes[start:end])
#
# If items continue across columns, make sure they are marked
# as continuations so the user knows to look at the previous column.
#
for i in range(len(cols) - 1):
try:
prev = cols[i][-1]
next = cols[i + 1][0]
except IndexError:
return cols
else:
n = min(len(prev.key), len(next.key))
for j in range(n):
if prev.key[j] != next.key[j]:
break
next.continuation = j + 1
return cols
DL_LEVEL_INDENT = " "
def format_column(nodes):
strings = ["<dl compact>"]
append = strings.append
level = 0
previous = []
for node in nodes:
current = node.text
count = 0
for i in range(min(len(current), len(previous))):
if previous[i] != current[i]:
break
count = i + 1
if count > level:
append("<dl compact>" * (count - level) + "\n")
level = count
elif level > count:
append("\n")
append(level * DL_LEVEL_INDENT)
append("</dl>" * (level - count))
level = count
# else: level == count
for i in range(count, len(current) - 1):
term = node.text[i]
level = level + 1
if node.continuation > i:
extra = " (continued)"
else:
extra = ""
append("\n<dt>%s%s\n<dd>\n%s<dl compact>"
% (term, extra, level * DL_LEVEL_INDENT))
append("\n%s<dt>%s%s</a>"
% (level * DL_LEVEL_INDENT, node.links[0], node.text[-1]))
for link in node.links[1:]:
append(",\n%s %s[Link]</a>" % (level * DL_LEVEL_INDENT, link))
previous = current
append("\n")
append("</dl>" * (level + 1))
return null_join(strings)
def format_nodes(nodes, columns=1):
strings = []
append = strings.append
if columns > 1:
colnos = range(columns)
colheight = int(len(nodes) / columns)
if len(nodes) % columns:
colheight = colheight + 1
colwidth = int(100 / columns)
append('<table width="100%"><tr valign="top">')
for col in split_columns(nodes, columns):
append('<td width="%d%%">\n' % colwidth)
append(format_column(col))
append("\n</td>")
append("\n</tr></table>")
else:
append(format_column(nodes))
append("\n<p>\n")
return null_join(strings)
def format_letter(letter):
if letter == '.':
lettername = ". (dot)"
elif letter == '_':
lettername = "_ (underscore)"
else:
lettername = letter.capitalize()
return "\n<hr>\n<h2><a name=\"letter-%s\">%s</a></h2>\n\n" \
% (letter, lettername)
def format_html_letters(nodes, columns, group_symbol_nodes):
letter_groups = split_letters(nodes)
if group_symbol_nodes:
group_symbols(letter_groups)
items = []
for letter, nodes in letter_groups:
s = "<b><a href=\"#letter-%s\">%s</a></b>" % (letter, letter)
items.append(s)
s = ["<hr><center>\n%s</center>\n" % " |\n".join(items)]
for letter, nodes in letter_groups:
s.append(format_letter(letter))
s.append(format_nodes(nodes, columns))
return null_join(s)
def format_html(nodes, columns):
return format_nodes(nodes, columns)
def collapse(nodes):
"""Collapse sequences of nodes with matching keys into a single node.
Destructive."""
if len(nodes) < 2:
return
prev = nodes[0]
i = 1
while i < len(nodes):
node = nodes[i]
if not node.cmp_entry(prev):
prev.links.append(node.links[0])
del nodes[i]
else:
i = i + 1
prev = node
def dump(nodes, fp):
for node in nodes:
fp.write(node.dump())
def process_nodes(nodes, columns, letters=0, group_symbol_nodes=0):
nodes.sort()
collapse(nodes)
if letters:
return format_html_letters(nodes, columns, group_symbol_nodes)
else:
return format_html(nodes, columns)
def main():
import getopt
ifn = "-"
ofn = "-"
columns = 1
letters = 0
group_symbol_nodes = 1
opts, args = getopt.getopt(sys.argv[1:], "c:lo:",
["columns=", "dont-group-symbols",
"group-symbols", "letters", "output="])
for opt, val in opts:
if opt in ("-o", "--output"):
ofn = val
elif opt in ("-c", "--columns"):
columns = int(val, 10)
elif opt in ("-l", "--letters"):
letters = 1
elif opt == "--group-symbols":
group_symbol_nodes = 1
elif opt == "--dont-group-symbols":
group_symbol_nodes = 0
if not args:
args = [ifn]
nodes = []
for fn in args:
nodes = nodes + load(open(fn))
num_nodes = len(nodes)
html = process_nodes(nodes, columns, letters, group_symbol_nodes)
program = os.path.basename(sys.argv[0])
if ofn == "-":
sys.stdout.write(html)
sys.stderr.write("\n%s: %d index nodes" % (program, num_nodes))
else:
open(ofn, "w").write(html)
print
print "%s: %d index nodes" % (program, num_nodes)
if __name__ == "__main__":
main()

112
doc/tools/checkargs.pm Normal file
View File

@ -0,0 +1,112 @@
#!/uns/bin/perl
package checkargs;
require 5.004; # uses "for my $var"
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(check_args check_args_range check_args_at_least);
use strict;
use Carp;
=head1 NAME
checkargs -- Provide rudimentary argument checking for perl5 functions
=head1 SYNOPSIS
check_args(cArgsExpected, @_)
check_args_range(cArgsMin, cArgsMax, @_)
check_args_at_least(cArgsMin, @_)
where "@_" should be supplied literally.
=head1 DESCRIPTION
As the first line of user-written subroutine foo, do one of the following:
my ($arg1, $arg2) = check_args(2, @_);
my ($arg1, @rest) = check_args_range(1, 4, @_);
my ($arg1, @rest) = check_args_at_least(1, @_);
my @args = check_args_at_least(0, @_);
These functions may also be called for side effect (put a call to one
of the functions near the beginning of the subroutine), but using the
argument checkers to set the argument list is the recommended usage.
The number of arguments and their definedness are checked; if the wrong
number are received, the program exits with an error message.
=head1 AUTHOR
Michael D. Ernst <F<mernst@cs.washington.edu>>
=cut
## Need to check that use of caller(1) really gives desired results.
## Need to give input chunk information.
## Is this obviated by Perl 5.003's declarations? Not entirely, I think.
sub check_args ( $@ )
{
my ($num_formals, @args) = @_;
my ($pack, $file_arg, $line_arg, $subname, $hasargs, $wantarr) = caller(1);
if (@_ < 1) { croak "check_args needs at least 7 args, got ", scalar(@_), ": @_\n "; }
if ((!wantarray) && ($num_formals != 0))
{ croak "check_args called in scalar context"; }
# Can't use croak below here: it would only go out to caller, not its caller
my $num_actuals = @args;
if ($num_actuals != $num_formals)
{ die "$file_arg:$line_arg: function $subname expected $num_formals argument",
(($num_formals == 1) ? "" : "s"),
", got $num_actuals",
(($num_actuals == 0) ? "" : ": @args"),
"\n"; }
for my $index (0..$#args)
{ if (!defined($args[$index]))
{ die "$file_arg:$line_arg: function $subname undefined argument ", $index+1, ": @args[0..$index-1]\n"; } }
return @args;
}
sub check_args_range ( $$@ )
{
my ($min_formals, $max_formals, @args) = @_;
my ($pack, $file_arg, $line_arg, $subname, $hasargs, $wantarr) = caller(1);
if (@_ < 2) { croak "check_args_range needs at least 8 args, got ", scalar(@_), ": @_"; }
if ((!wantarray) && ($max_formals != 0) && ($min_formals !=0) )
{ croak "check_args_range called in scalar context"; }
# Can't use croak below here: it would only go out to caller, not its caller
my $num_actuals = @args;
if (($num_actuals < $min_formals) || ($num_actuals > $max_formals))
{ die "$file_arg:$line_arg: function $subname expected $min_formals-$max_formals arguments, got $num_actuals",
($num_actuals == 0) ? "" : ": @args", "\n"; }
for my $index (0..$#args)
{ if (!defined($args[$index]))
{ die "$file_arg:$line_arg: function $subname undefined argument ", $index+1, ": @args[0..$index-1]\n"; } }
return @args;
}
sub check_args_at_least ( $@ )
{
my ($min_formals, @args) = @_;
my ($pack, $file_arg, $line_arg, $subname, $hasargs, $wantarr) = caller(1);
# Don't do this, because we want every sub to start with a call to check_args*
# if ($min_formals == 0)
# { die "Isn't it pointless to check for at least zero args to $subname?\n"; }
if (scalar(@_) < 1)
{ croak "check_args_at_least needs at least 1 arg, got ", scalar(@_), ": @_"; }
if ((!wantarray) && ($min_formals != 0))
{ croak "check_args_at_least called in scalar context"; }
# Can't use croak below here: it would only go out to caller, not its caller
my $num_actuals = @args;
if ($num_actuals < $min_formals)
{ die "$file_arg:$line_arg: function $subname expected at least $min_formals argument",
($min_formals == 1) ? "" : "s",
", got $num_actuals",
($num_actuals == 0) ? "" : ": @args", "\n"; }
for my $index (0..$#args)
{ if (!defined($args[$index]))
{ warn "$file_arg:$line_arg: function $subname undefined argument ", $index+1, ": @args[0..$index-1]\n"; last; } }
return @args;
}
1; # successful import
__END__

26
doc/tools/cklatex Executable file
View File

@ -0,0 +1,26 @@
#! /bin/sh
# -*- ksh -*-
# This script *helps* locate lines of normal content that end in '}';
# this is useful since LaTeX2HTML (at least the old version that we
# use) breaks on many lines that end that way.
#
# Usage: cklatex files... | less
#
# *Read* the output looking for suspicious lines!
grep -n "[^ ]}\$" $@ | \
grep -v '\\begin{' | \
grep -v '\\end{' | \
grep -v '\\input{' | \
grep -v '\\documentclass{' | \
grep -v '\\title{' | \
grep -v '\\chapter{' | \
grep -v '\\chapter\*{' | \
grep -v '\\section{' | \
grep -v '\\subsection{' | \
grep -v '\\subsubsection{' | \
grep -v '\\sectionauthor{' | \
grep -v '\\moduleauthor{'
exit $?

78
doc/tools/custlib.py Normal file
View File

@ -0,0 +1,78 @@
# Generate custlib.tex, which is a site-specific library document.
# Phase I: list all the things that can be imported
import glob
import os.path
import sys
modules = {}
for modname in sys.builtin_module_names:
modules[modname] = modname
for dir in sys.path:
# Look for *.py files
filelist = glob.glob(os.path.join(dir, '*.py'))
for file in filelist:
path, file = os.path.split(file)
base, ext = os.path.splitext(file)
modules[base.lower()] = base
# Look for shared library files
filelist = (glob.glob(os.path.join(dir, '*.so')) +
glob.glob(os.path.join(dir, '*.sl')) +
glob.glob(os.path.join(dir, '*.o')) )
for file in filelist:
path, file = os.path.split(file)
base, ext = os.path.splitext(file)
if base[-6:] == 'module':
base = base[:-6]
modules[base.lower()] = base
# Minor oddity: the types module is documented in libtypes2.tex
if modules.has_key('types'):
del modules['types']
modules['types2'] = None
# Phase II: find all documentation files (lib*.tex)
# and eliminate modules that don't have one.
docs = {}
filelist = glob.glob('lib*.tex')
for file in filelist:
modname = file[3:-4]
docs[modname] = modname
mlist = modules.keys()
mlist = filter(lambda x, docs=docs: docs.has_key(x), mlist)
mlist.sort()
mlist = map(lambda x, docs=docs: docs[x], mlist)
modules = mlist
# Phase III: write custlib.tex
# Write the boilerplate
# XXX should be fancied up.
print """\documentstyle[twoside,11pt,myformat]{report}
\\title{Python Library Reference}
\\input{boilerplate}
\\makeindex % tell \\index to actually write the .idx file
\\begin{document}
\\pagenumbering{roman}
\\maketitle
\\input{copyright}
\\begin{abstract}
\\noindent This is a customized version of the Python Library Reference.
\\end{abstract}
\\pagebreak
{\\parskip = 0mm \\tableofcontents}
\\pagebreak\\pagenumbering{arabic}"""
for modname in mlist:
print "\\input{lib%s}" % (modname,)
# Write the end
print """\\input{custlib.ind} % Index
\\end{document}"""

81
doc/tools/cvsinfo.py Normal file
View File

@ -0,0 +1,81 @@
"""Utility class and function to get information about the CVS repository
based on checked-out files.
"""
import os
def get_repository_list(paths):
d = {}
for name in paths:
if os.path.isfile(name):
dir = os.path.dirname(name)
else:
dir = name
rootfile = os.path.join(name, "CVS", "Root")
root = open(rootfile).readline().strip()
if not d.has_key(root):
d[root] = RepositoryInfo(dir), [name]
else:
d[root][1].append(name)
return d.values()
class RepositoryInfo:
"""Record holding information about the repository we want to talk to."""
cvsroot_path = None
branch = None
# type is '', ':ext', or ':pserver:'
type = ""
def __init__(self, dir=None):
if dir is None:
dir = os.getcwd()
dir = os.path.join(dir, "CVS")
root = open(os.path.join(dir, "Root")).readline().strip()
if root.startswith(":pserver:"):
self.type = ":pserver:"
root = root[len(":pserver:"):]
elif ":" in root:
if root.startswith(":ext:"):
root = root[len(":ext:"):]
self.type = ":ext:"
self.repository = root
if ":" in root:
host, path = root.split(":", 1)
self.cvsroot_path = path
else:
self.cvsroot_path = root
fn = os.path.join(dir, "Tag")
if os.path.isfile(fn):
self.branch = open(fn).readline().strip()[1:]
def get_cvsroot(self):
return self.type + self.repository
_repository_dir_cache = {}
def get_repository_file(self, path):
filename = os.path.abspath(path)
if os.path.isdir(path):
dir = path
join = 0
else:
dir = os.path.dirname(path)
join = 1
try:
repodir = self._repository_dir_cache[dir]
except KeyError:
repofn = os.path.join(dir, "CVS", "Repository")
repodir = open(repofn).readline().strip()
repodir = os.path.join(self.cvsroot_path, repodir)
self._repository_dir_cache[dir] = repodir
if join:
fn = os.path.join(repodir, os.path.basename(path))
else:
fn = repodir
return fn[len(self.cvsroot_path)+1:]
def __repr__(self):
return "<RepositoryInfo for %s>" % `self.get_cvsroot()`

161
doc/tools/findacks Executable file
View File

@ -0,0 +1,161 @@
#!/usr/bin/env python
"""Script to locate email addresses in the CVS logs."""
__version__ = '$Revision$'
import os
import re
import sys
import UserDict
import cvsinfo
class Acknowledgements(UserDict.UserDict):
def add(self, email, name, path):
d = self.data
d.setdefault(email, {})[path] = name
def open_cvs_log(info, paths=None):
cvsroot = info.get_cvsroot()
cmd = "cvs -q -d%s log " % cvsroot
if paths:
cmd += " ".join(paths)
return os.popen(cmd, "r")
email_rx = re.compile("<([a-z][-a-z0-9._]*@[-a-z0-9.]+)>", re.IGNORECASE)
def find_acks(f, acks):
prev = ''
filename = None
MAGIC_WORDS = ('van', 'von')
while 1:
line = f.readline()
if not line:
break
if line.startswith("Working file: "):
filename = line.split(None, 2)[2].strip()
prev = line
continue
m = email_rx.search(line)
if m:
words = prev.split() + line[:m.start()].split()
L = []
while words \
and (words[-1][0].isupper() or words[-1] in MAGIC_WORDS):
L.insert(0, words.pop())
name = " ".join(L)
email = m.group(1).lower()
acks.add(email, name, filename)
prev = line
def load_cvs_log_acks(acks, args):
repolist = cvsinfo.get_repository_list(args or [""])
for info, paths in repolist:
print >>sys.stderr, "Repository:", info.get_cvsroot()
f = open_cvs_log(info, paths)
find_acks(f, acks)
f.close()
def load_tex_source_acks(acks, args):
for path in args:
path = path or os.curdir
if os.path.isfile(path):
read_acks_from_tex_file(acks, path)
else:
read_acks_from_tex_dir(acks, path)
def read_acks_from_tex_file(acks, path):
f = open(path)
while 1:
line = f.readline()
if not line:
break
if line.startswith(r"\sectionauthor{"):
line = line[len(r"\sectionauthor"):]
name, line = extract_tex_group(line)
email, line = extract_tex_group(line)
acks.add(email, name, path)
def read_acks_from_tex_dir(acks, path):
stack = [path]
while stack:
p = stack.pop()
for n in os.listdir(p):
n = os.path.join(p, n)
if os.path.isdir(n):
stack.insert(0, n)
elif os.path.normpath(n).endswith(".tex"):
read_acks_from_tex_file(acks, n)
def extract_tex_group(s):
c = 0
for i in range(len(s)):
if s[i] == '{':
c += 1
elif s[i] == '}':
c -= 1
if c == 0:
return s[1:i], s[i+1:]
def print_acks(acks):
first = 1
for email, D in acks.items():
if first:
first = 0
else:
print
L = D.items()
L.sort()
prefname = L[0][1]
for file, name in L[1:]:
if name != prefname:
prefname = ""
break
if prefname:
print prefname, "<%s>:" % email
else:
print email + ":"
for file, name in L:
if name == prefname:
print " " + file
else:
print " %s (as %s)" % (file, name)
def print_ack_names(acks):
names = []
for email, D in acks.items():
L = D.items()
L.sort()
prefname = L[0][1]
for file, name in L[1:]:
prefname = prefname or name
names.append(prefname or email)
def f(s1, s2):
s1 = s1.lower()
s2 = s2.lower()
return cmp((s1.split()[-1], s1),
(s2.split()[-1], s2))
names.sort(f)
for name in names:
print name
def main():
args = sys.argv[1:]
acks = Acknowledgements()
load_cvs_log_acks(acks, args)
load_tex_source_acks(acks, args)
print_ack_names(acks)
if __name__ == "__main__":
main()

136
doc/tools/findcsyms Executable file
View File

@ -0,0 +1,136 @@
#! /usr/bin/env python
import errno
import os
import re
import sys
if __name__ == "__main__":
_base = sys.argv[0]
else:
_base = __file__
_script_home = os.path.abspath(os.path.dirname(_base))
srcdir = os.path.dirname(os.path.dirname(_script_home))
EXCLUDES = ["bitset.h", "cStringIO.h", "graminit.h", "grammar.h",
"longintrepr.h", "metagrammar.h",
"node.h", "opcode.h", "osdefs.h", "pgenheaders.h",
"py_curses.h", "parsetok.h", "symtable.h", "token.h"]
def list_headers():
"""Return a list of headers."""
incdir = os.path.join(srcdir, "Include")
return [fn for fn in os.listdir(incdir)
if fn.endswith(".h") and fn not in EXCLUDES]
def matcher(pattern):
return re.compile(pattern).match
MATCHERS = [
matcher(r"\\begin\{cfuncdesc\}\{[^{]*\}\{(?P<sym>[^{]*)\}"),
matcher(r"\\cfuncline\{[^{]*\}\{(?P<sym>[^{]*)\}"),
matcher(r"\\begin\{ctypedesc\}(\[[^{]*\])?\{(?P<sym>[^{]*)\}"),
matcher(r"\\begin\{cvardesc\}\{[^{]*\}\{(?P<sym>[^{]*)\}"),
matcher(r"\\begin\{cmemberdesc\}\{[^{]*\}\{(?P<sym>[^{]*)\}"),
matcher(r"\\cmemberline\{[^{]*\}\{(?P<sym>[^{]*)\}"),
matcher(r"\\begin\{csimplemacrodesc\}\{(?P<sym>[^{]*)\}"),
]
def list_documented_items():
"""Return a list of everything that's already documented."""
apidir = os.path.join(srcdir, "Doc", "api")
files = [fn for fn in os.listdir(apidir) if fn.endswith(".tex")]
L = []
for fn in files:
fullname = os.path.join(apidir, fn)
for line in open(fullname):
line = line.lstrip()
if not line.startswith("\\"):
continue
for matcher in MATCHERS:
m = matcher(line)
if m:
L.append(m.group("sym"))
break
return L
def split_documented(all, documented):
"""Split the list of all symbols into documented and undocumented
categories."""
doc = []
undoc = []
for t in all:
if t[0] in documented:
doc.append(t)
else:
undoc.append(t)
return doc, undoc
def print_list(L, title=None):
"""Dump a list to stdout."""
if title:
print title + ":"
print "-" * (len(title) + 1)
w = 0
for sym, filename in L:
w = max(w, len(sym))
if w % 4 == 0:
w += 4
else:
w += (4 - (w % 4))
for sym, filename in L:
print "%-*s%s" % (w, sym, filename)
_spcjoin = ' '.join
def main():
args = sys.argv[1:]
if args:
headers = args
documented = []
else:
os.chdir(os.path.join(srcdir, "Include"))
headers = list_headers()
documented = list_documented_items()
cmd = ("ctags -f - --file-scope=no --c-types=dgpstux "
"-Istaticforward -Istatichere=static "
+ _spcjoin(headers))
fp = os.popen(cmd)
L = []
prevsym = None
while 1:
line = fp.readline()
if not line:
break
sym, filename = line.split()[:2]
if sym == prevsym:
continue
if not sym.endswith("_H"):
L.append((sym, filename))
prevsym = sym
L.sort()
fp.close()
try:
if documented:
documented, undocumented = split_documented(L, documented)
print_list(documented, "Documented symbols")
if undocumented:
print
print_list(undocumented, "Undocumented symbols")
else:
print_list(L)
except IOError, e:
if e.errno != errno.EPIPE:
raise
if __name__ == "__main__":
main()

63
doc/tools/findmodrefs Executable file
View File

@ -0,0 +1,63 @@
#! /usr/bin/env python
# -*- Python -*-
import fileinput
import getopt
import glob
import os
import re
import sys
declare_rx = re.compile(
r"\\declaremodule(?:\[[a-zA-Z0-9]*\]*)?{[a-zA-Z_0-9]+}{([a-zA-Z_0-9]+)}")
module_rx = re.compile(r"\\module{([a-zA-Z_0-9]+)}")
def main():
try:
just_list = 0
print_lineno = 0
opts, args = getopt.getopt(sys.argv[1:], "ln", ["list", "number"])
for opt, arg in opts:
if opt in ("-l", "--list"):
just_list = 1
elif opt in ("-n", "--number"):
print_lineno = 1
files = args
if not files:
files = glob.glob("*.tex")
files.sort()
modulename = None
for line in fileinput.input(files):
if line[:9] == r"\section{":
modulename = None
continue
if line[:16] == r"\modulesynopsys{":
continue
m = declare_rx.match(line)
if m:
modulename = m.group(1)
continue
if not modulename:
continue
m = module_rx.search(line)
if m:
name = m.group(1)
if name != modulename:
filename = fileinput.filename()
if just_list:
print filename
fileinput.nextfile()
modulename = None
elif print_lineno:
print "%s(%d):%s" \
% (filename, fileinput.filelineno(), line[:-1])
else:
print "%s:%s" % (filename, line[:-1])
except KeyboardInterrupt:
sys.exit(1)
if __name__ == "__main__":
main()

128
doc/tools/findsyms Executable file
View File

@ -0,0 +1,128 @@
#!/usr/bin/env python
# Released to the public domain by Skip Montanaro, 28 March 2002
"""
findsyms.py - try to identify undocumented symbols exported by modules
Usage: findsyms.py librefdir
For each lib*.tex file in the libref manual source directory, identify which
module is documented, import the module if possible, then search the LaTeX
source for the symbols global to that module. Report any that don't seem to
be documented.
Certain exceptions are made to the list of undocumented symbols:
* don't mention symbols in which all letters are upper case on the
assumption they are manifest constants
* don't mention symbols that are themselves modules
* don't mention symbols that match those exported by os, math, string,
types, or __builtin__ modules
Finally, if a name is exported by the module but fails a getattr() lookup,
that anomaly is reported.
"""
import __builtin__
import getopt
import glob
import math
import os
import re
import string
import sys
import types
import warnings
def usage():
print >> sys.stderr, """
usage: %s dir
where 'dir' is the Library Reference Manual source directory.
""" % os.path.basename(sys.argv[0])
def main():
try:
opts, args = getopt.getopt(sys.argv[1:], "")
except getopt.error:
usage()
return
if not args:
usage()
return
libdir = args[0]
warnings.filterwarnings("error")
pat = re.compile(r"\\declaremodule\s*{[^}]*}\s*{([^}]*)}")
missing = []
filelist = glob.glob(os.path.join(libdir, "lib*.tex"))
filelist.sort()
for f in filelist:
mod = f[3:-4]
if not mod: continue
data = open(f).read()
mods = re.findall(pat, data)
if not mods:
print "No module declarations found in", f
continue
for modname in mods:
# skip special modules
if modname.startswith("__"):
continue
try:
mod = __import__(modname)
except ImportError:
missing.append(modname)
continue
except DeprecationWarning:
print "Deprecated module:", modname
continue
if hasattr(mod, "__all__"):
all = mod.__all__
else:
all = [k for k in dir(mod) if k[0] != "_"]
mentioned = 0
all.sort()
for name in all:
if data.find(name) == -1:
# certain names are predominantly used for testing
if name in ("main","test","_test"):
continue
# is it some sort of manifest constant?
if name.upper() == name:
continue
try:
item = getattr(mod, name)
except AttributeError:
print " ", name, "exposed, but not an attribute"
continue
# don't care about modules that might be exposed
if type(item) == types.ModuleType:
continue
# check a few modules which tend to be import *'d
isglobal = 0
for m in (os, math, string, __builtin__, types):
if hasattr(m, name) and item == getattr(m, name):
isglobal = 1
break
if isglobal: continue
if not mentioned:
print "Not mentioned in", modname, "docs:"
mentioned = 1
print " ", name
if missing:
missing.sort()
print "Could not import:"
print " ", ", ".join(missing)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
pass

2
doc/tools/fix_hack Executable file
View File

@ -0,0 +1,2 @@
#!/bin/sh
sed -e 's/{\\ptt[ ]*\\char[ ]*'"'"'137}/_/g' <"$1" > "@$1" && mv "@$1" $1

3
doc/tools/fix_libaux.sed Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sed -f
s/{\\tt \\hackscore {}\\hackscore {}/\\sectcode{__/
s/\\hackscore {}\\hackscore {}/__/

15
doc/tools/fixinfo.el Normal file
View File

@ -0,0 +1,15 @@
(defun fix-python-texinfo ()
(goto-char (point-min))
(replace-regexp "\\(@setfilename \\)\\([-a-z]*\\)$"
"\\1python-\\2.info")
(replace-string "@node Front Matter\n@chapter Abstract\n"
"@node Abstract\n@section Abstract\n")
(mark-whole-buffer)
(texinfo-master-menu 'update-all-nodes)
(save-buffer)
) ;; fix-python-texinfo
;; now really do it:
(find-file (car command-line-args-left))
(fix-python-texinfo)
(kill-emacs)

97
doc/tools/getpagecounts Executable file
View File

@ -0,0 +1,97 @@
#! /usr/bin/env python
"""Generate a page count report of the PostScript version of the manuals."""
__version__ = '$Revision$'
import getopt
import sys
class PageCounter:
def __init__(self):
self.doclist = []
self.total = 0
self.title_width = 0
self.version = ""
def add_document(self, prefix, title):
count = count_pages(prefix + ".ps")
self.doclist.append((title, prefix, count))
self.title_width = max(self.title_width, len(title))
self.total = self.total + count
def dump(self):
fmt = "%%-%ds (%%s.ps, %%d pages)" % self.title_width
for item in self.doclist:
print fmt % item
print
print " Total page count: %d" % self.total
def parse_options(self):
opts, args = getopt.getopt(sys.argv[1:], "r:", ["release="])
assert not args
for opt, arg in opts:
if opt in ("-r", "--release"):
self.version = arg
def run(self):
self.parse_options()
if self.version:
version = self.version[:3]
self.add_document("whatsnew" + version.replace(".", ""),
"What's New in Python " + version)
for prefix, title in [
("api", "Python/C API"),
("ext", "Extending and Embedding the Python Interpreter"),
("lib", "Python Library Reference"),
("mac", "Macintosh Module Reference"),
("ref", "Python Reference Manual"),
("tut", "Python Tutorial"),
("doc", "Documenting Python"),
("inst", "Installing Python Modules"),
("dist", "Distributing Python Modules"),
]:
self.add_document(prefix, title)
print self.PREFIX
self.dump()
print self.SUFFIX
PREFIX = """\
This is the PostScript version of the standard Python documentation.
If you plan to print this, be aware that some of the documents are
long. It is formatted for printing on two-sided paper; if you do plan
to print this, *please* print two-sided if you have a printer capable
of it! To locate published copies of the larger manuals, or other
Python reference material, consult the Python Bookstore at:
http://www.amk.ca/bookstore/
The following manuals are included in this package:
"""
SUFFIX = """\
If you have any questions, comments, or suggestions regarding these
documents, please send them via email to python-docs@python.org.
"""
def count_pages(filename):
fp = open(filename)
count = 0
while 1:
lines = fp.readlines(1024*40)
if not lines:
break
for line in lines:
if line[:7] == "%%Page:":
count = count + 1
fp.close()
return count
def main():
PageCounter().run()
if __name__ == "__main__":
main()

1750
doc/tools/html2texi.pl Executable file

File diff suppressed because it is too large Load Diff

100
doc/tools/indfix.py Executable file
View File

@ -0,0 +1,100 @@
#! /usr/bin/env python
"""Combine similar index entries into an entry and subentries.
For example:
\item {foobar} (in module flotz), 23
\item {foobar} (in module whackit), 4323
becomes
\item {foobar}
\subitem in module flotz, 23
\subitem in module whackit, 4323
Note that an item which matches the format of a collapsable item but which
isn't part of a group of similar items is not modified.
"""
__version__ = '$Revision$'
import re
import StringIO
import sys
def cmp_entries(e1, e2):
return cmp(e1[1].lower(), e2[1].lower()) or cmp(e1, e2)
def dump_entries(write, entries):
if len(entries) == 1:
write(" \\item %s (%s)%s\n" % entries[0])
return
write(" \item %s\n" % entries[0][0])
# now sort these in a case insensitive manner:
if len(entries) > 0:
entries.sort(cmp_entries)
for xxx, subitem, pages in entries:
write(" \subitem %s%s\n" % (subitem, pages))
breakable_re = re.compile(
r" \\item (.*) [(](.*)[)]((?:(?:, \d+)|(?:, \\[a-z]*\{\d+\}))+)")
def process(ifn, ofn=None):
if ifn == "-":
ifp = sys.stdin
else:
ifp = open(ifn)
if ofn is None:
ofn = ifn
ofp = StringIO.StringIO()
entries = []
match = breakable_re.match
write = ofp.write
while 1:
line = ifp.readline()
if not line:
break
m = match(line)
if m:
entry = m.group(1, 2, 3)
if entries and entries[-1][0] != entry[0]:
dump_entries(write, entries)
entries = []
entries.append(entry)
elif entries:
dump_entries(write, entries)
entries = []
write(line)
else:
write(line)
del write
del match
ifp.close()
data = ofp.getvalue()
ofp.close()
if ofn == "-":
ofp = sys.stdout
else:
ofp = open(ofn, "w")
ofp.write(data)
ofp.close()
def main():
import getopt
outfile = None
opts, args = getopt.getopt(sys.argv[1:], "o:")
for opt, val in opts:
if opt in ("-o", "--output"):
outfile = val
filename = args[0]
outfile = outfile or filename
process(filename, outfile)
if __name__ == "__main__":
main()

19
doc/tools/keywords.py Normal file
View File

@ -0,0 +1,19 @@
#! /usr/bin/env python
# This Python program sorts and reformats the table of keywords in ref2.tex
l = []
try:
while 1:
l = l + raw_input().split()
except EOFError:
pass
l.sort()
for x in l[:]:
while l.count(x) > 1: l.remove(x)
ncols = 5
nrows = (len(l)+ncols-1)/ncols
for i in range(nrows):
for j in range(i, len(l), nrows):
print l[j].ljust(10),
print

183
doc/tools/listmodules Executable file
View File

@ -0,0 +1,183 @@
#! /usr/bin/env python
# -*- Python -*-
#
# This script can be used to identify undocumented modules in the Python
# standard library. Use it like this:
#
# .../Doc/tools/listmodules --ignore-from .../Doc/paper-<paper>/modlib.idx
"""%(program)s - list modules in the Python standard library
-a, --annotate Annotate the module names with the subdirectory they
live in
-c, --categorize Group the modules by subdirectory
-i <file>,
--ignore-from <file> Ignore the modules listed in <file>. <file> may
contain a list of module names or a module index file
as produced when formatting the Python documentation
(.idx or .html flavor).
If neither -a nor -c are given, the modules are listed in alphabetical
order.
Note that -a and -c are mutually exclusive.
Limitation: Modules loadable as shared objects may not be listed,
though this script attempts to locate such modules.
"""
__version__ = '$Revision$'
import getopt
import glob
import os
import re
import string
import sys
REMOVE_DIRS = ["dos-8x3", "encodings", "distutils",
"lib-old", "lib-stdwin", "test"]
def main():
args = sys.argv[1:]
annotate = 0
builtin = 0
categorize = 0
ignore_dict = {}
ignore = ignore_dict.has_key
try:
opts, args = getopt.getopt(
args, "abchi:",
["annotate", "built-in", "categorize", "help", "ignore-from="])
except getopt.error, msg:
sys.stdout = sys.stderr
print msg
print
usage()
sys.exit(2)
for opt, arg in opts:
if opt in ("-a", "--annotate"):
annotate = 1
elif opt in ("-b", "--built-in"):
builtin = 1
elif opt in ("-c", "--categorize"):
categorize = 1
elif opt in ("-h", "--help"):
usage()
sys.exit()
elif opt in ("-i", "--ignore-from"):
data = open(arg).read()
if data[:1] == "\\":
ignore_from_idx(data, ignore_dict)
else:
ignore_from_modulelist(data, ignore_dict)
if args or (annotate and categorize):
usage()
sys.exit(2)
#
# Populate the database:
#
srcdir = os.path.normpath(os.path.join(
os.path.dirname(sys.argv[0]), os.pardir, os.pardir))
os.chdir(srcdir)
modules_by_name = {}
modules_by_dir = {}
if builtin:
l = []
modules_by_dir["<builtin>"] = l
for name in sys.builtin_module_names:
if not ignore(name):
modules_by_name[name] = "<built-in>"
l.append(name)
rx = re.compile("Lib/plat-[a-zA-Z0-9]*/")
fp = os.popen("find Lib -name \*.py -print", "r")
while 1:
line = fp.readline()
if not line:
break
m = rx.match(line)
if m:
line = "Lib/plat-*/" + line[m.end():]
line = line[4:-4] # strip off 'Lib/' and '.py\n'
dir, name = os.path.split(line)
dir = dir or "<standard>"
if ignore(name):
continue
if dir not in REMOVE_DIRS:
modules_by_name[name] = dir
l = modules_by_dir.get(dir, [])
modules_by_dir[dir] = l
if name not in l:
l.append(name)
# load up extension modules:
pwd = os.getcwd()
try:
os.chdir("Modules")
dir = "<extension>"
for line in glob.glob("*module.c"):
name = line[:-8]
if ignore(name) or modules_by_name.has_key(name) or name == "xx":
continue
modules_by_name[name] = dir
l = modules_by_dir.get(dir, [])
modules_by_dir[dir] = l
if name not in l:
l.append(name)
finally:
os.chdir(pwd)
#
# Dump the results:
#
if annotate:
modules = modules_by_name.items()
modules.sort()
width = max(map(len, modules_by_name.keys()))
format = "%%-%ds %%s" % width
for name, dir in modules:
if dir and dir[0] != "<":
print format % (name, dir)
else:
print name
elif categorize:
modules = modules_by_dir.items()
modules.sort()
width = max(map(len, modules_by_dir.keys()))
format = "%%-%ds %%s" % width
for dir, names in modules:
names.sort()
print format % (dir, names[0])
for name in names[1:]:
print format % ('', name)
print
else:
modules = modules_by_name.keys()
modules.sort()
print string.join(modules, "\n")
def ignore_from_modulelist(data, ignore_dict):
for name in string.split(data):
ignore_dict[name] = name
def ignore_from_idx(data, ignore_dict):
data = string.replace(data, r"\hackscore {}", "_")
rx = re.compile(r"\\indexentry\s*{([^@]*)@")
for line in string.split(data, "\n"):
m = rx.match(line)
if m:
name = m.group(1)
ignore_dict[name] = name
def usage():
vars = {}
vars["program"] = os.path.basename(sys.argv[0])
print __doc__ % vars
if __name__ == "__main__":
main()

129
doc/tools/makesec.sh Executable file
View File

@ -0,0 +1,129 @@
#!/bin/bash
# Simple little checker for individual libref manual sections
#
# usage: makesec.sh section
#
# This script builds the minimal file necessary to run a single section
# through latex, does so, then converts the resulting dvi file to ps or pdf
# and feeds the result into a viewer. It's by no means foolproof, but seems
# to work okay for me (knock wood). It sure beats manually commenting out
# most of the man lib.tex file and running everything manually.
# It attempts to locate an appropriate dvi converter and viewer for the
# selected output format. It understands the following environment
# variables:
#
# PYSRC - refers to the root of your build tree (dir containing Doc)
# DVICVT - refers to a dvi converter like dvips or dvipdf
# VIEWER - refers to an appropriate viewer for the ps/pdf file
#
# Of the three, only PYSRC is currently required. The other two can be set
# to specify unusual tools which perform those tasks.
# Known issues:
# - It would be nice if the script could determine PYSRC on its own.
# - Something about \seealso{}s blows the latex stack, so they need
# to be commented out for now.
if [ x$PYSRC = x ] ; then
echo "PYSRC must refer to the Python source tree" 1>&2
exit 1
fi
if [ ! -d $PYSRC/Doc ] ; then
echo "Can't find a Doc subdirectory in $PYSRC" 1>&2
exit 1
fi
if [ "$#" -ne 1 ] ; then
echo "Must specify a single libref manual section on cmd line" 1>&2
exit 1
fi
# settle on a dvi converter
if [ x$DVICVT != x ] ; then
converter=$DVICVT
ext=`echo $DVICVT | sed -e 's/^dvi//'`
result=lib.$ext
elif [ x`which dvipdf` != x ] ; then
converter=`which dvipdf`
ext=.pdf
elif [ x`which dvips` != x ] ; then
converter=`which dvips`
ext=.ps
else
echo "Can't find a reasonable dvi converter" 1>&2
echo "Set DVICVT to refer to one" 1>&2
exit 1
fi
# how about a viewer?
if [ x$VIEWER != x ] ; then
viewer=$VIEWER
elif [ $ext = ".ps" -a x`which gv` != x ] ; then
viewer=gv
elif [ $ext = ".ps" -a x`which gs` != x ] ; then
viewer=gs
elif [ $ext = ".pdf" -a x`which acroread` != x ] ; then
viewer=acroread
elif [ $ext = ".pdf" -a "`uname`" = "Darwin" -a x`which open` != x ] ; then
viewer=open
elif [ $ext = ".pdf" -a x`which acroread` != x ] ; then
viewer=acroread
else
echo "Can't find a reasonable viewer" 1>&2
echo "Set VIEWER to refer to one" 1>&2
exit 1
fi
# make sure necessary links are in place
for f in howto.cls pypaper.sty ; do
rm -f $f
ln -s $PYSRC/Doc/$f
done
export TEXINPUTS=.:$PYSRC/Doc/texinputs:
# strip extension in case they gave full filename
inp=`basename $1 .tex`
# create the minimal framework necessary to run section through latex
tmpf=lib.tex
cat > $tmpf <<EOF
\documentclass{manual}
% NOTE: this file controls which chapters/sections of the library
% manual are actually printed. It is easy to customize your manual
% by commenting out sections that you are not interested in.
\title{Python Library Reference}
\input{boilerplate}
\makeindex % tell \index to actually write the
% .idx file
\makemodindex % ... and the module index as well.
\begin{document}
\maketitle
\ifhtml
\chapter*{Front Matter\label{front}}
\fi
\input{$inp}
\end{document}
EOF
latex $tmpf
$converter lib
$viewer lib.pdf
rm -f $tmpf howto.cls pypaper.sty *.idx *.syn
rm -f lib.{aux,log}

66
doc/tools/mkackshtml Executable file
View File

@ -0,0 +1,66 @@
#! /usr/bin/env python
# -*- Python -*-
import string
import support
import sys
def collect(fp):
names = []
while 1:
line = fp.readline()
if not line:
break
line = string.strip(line)
if line:
names.append(line)
else:
names = []
return names
def main():
options = support.Options()
options.columns = 4
options.variables["title"] = "Acknowledgements"
options.parse(sys.argv[1:])
names = collect(sys.stdin)
percol = (len(names) + options.columns - 1) / options.columns
colnums = []
for i in range(options.columns):
colnums.append(percol*i)
options.aesop_type = "information"
fp = options.get_output_file()
fp.write(string.rstrip(options.get_header()) + "\n")
fp.write(THANKS + "\n")
fp.write('<table width="100%" align="center">\n')
for i in range(percol):
fp.write(" <tr>\n")
for j in colnums:
try:
fp.write(" <td>%s</td>\n" % names[i + j])
except IndexError:
pass
fp.write(" </tr>\n")
fp.write("</table>\n")
fp.write(string.rstrip(options.get_footer()) + "\n")
fp.close()
THANKS = '''\
<p>These people have contributed in some way to the Python
documentation. This list is probably not complete -- if you feel that
you or anyone else should be on this list, please let us know (send
email to <a
href="mailto:python-docs@python.org">python-docs@python.org</a>), and
we will be glad to correct the problem.</p>
<p>It is only with the input and contributions of the Python community
that Python has such wonderful documentation -- <b>Thank You!</b></p>
'''
if __name__ == "__main__":
main()

665
doc/tools/mkhowto Executable file
View File

@ -0,0 +1,665 @@
#! /usr/bin/env python
# -*- Python -*-
"""usage: %(program)s [options...] file ...
Options specifying formats to build:
--html HyperText Markup Language (default)
--pdf Portable Document Format
--ps PostScript
--dvi 'DeVice Indepentent' format from TeX
--text ASCII text (requires lynx)
More than one output format may be specified, or --all.
HTML options:
--address, -a Specify an address for page footers.
--dir Specify the directory for HTML output.
--link Specify the number of levels to include on each page.
--split, -s Specify a section level for page splitting, default: %(max_split_depth)s.
--iconserver, -i Specify location of icons (default: ./).
--image-type Specify the image type to use in HTML output;
values: gif (default), png.
--numeric Don't rename the HTML files; just keep node#.html for
the filenames.
--style Specify the CSS file to use for the output (filename,
not a URL).
--up-link URL to a parent document.
--up-title Title of a parent document.
--favicon Icon to display in the browsers location bar.
Other options:
--a4 Format for A4 paper.
--letter Format for US letter paper (the default).
--help, -H Show this text.
--logging, -l Log stdout and stderr to a file (*.how).
--debugging, -D Echo commands as they are executed.
--keep, -k Keep temporary files around.
--quiet, -q Do not print command output to stdout.
(stderr is also lost, sorry; see *.how for errors)
"""
import getopt
import glob
import os
import re
import shutil
import string
import sys
if not hasattr(os.path, "abspath"):
# Python 1.5.1 or earlier
def abspath(path):
"""Return an absolute path."""
if not os.path.isabs(path):
path = os.path.join(os.getcwd(), path)
return os.path.normpath(path)
os.path.abspath = abspath
MYDIR = os.path.abspath(sys.path[0])
TOPDIR = os.path.dirname(MYDIR)
ISTFILE = os.path.join(TOPDIR, "texinputs", "python.ist")
NODE2LABEL_SCRIPT = os.path.join(MYDIR, "node2label.pl")
L2H_INIT_FILE = os.path.join(TOPDIR, "perl", "l2hinit.perl")
BIBTEX_BINARY = "bibtex"
DVIPS_BINARY = "dvips"
LATEX_BINARY = "latex"
LATEX2HTML_BINARY = "latex2html"
LYNX_BINARY = "lynx"
MAKEINDEX_BINARY = "makeindex"
PDFLATEX_BINARY = "pdflatex"
PERL_BINARY = "perl"
PYTHON_BINARY = "python"
def usage(options):
print __doc__ % options
def error(options, message, err=2):
sys.stdout = sys.stderr
print message
print
usage(options)
sys.exit(2)
class Options:
program = os.path.basename(sys.argv[0])
#
address = ''
builddir = None
debugging = 0
discard_temps = 1
have_temps = 0
icon_server = "."
image_type = "gif"
logging = 0
max_link_depth = 3
max_split_depth = 6
paper = "letter"
quiet = 0
runs = 0
numeric = 0
global_module_index = None
style_file = os.path.join(TOPDIR, "html", "style.css")
about_file = os.path.join(TOPDIR, "html", "about.dat")
up_link = None
up_title = None
favicon = None
#
# 'dvips_safe' is a weird option. It is used mostly to make
# LaTeX2HTML not try to be too smart about protecting the user
# from a bad version of dvips -- some versions would core dump if
# the path to the source DVI contained a dot, and it's appearantly
# difficult to determine if the version available has that bug.
# This option gets set when PostScript output is requested
# (because we're going to run dvips regardless, and we'll either
# know it succeeds before LaTeX2HTML is run, or we'll have
# detected the failure and bailed), or the user asserts that it's
# safe from the command line.
#
# So, why does LaTeX2HTML think it appropriate to protect the user
# from a dvips that's only potentially going to core dump? Only
# because they want to avoid doing a lot of work just to have to
# bail later with no useful intermediates. Unfortunately, they
# bail *before* they know whether dvips will be needed at all.
# I've gone around the bush a few times with the LaTeX2HTML
# developers over whether this is appropriate behavior, and they
# don't seem interested in changing their position.
#
dvips_safe = 0
#
DEFAULT_FORMATS = ("html",)
ALL_FORMATS = ("dvi", "html", "pdf", "ps", "text")
def __init__(self):
self.formats = []
self.l2h_init_files = []
def __getitem__(self, key):
# This is used when formatting the usage message.
try:
return getattr(self, key)
except AttributeError:
raise KeyError, key
def parse(self, args):
opts, args = getopt.getopt(args, "Hi:a:s:lDkqr:",
["all", "postscript", "help", "iconserver=",
"address=", "a4", "letter", "l2h-init=",
"link=", "split=", "logging", "debugging",
"keep", "quiet", "runs=", "image-type=",
"about=", "numeric", "style=", "paper=",
"up-link=", "up-title=", "dir=",
"global-module-index=", "dvips-safe",
"favicon="]
+ list(self.ALL_FORMATS))
for opt, arg in opts:
if opt == "--all":
self.formats = list(self.ALL_FORMATS)
self.dvips_safe = "ps" in self.formats
elif opt in ("-H", "--help"):
usage(self)
sys.exit()
elif opt == "--iconserver":
self.icon_server = arg
elif opt in ("-a", "--address"):
self.address = arg
elif opt == "--a4":
self.paper = "a4"
elif opt == "--letter":
self.paper = "letter"
elif opt == "--link":
self.max_link_depth = int(arg)
elif opt in ("-s", "--split"):
self.max_split_depth = int(arg)
elif opt in ("-l", "--logging"):
self.logging = self.logging + 1
elif opt in ("-D", "--debugging"):
self.debugging = self.debugging + 1
elif opt in ("-k", "--keep"):
self.discard_temps = 0
elif opt in ("-q", "--quiet"):
self.quiet = 1
elif opt in ("-r", "--runs"):
self.runs = int(arg)
elif opt == "--image-type":
self.image_type = arg
elif opt == "--about":
# always make this absolute:
self.about_file = os.path.normpath(
os.path.abspath(arg))
elif opt == "--numeric":
self.numeric = 1
elif opt == "--style":
self.style_file = os.path.abspath(arg)
elif opt == "--l2h-init":
self.l2h_init_files.append(os.path.abspath(arg))
elif opt == "--favicon":
self.favicon = arg
elif opt == "--up-link":
self.up_link = arg
elif opt == "--up-title":
self.up_title = arg
elif opt == "--global-module-index":
self.global_module_index = arg
elif opt == "--dir":
if os.sep == "\\":
arg = re.sub("/", "\\", arg)
self.builddir = os.path.expanduser(arg)
elif opt == "--paper":
self.paper = arg
elif opt == "--dvips-safe":
self.dvips_safe = 1
#
# Format specifiers:
#
elif opt[2:] in self.ALL_FORMATS:
self.add_format(opt[2:])
elif opt == "--postscript":
# synonym for --ps
self.add_format("ps")
self.initialize()
#
# return the args to allow the caller access:
#
return args
def add_format(self, format):
"""Add a format to the formats list if not present."""
if not format in self.formats:
if format == "ps":
# assume this is safe since we're going to run it anyway
self.dvips_safe = 1
self.formats.append(format)
def initialize(self):
"""Complete initialization. This is needed if parse() isn't used."""
# add the default format if no formats were specified:
if not self.formats:
self.formats = self.DEFAULT_FORMATS
# determine the base set of texinputs directories:
texinputs = string.split(os.environ.get("TEXINPUTS", ""), os.pathsep)
if not texinputs:
texinputs = ['']
self.base_texinputs = [
os.path.join(TOPDIR, "paper-" + self.paper),
os.path.join(TOPDIR, "texinputs"),
] + texinputs
if self.builddir:
self.builddir = os.path.abspath(self.builddir)
class Job:
latex_runs = 0
def __init__(self, options, path):
self.options = options
self.doctype = get_doctype(path)
self.filedir, self.doc = split_pathname(path)
self.builddir = os.path.abspath(options.builddir or self.doc)
if ("html" in options.formats or "text" in options.formats):
if not os.path.exists(self.builddir):
os.mkdir(self.builddir)
self.log_filename = os.path.join(self.builddir, self.doc + ".how")
else:
self.log_filename = os.path.abspath(self.doc + ".how")
if os.path.exists(self.log_filename):
os.unlink(self.log_filename)
l2hconf = self.doc + ".l2h"
if os.path.exists(l2hconf):
if os.path.exists(l2hconf + "~"):
os.unlink(l2hconf + "~")
os.rename(l2hconf, l2hconf + "~")
self.l2h_aux_init_file = self.doc + ".l2h"
self.write_l2h_aux_init_file()
def build(self):
self.setup_texinputs()
formats = self.options.formats
if "dvi" in formats or "ps" in formats:
self.build_dvi()
if "pdf" in formats:
self.build_pdf()
if "ps" in formats:
self.build_ps()
if "html" in formats:
self.require_temps()
self.build_html(self.builddir)
if self.options.icon_server == ".":
pattern = os.path.join(TOPDIR, "html", "icons",
"*." + self.options.image_type)
imgs = glob.glob(pattern)
if not imgs:
self.warning(
"Could not locate support images of type %s."
% `self.options.image_type`)
for fn in imgs:
new_fn = os.path.join(self.builddir, os.path.basename(fn))
shutil.copyfile(fn, new_fn)
if "text" in formats:
self.require_temps()
tempdir = self.doc
need_html = "html" not in formats
if self.options.max_split_depth != 1:
fp = open(self.l2h_aux_init_file, "a")
fp.write("# re-hack this file for --text:\n")
l2hoption(fp, "MAX_SPLIT_DEPTH", "1")
fp.write("1;\n")
fp.close()
tempdir = self.doc + "-temp-html"
need_html = 1
if need_html:
self.build_html(tempdir, max_split_depth=1)
self.build_text(tempdir)
if self.options.discard_temps:
self.cleanup()
def setup_texinputs(self):
texinputs = [self.filedir] + list(self.options.base_texinputs)
os.environ["TEXINPUTS"] = string.join(texinputs, os.pathsep)
self.message("TEXINPUTS=" + os.environ["TEXINPUTS"])
def build_aux(self, binary=None):
if binary is None:
binary = LATEX_BINARY
new_index( "%s.ind" % self.doc, "genindex")
new_index("mod%s.ind" % self.doc, "modindex")
self.run("%s %s" % (binary, self.doc))
self.use_bibtex = check_for_bibtex(self.doc + ".aux")
self.latex_runs = 1
def build_dvi(self):
self.use_latex(LATEX_BINARY)
def build_pdf(self):
self.use_latex(PDFLATEX_BINARY)
def use_latex(self, binary):
self.require_temps(binary=binary)
if self.latex_runs < 2:
if os.path.isfile("mod%s.idx" % self.doc):
self.run("%s mod%s.idx" % (MAKEINDEX_BINARY, self.doc))
use_indfix = 0
if os.path.isfile(self.doc + ".idx"):
use_indfix = 1
# call to Doc/tools/fix_hack omitted; doesn't appear necessary
self.run("%s %s.idx" % (MAKEINDEX_BINARY, self.doc))
import indfix
indfix.process(self.doc + ".ind")
if self.use_bibtex:
self.run("%s %s" % (BIBTEX_BINARY, self.doc))
self.process_synopsis_files()
self.run("%s %s" % (binary, self.doc))
self.latex_runs = self.latex_runs + 1
if os.path.isfile("mod%s.idx" % self.doc):
self.run("%s -s %s mod%s.idx"
% (MAKEINDEX_BINARY, ISTFILE, self.doc))
if use_indfix:
self.run("%s -s %s %s.idx"
% (MAKEINDEX_BINARY, ISTFILE, self.doc))
indfix.process(self.doc + ".ind")
self.process_synopsis_files()
#
# and now finish it off:
#
if os.path.isfile(self.doc + ".toc") and binary == PDFLATEX_BINARY:
import toc2bkm
if self.doctype == "manual":
bigpart = "chapter"
else:
bigpart = "section"
toc2bkm.process(self.doc + ".toc", self.doc + ".bkm", bigpart)
if self.use_bibtex:
self.run("%s %s" % (BIBTEX_BINARY, self.doc))
self.run("%s %s" % (binary, self.doc))
self.latex_runs = self.latex_runs + 1
def process_synopsis_files(self):
synopsis_files = glob.glob(self.doc + "*.syn")
for path in synopsis_files:
uniqify_module_table(path)
def build_ps(self):
self.run("%s -N0 -o %s.ps %s" % (DVIPS_BINARY, self.doc, self.doc))
def build_html(self, builddir, max_split_depth=None):
if max_split_depth is None:
max_split_depth = self.options.max_split_depth
texfile = None
for p in string.split(os.environ["TEXINPUTS"], os.pathsep):
fn = os.path.join(p, self.doc + ".tex")
if os.path.isfile(fn):
texfile = fn
break
if not texfile:
self.warning("Could not locate %s.tex; aborting." % self.doc)
sys.exit(1)
# remove leading ./ (or equiv.); might avoid problems w/ dvips
if texfile[:2] == os.curdir + os.sep:
texfile = texfile[2:]
# build the command line and run LaTeX2HTML:
if not os.path.isdir(builddir):
os.mkdir(builddir)
else:
for fname in glob.glob(os.path.join(builddir, "*.html")):
os.unlink(fname)
args = [LATEX2HTML_BINARY,
"-init_file", self.l2h_aux_init_file,
"-dir", builddir,
texfile
]
self.run(string.join(args)) # XXX need quoting!
# ... postprocess
shutil.copyfile(self.options.style_file,
os.path.join(builddir, self.doc + ".css"))
shutil.copyfile(os.path.join(builddir, self.doc + ".html"),
os.path.join(builddir, "index.html"))
if max_split_depth != 1:
label_file = os.path.join(builddir, "labels.pl")
fp = open(label_file)
about_node = None
target = " = q/about/;\n"
x = len(target)
while 1:
line = fp.readline()
if not line:
break
if line[-x:] == target:
line = fp.readline()
m = re.search(r"\|(node\d+\.[a-z]+)\|", line)
about_node = m.group(1)
shutil.copyfile(os.path.join(builddir, about_node),
os.path.join(builddir, "about.html"))
break
if not self.options.numeric:
pwd = os.getcwd()
try:
os.chdir(builddir)
self.run("%s %s *.html" % (PERL_BINARY, NODE2LABEL_SCRIPT))
finally:
os.chdir(pwd)
# These files need to be cleaned up here since builddir there
# can be more than one, so we clean each of them.
if self.options.discard_temps:
for fn in ("images.tex", "images.log", "images.aux"):
safe_unlink(os.path.join(builddir, fn))
def build_text(self, tempdir=None):
if tempdir is None:
tempdir = self.doc
indexfile = os.path.join(tempdir, "index.html")
self.run("%s -nolist -dump %s >%s.txt"
% (LYNX_BINARY, indexfile, self.doc))
def require_temps(self, binary=None):
if not self.latex_runs:
self.build_aux(binary=binary)
def write_l2h_aux_init_file(self):
options = self.options
fp = open(self.l2h_aux_init_file, "w")
d = string_to_perl(os.path.dirname(L2H_INIT_FILE))
fp.write("package main;\n"
"push (@INC, '%s');\n"
"$mydir = '%s';\n"
% (d, d))
fp.write(open(L2H_INIT_FILE).read())
for filename in options.l2h_init_files:
fp.write("\n# initialization code incorporated from:\n# ")
fp.write(filename)
fp.write("\n")
fp.write(open(filename).read())
fp.write("\n"
"# auxillary init file for latex2html\n"
"# generated by mkhowto\n"
"$NO_AUTO_LINK = 1;\n"
)
l2hoption(fp, "ABOUT_FILE", options.about_file)
l2hoption(fp, "ICONSERVER", options.icon_server)
l2hoption(fp, "IMAGE_TYPE", options.image_type)
l2hoption(fp, "ADDRESS", options.address)
l2hoption(fp, "MAX_LINK_DEPTH", options.max_link_depth)
l2hoption(fp, "MAX_SPLIT_DEPTH", options.max_split_depth)
l2hoption(fp, "EXTERNAL_UP_LINK", options.up_link)
l2hoption(fp, "EXTERNAL_UP_TITLE", options.up_title)
l2hoption(fp, "FAVORITES_ICON", options.favicon)
l2hoption(fp, "GLOBAL_MODULE_INDEX", options.global_module_index)
l2hoption(fp, "DVIPS_SAFE", options.dvips_safe)
fp.write("1;\n")
fp.close()
def cleanup(self):
self.__have_temps = 0
for pattern in ("%s.aux", "%s.log", "%s.out", "%s.toc", "%s.bkm",
"%s.idx", "%s.ilg", "%s.ind", "%s.pla",
"%s.bbl", "%s.blg",
"mod%s.idx", "mod%s.ind", "mod%s.ilg",
):
safe_unlink(pattern % self.doc)
map(safe_unlink, glob.glob(self.doc + "*.syn"))
for spec in ("IMG*", "*.pl", "WARNINGS", "index.dat", "modindex.dat"):
pattern = os.path.join(self.doc, spec)
map(safe_unlink, glob.glob(pattern))
if "dvi" not in self.options.formats:
safe_unlink(self.doc + ".dvi")
if os.path.isdir(self.doc + "-temp-html"):
shutil.rmtree(self.doc + "-temp-html", ignore_errors=1)
if not self.options.logging:
os.unlink(self.log_filename)
if not self.options.debugging:
os.unlink(self.l2h_aux_init_file)
def run(self, command):
self.message(command)
if sys.platform.startswith("win"):
rc = os.system(command)
else:
rc = os.system("(%s) </dev/null >>%s 2>&1"
% (command, self.log_filename))
if rc:
self.warning(
"Session transcript and error messages are in %s."
% self.log_filename)
if hasattr(os, "WIFEXITED"):
if os.WIFEXITED(rc):
self.warning("Exited with status %s." % os.WEXITSTATUS(rc))
else:
self.warning("Killed by signal %s." % os.WSTOPSIG(rc))
else:
self.warning("Return code: %s" % rc)
sys.stderr.write("The relevant lines from the transcript are:\n")
sys.stderr.write("-" * 72 + "\n")
sys.stderr.writelines(get_run_transcript(self.log_filename))
sys.exit(rc)
def message(self, msg):
msg = "+++ " + msg
if not self.options.quiet:
print msg
self.log(msg + "\n")
def warning(self, msg):
msg = "*** %s\n" % msg
sys.stderr.write(msg)
self.log(msg)
def log(self, msg):
fp = open(self.log_filename, "a")
fp.write(msg)
fp.close()
def get_run_transcript(filename):
"""Return lines from the transcript file for the most recent run() call."""
fp = open(filename)
lines = fp.readlines()
fp.close()
lines.reverse()
L = []
for line in lines:
L.append(line)
if line[:4] == "+++ ":
break
L.reverse()
return L
def safe_unlink(path):
"""Unlink a file without raising an error if it doesn't exist."""
try:
os.unlink(path)
except os.error:
pass
def split_pathname(path):
path = os.path.abspath(path)
dirname, basename = os.path.split(path)
if basename[-4:] == ".tex":
basename = basename[:-4]
return dirname, basename
_doctype_rx = re.compile(r"\\documentclass(?:\[[^]]*\])?{([a-zA-Z]*)}")
def get_doctype(path):
fp = open(path)
doctype = None
while 1:
line = fp.readline()
if not line:
break
m = _doctype_rx.match(line)
if m:
doctype = m.group(1)
break
fp.close()
return doctype
def main():
options = Options()
try:
args = options.parse(sys.argv[1:])
except getopt.error, msg:
error(options, msg)
if not args:
# attempt to locate single .tex file in current directory:
args = glob.glob("*.tex")
if not args:
error(options, "No file to process.")
if len(args) > 1:
error(options, "Could not deduce which files should be processed.")
#
# parameters are processed, let's go!
#
for path in args:
Job(options, path).build()
def l2hoption(fp, option, value):
if value:
fp.write('$%s = "%s";\n' % (option, string_to_perl(str(value))))
_to_perl = {}
for c in map(chr, range(1, 256)):
_to_perl[c] = c
_to_perl["@"] = "\\@"
_to_perl["$"] = "\\$"
_to_perl['"'] = '\\"'
def string_to_perl(s):
return string.join(map(_to_perl.get, s), '')
def check_for_bibtex(filename):
fp = open(filename)
pos = string.find(fp.read(), r"\bibdata{")
fp.close()
return pos >= 0
def uniqify_module_table(filename):
lines = open(filename).readlines()
if len(lines) > 1:
if lines[-1] == lines[-2]:
del lines[-1]
open(filename, "w").writelines(lines)
def new_index(filename, label="genindex"):
fp = open(filename, "w")
fp.write(r"""\
\begin{theindex}
\label{%s}
\end{theindex}
""" % label)
fp.close()
if __name__ == "__main__":
main()

63
doc/tools/mkinfo Executable file
View File

@ -0,0 +1,63 @@
#! /bin/sh
# -*- Ksh -*-
# Script to drive the HTML-info conversion process.
# Pass in upto three parameters:
# - the name of the main tex file
# - the name of the output file in texi format (optional)
# - the name of the output file in info format (optional)
#
# Written by Fred L. Drake, Jr. <fdrake@acm.org>
EMACS=${EMACS:-emacs}
MAKEINFO=${MAKEINFO:-makeinfo}
# Normalize file name since something called by html2texi.pl seems to
# screw up with relative path names.
FILENAME="$1"
DOCDIR=`dirname "$FILENAME"`
DOCFILE=`basename "$FILENAME"`
DOCNAME=`basename "$FILENAME" .tex`
if [ $# -gt 1 ]; then
TEXINAME="$2"
else
TEXINAME="python-$DOCNAME.texi"
fi
if [ $# -gt 2 ]; then
INFONAME="$3"
else
INFONAME="python-$DOCNAME.info"
fi
# Now build the real directory names, and locate our support stuff:
WORKDIR=`pwd`
cd `dirname $0`
TOOLSDIR=`pwd`
cd $DOCDIR
DOCDIR=`pwd`
cd $WORKDIR
run() {
# show what we're doing, like make does:
echo "$*"
"$@" || exit $?
}
# generate the Texinfo file:
run $EMACS -batch -q --no-site-file -l $TOOLSDIR/py2texi.el \
--eval "(setq py2texi-dirs '(\"./\" \"../texinputs/\" \"$DOCDIR\"))" \
--eval "(setq py2texi-texi-file-name \"$TEXINAME\")" \
--eval "(setq py2texi-info-file-name \"$INFONAME\")" \
--eval "(py2texi \"$DOCDIR/$DOCFILE\")" \
-f kill-emacs
echo Done
# generate the .info files:
run $MAKEINFO --footnote-style end --fill-column 72 \
--paragraph-indent 0 --output=$INFONAME $TEXINAME

158
doc/tools/mkmodindex Executable file
View File

@ -0,0 +1,158 @@
#! /usr/bin/env python
# -*- Python -*-
"""usage: %(program)s [options] file...
Supported options:
--address addr
-a addr Set the address text to include at the end of the generated
HTML; this should be used for contact information.
--columns cols
-c cols Set the number of columns each index section should be
displayed in. The default is 1.
--help
-h Display this help message.
--letters
-l Split the output into sections by letter.
--output file
-o file Write output to 'file' instead of standard out.
--iconserver is Use 'is' as the directory containing icons for the
navigation bar. The default is 'icons'.
--title str Set the page title to 'str'. The default is 'Global
Module Index'.
--uplink url Set the upward link URL. The default is './'.
--uptitle str Set the upward link title. The default is 'Python
Documentation Index'.
"""
import os
import re
import sys
from xml.sax.saxutils import quoteattr
import buildindex
import support
class IndexOptions(support.Options):
aesop_type = "links"
def __init__(self):
support.Options.__init__(self)
self.add_args("l", ["letters"])
self.letters = 0
def handle_option(self, opt, val):
if opt in ("-l", "--letters"):
self.letters = 1
def usage(self):
program = os.path.basename(sys.argv[0])
print __doc__ % {"program": program}
links = [
('author', 'acks.html', 'Acknowledgements'),
('help', 'about.html', 'About the Python Documentation'),
]
def get_header(self):
header = support.Options.get_header(self)
s = ''
for rel, href, title in self.links:
s += '<link rel="%s" href="%s"' % (rel, href)
if title:
s += ' title=' + quoteattr(title)
s += '>\n '
return header.replace("<link ", s + "<link ", 1)
class Node(buildindex.Node):
def __init__(self, link, str, seqno, platinfo):
self.annotation = platinfo or None
if str[0][-5:] == "</tt>":
str = str[:-5]
self.modname = str
buildindex.Node.__init__(self, link, self.modname, seqno)
if platinfo:
s = '<tt class="module">%s</tt> %s' \
% (self.modname, self.annotation)
else:
s = '<tt class="module">%s</tt>' % str
self.text = [s]
def __str__(self):
if self.annotation:
return '<tt class="module">%s</tt> %s' \
% (self.modname, self.annotation)
else:
return '<tt class="module">%s</tt>' % self.modname
_rx = re.compile(
"<dt><a href=['\"](module-.*\.html)(?:#l2h-\d+)?['\"]>"
"<tt class=['\"]module['\"]>([a-zA-Z_][a-zA-Z0-9_.]*)</tt>\s*(<em>"
"\(<span class=['\"]platform['\"]>.*</span>\)</em>)?</a>")
def main():
options = IndexOptions()
options.variables["title"] = "Global Module Index"
options.parse(sys.argv[1:])
args = options.args
if not args:
args = ["-"]
#
# Collect the input data:
#
nodes = []
has_plat_flag = 0
for ifn in args:
if ifn == "-":
ifp = sys.stdin
dirname = ''
else:
ifp = open(ifn)
dirname = os.path.dirname(ifn)
while 1:
line = ifp.readline()
if not line:
break
m = _rx.match(line)
if m:
# This line specifies a module!
basename, modname, platinfo = m.group(1, 2, 3)
has_plat_flag = has_plat_flag or platinfo
linkfile = os.path.join(dirname, basename)
nodes.append(Node('<a href="%s">' % linkfile, modname,
len(nodes), platinfo))
ifp.close()
#
# Generate all output:
#
num_nodes = len(nodes)
# Here's the HTML generation:
parts = [options.get_header(),
buildindex.process_nodes(nodes, options.columns, options.letters),
options.get_footer(),
]
if has_plat_flag:
parts.insert(1, PLAT_DISCUSS)
html = ''.join(parts)
program = os.path.basename(sys.argv[0])
fp = options.get_output_file()
fp.write(html.rstrip() + "\n")
if options.outputfile == "-":
sys.stderr.write("%s: %d index nodes\n" % (program, num_nodes))
else:
print
print "%s: %d index nodes" % (program, num_nodes)
PLAT_DISCUSS = """
<p> Some module names are followed by an annotation indicating what
platform they are available on.</p>
"""
if __name__ == "__main__":
main()

90
doc/tools/mkpkglist Executable file
View File

@ -0,0 +1,90 @@
#! /usr/bin/env python
#
# Simple script to create the table that lists the packages available
# for download. This expects the downloadable files and the Makefile
# to be in the current directory.
#
# The output of this script can be pasted directly into the download
# page for the documentation.
import os
import sys
from os.path import isfile
PKG_TYPES = [
# human name, filename prefix
("HTML", "html"),
("PDF (US-Letter)", "pdf-letter"),
("PDF (A4)", "pdf-a4"),
("PostScript (US-Letter)", "postscript-letter"),
("PostScript (A4)", "postscript-a4"),
("GNU info", "info"),
("iSilo", "isilo"),
("LaTeX", "latex"),
]
fp = open("Makefile")
for line in fp:
line = line.replace('=', ' ', 1)
parts = line.split()
if parts[:1] == ["RELEASE"]:
release = parts[1]
break
else:
print >>sys.stderr, "Could not locate RELEASE in Makefile."
sys.exit(1)
print '''\
<table border="1" cellpadding="3" align="center">
<thead>
<tr bgcolor="#99ccff"><th rowspan="2">Content</th>
<th colspan="3">Format</th>
</tr>
<tr bgcolor="#99ccff"><th>ZIP</th><th>GZip</th><th>BZip2</th>
</thead>
<tbody>'''
# formatted using FILE_TEMPLATE % (release, prefix, release, extension)
FILE_TEMPLATE = '''\
<td><a href="../../ftp/python/doc/%s/%s-%s%s"
>%dK</a></td>'''
NO_FILE_TEMPLATE = '''\
<td>&nbsp;</td>'''
def get_size(prefix, ext):
fn = "%s-%s%s" % (prefix, release, ext)
return int(round(os.path.getsize(fn) / 1024.0))
def get_file_cell(prefix, ext, have):
if have:
kb = get_size(prefix, ext)
return FILE_TEMPLATE % (release, prefix, release, ext, kb)
else:
return NO_FILE_TEMPLATE
for name, prefix in PKG_TYPES:
zip_fn = "%s-%s.zip" % (prefix, release)
tgz_fn = "%s-%s.tgz" % (prefix, release)
bz2_fn = "%s-%s.tar.bz2" % (prefix, release)
have_zip = isfile(zip_fn)
have_tgz = isfile(tgz_fn)
have_bz2 = isfile(bz2_fn)
if have_zip or have_tgz or have_bz2:
print " <tr><td>%s</td>" % name
print get_file_cell(prefix, ".zip", have_zip)
print get_file_cell(prefix, ".tgz", have_tgz)
print get_file_cell(prefix, ".tar.bz2", have_bz2)
print " </tr>"
print '''\
</tbody>
</table>
'''

164
doc/tools/mksourcepkg Executable file
View File

@ -0,0 +1,164 @@
#! /usr/bin/env python
# -*- Python -*-
"""%(program)s - script to create the latex source distribution
usage:
%(program)s [-t|--tools] release [tag]
with -t|--tools: doesn't include the documents, only the framework
without [tag]: generate from the current version that's checked in
(*NOT* what's in the current directory!)
with [tag]: generate from the named tag
"""
#* should be modified to get the Python version number automatically
# from the Makefile or someplace.
import getopt
import glob
import os
import re
import shutil
import sys
import tempfile
import cvsinfo
quiet = 0
rx = re.compile(r":ext:(?:[a-zA-Z0-9]+)@cvs\.([a-zA-Z0-9]+).sourceforge.net:"
r"/cvsroot/\1")
def main():
global quiet
try:
opts, args = getopt.getopt(sys.argv[1:], "abgtzq",
["all", "bzip2", "gzip", "tools", "zip",
"quiet"])
except getopt.error, e:
usage(warning=str(e))
sys.exit(2)
if len(args) not in (1, 2):
usage(warning="wrong number of parameters")
sys.exit(2)
tools = 0
formats = {}
for opt, arg in opts:
if opt in ("-t", "--tools"):
tools = 1
elif opt in ("-q", "--quiet"):
quiet = quiet + 1
elif opt in ("-b", "--bzip2"):
formats["bzip2"] = 1
elif opt in ("-g", "--gzip"):
formats["gzip"] = 1
elif opt in ("-z", "--zip"):
formats["zip"] = 1
elif opt in ("-a", "--all"):
formats["bzip2"] = 1
formats["gzip"] = 1
formats["zip"] = 1
if formats:
# make order human-predictable
formats = formats.keys()
formats.sort()
else:
formats = ["gzip"]
release = args[0]
cvstag = None
if len(args) > 1:
cvstag = args[1]
tempdir = tempfile.mktemp()
os.mkdir(tempdir)
pkgdir = os.path.join(tempdir, "Python-Docs-" + release)
os.mkdir(pkgdir)
pwd = os.getcwd()
mydir = os.path.abspath(os.path.dirname(sys.argv[0]))
info = cvsinfo.RepositoryInfo(mydir)
cvsroot = info.get_cvsroot()
m = rx.match(cvsroot)
if m:
# If this is an authenticated SourceForge repository, convert to
# anonymous usage for the export/checkout, since that avoids the
# SSH overhead.
group = m.group(1)
cvsroot = ":pserver:anonymous@cvs.%s.sourceforge.net:/cvsroot/%s" \
% (group, group)
# For some reason, SourceForge/CVS doesn't seem to care that we
# might not have done a "cvs login" to the anonymous server.
# That avoids a lot of painful gunk here.
os.chdir(tempdir)
if not quiet:
print "--- current directory is:", pkgdir
if cvstag:
run("cvs -d%s export -r %s -d Python-Docs-%s python/dist/src/Doc"
% (cvsroot, cvstag, release))
else:
run("cvs -Q -d%s checkout -d Python-Docs-%s python/dist/src/Doc"
% (cvsroot, release))
# remove CVS directories
for p in ('*/CVS', '*/*/CVS', '*/*/*/CVS'):
map(shutil.rmtree, glob.glob(p))
for f in ('.cvsignore', '*/.cvsignore'):
map(os.unlink, glob.glob(f))
LICENSE = os.path.normpath(
os.path.join(mydir, os.pardir, os.pardir, "LICENSE"))
shutil.copyfile(LICENSE, "LICENSE")
if tools:
archive = "doctools-" + release
# we don't want the actual documents in this case:
for d in ("api", "dist", "doc", "ext", "inst",
"lib", "mac", "ref", "tut"):
shutil.rmtree(os.path.join(pkgdir, d))
else:
archive = "latex-" + release
# XXX should also remove the .cvsignore files at this point
os.chdir(tempdir)
archive = os.path.join(pwd, archive)
for format in formats:
if format == "bzip2":
run("tar cf - Python-Docs-%s | bzip2 -9 >%s.tar.bz2"
% (release, archive))
elif format == "gzip":
run("tar cf - Python-Docs-%s | gzip -9 >%s.tgz"
% (release, archive))
elif format == "zip":
if os.path.exists(archive + ".zip"):
os.unlink(archive + ".zip")
run("zip -q -r9 %s.zip Python-Docs-%s"
% (archive, release))
# clean up the work area:
os.chdir(pwd)
shutil.rmtree(tempdir)
def run(cmd):
if quiet < 2:
print "+++", cmd
if quiet:
cmd = "%s >/dev/null" % cmd
rc = os.system(cmd)
if rc:
sys.exit(rc)
def usage(warning=None):
stdout = sys.stdout
sys.stdout = sys.stderr
program = os.path.basename(sys.argv[0])
try:
if warning:
print "%s: %s\n" % (program, warning)
print __doc__ % {"program": program}
finally:
sys.stdout = stdout
if __name__ == "__main__":
main()

71
doc/tools/node2label.pl Executable file
View File

@ -0,0 +1,71 @@
#! /usr/bin/env perl
# On Cygwin, we actually have to generate a temporary file when doing
# the inplace edit, or we'll get permission errors. Not sure who's
# bug this is, except that it isn't ours. To deal with this, we
# generate backups during the edit phase and remove them at the end.
#
use English;
$INPLACE_EDIT = '.bak';
# read the labels, then reverse the mappings
require "labels.pl";
%nodes = ();
my $key;
# sort so that we get a consistent assignment for nodes with multiple labels
foreach $label (sort keys %external_labels) {
#
# If the label can't be used as a filename on non-Unix platforms,
# skip it. Such labels may be used internally within the documentation,
# but will never be used for filename generation.
#
if ($label =~ /^([-.a-zA-Z0-9]+)$/) {
$key = $external_labels{$label};
$key =~ s|^/||;
$nodes{$key} = $label;
}
}
# This adds the "internal" labels added for indexing. These labels will not
# be used for file names.
require "intlabels.pl";
foreach $label (keys %internal_labels) {
$key = $internal_labels{$label};
$key =~ s|^/||;
if (defined($nodes{$key})) {
$nodes{$label} = $nodes{$key};
}
}
# collect labels that have been used
%newnames = ();
while (<>) {
# don't want to do one s/// per line per node
# so look for lines with hrefs, then do s/// on nodes present
if (/(HREF|href)=[\"\']node\d+\.html[\#\"\']/) {
@parts = split(/(HREF|href)\=[\"\']/);
shift @parts;
for $node (@parts) {
$node =~ s/[\#\"\'].*$//g;
chomp($node);
if (defined($nodes{$node})) {
$label = $nodes{$node};
if (s/(HREF|href)=([\"\'])$node([\#\"\'])/href=$2$label.html$3/g) {
s/(HREF|href)=([\"\'])$label.html/href=$2$label.html/g;
$newnames{$node} = "$label.html";
}
}
}
}
print;
}
foreach $oldname (keys %newnames) {
rename($oldname, $newnames{$oldname});
}
foreach $filename (glob('*.bak')) {
unlink($filename);
}

471
doc/tools/prechm.py Normal file
View File

@ -0,0 +1,471 @@
"""
Makes the necesary files to convert from plain html of
Python 1.5 and 1.5.x Documentation to
Microsoft HTML Help format version 1.1
Doesn't change the html's docs.
by hernan.foffani@iname.com
no copyright and no responsabilities.
modified by Dale Nagata for Python 1.5.2
Renamed from make_chm.py to prechm.py, and checked into the Python
project, 19-Apr-2002 by Tim Peters. Assorted modifications by Tim
and Fred Drake. Obtained from Robin Dunn's .chm packaging of the
Python 2.2 docs, at <http://alldunn.com/python/>.
"""
import sys
import os
from formatter import NullWriter, AbstractFormatter
from htmllib import HTMLParser
import getopt
import cgi
usage_mode = '''
Usage: make_chm.py [-c] [-k] [-p] [-v 1.5[.x]] filename
-c: does not build filename.hhc (Table of Contents)
-k: does not build filename.hhk (Index)
-p: does not build filename.hhp (Project File)
-v 1.5[.x]: makes help for the python 1.5[.x] docs
(default is python 1.5.2 docs)
'''
# Project file (*.hhp) template. 'arch' is the file basename (like
# the pythlp in pythlp.hhp); 'version' is the doc version number (like
# the 2.2 in Python 2.2).
# The magical numbers in the long line under [WINDOWS] set most of the
# user-visible features (visible buttons, tabs, etc).
# About 0x10384e: This defines the buttons in the help viewer. The
# following defns are taken from htmlhelp.h. Not all possibilities
# actually work, and not all those that work are available from the Help
# Workshop GUI. In particular, the Zoom/Font button works and is not
# available from the GUI. The ones we're using are marked with 'x':
#
# 0x000002 Hide/Show x
# 0x000004 Back x
# 0x000008 Forward x
# 0x000010 Stop
# 0x000020 Refresh
# 0x000040 Home x
# 0x000080 Forward
# 0x000100 Back
# 0x000200 Notes
# 0x000400 Contents
# 0x000800 Locate x
# 0x001000 Options x
# 0x002000 Print x
# 0x004000 Index
# 0x008000 Search
# 0x010000 History
# 0x020000 Favorites
# 0x040000 Jump 1
# 0x080000 Jump 2
# 0x100000 Zoom/Font x
# 0x200000 TOC Next
# 0x400000 TOC Prev
project_template = '''
[OPTIONS]
Compiled file=%(arch)s.chm
Contents file=%(arch)s.hhc
Default Window=%(arch)s
Default topic=index.html
Display compile progress=No
Full text search stop list file=%(arch)s.stp
Full-text search=Yes
Index file=%(arch)s.hhk
Language=0x409
Title=Python %(version)s Documentation
[WINDOWS]
%(arch)s="Python %(version)s Documentation","%(arch)s.hhc","%(arch)s.hhk",\
"index.html","index.html",,,,,0x63520,220,0x10384e,[271,372,740,718],,,,,,,0
[FILES]
'''
contents_header = '''\
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<HTML>
<HEAD>
<meta name="GENERATOR" content="Microsoft&reg; HTML Help Workshop 4.1">
<!-- Sitemap 1.0 -->
</HEAD><BODY>
<OBJECT type="text/site properties">
<param name="Window Styles" value="0x801227">
<param name="ImageType" value="Folder">
</OBJECT>
<UL>
'''
contents_footer = '''\
</UL></BODY></HTML>
'''
object_sitemap = '''\
<OBJECT type="text/sitemap">
<param name="Name" value="%s">
<param name="Local" value="%s">
</OBJECT>
'''
# List of words the full text search facility shouldn't index. This
# becomes file ARCH.stp. Note that this list must be pretty small!
# Different versions of the MS docs claim the file has a maximum size of
# 256 or 512 bytes (including \r\n at the end of each line).
# Note that "and", "or", "not" and "near" are operators in the search
# language, so no point indexing them even if we wanted to.
stop_list = '''
a and are as at
be but by
for
if in into is it
near no not
of on or
such
that the their then there these they this to
was will with
'''
# s is a string or None. If None or empty, return None. Else tack '.html'
# on to the end, unless it's already there.
def addhtml(s):
if s:
if not s.endswith('.html'):
s += '.html'
return s
# Convenience class to hold info about "a book" in HTMLHelp terms == a doc
# directory in Python terms.
class Book:
def __init__(self, directory, title, firstpage,
contentpage=None, indexpage=None):
self.directory = directory
self.title = title
self.firstpage = addhtml(firstpage)
self.contentpage = addhtml(contentpage)
self.indexpage = addhtml(indexpage)
# Library Doc list of books:
# each 'book' : (Dir, Title, First page, Content page, Index page)
supported_libraries = {
'2.2':
[
Book('.', 'Main page', 'index'),
Book('.', 'Global Module Index', 'modindex'),
Book('whatsnew', "What's New", 'index', 'contents'),
Book('tut','Tutorial','tut','node2'),
Book('lib','Library Reference','lib','contents','genindex'),
Book('ref','Language Reference','ref','contents','genindex'),
Book('mac','Macintosh Reference','mac','contents','genindex'),
Book('ext','Extending and Embedding','ext','contents'),
Book('api','Python/C API','api','contents','genindex'),
Book('doc','Documenting Python','doc','contents'),
Book('inst','Installing Python Modules', 'inst', 'index'),
Book('dist','Distributing Python Modules', 'dist', 'index'),
],
'2.1.1':
[
Book('.', 'Main page', 'index'),
Book('.', 'Global Module Index', 'modindex'),
Book('tut','Tutorial','tut','node2'),
Book('lib','Library Reference','lib','contents','genindex'),
Book('ref','Language Reference','ref','contents','genindex'),
Book('mac','Macintosh Reference','mac','contents','genindex'),
Book('ext','Extending and Embedding','ext','contents'),
Book('api','Python/C API','api','contents','genindex'),
Book('doc','Documenting Python','doc','contents'),
Book('inst','Installing Python Modules', 'inst', 'index'),
Book('dist','Distributing Python Modules', 'dist', 'index'),
],
'2.0.0':
[
Book('.', 'Global Module Index', 'modindex'),
Book('tut','Tutorial','tut','node2'),
Book('lib','Library Reference','lib','contents','genindex'),
Book('ref','Language Reference','ref','contents','genindex'),
Book('mac','Macintosh Reference','mac','contents','genindex'),
Book('ext','Extending and Embedding','ext','contents'),
Book('api','Python/C API','api','contents','genindex'),
Book('doc','Documenting Python','doc','contents'),
Book('inst','Installing Python Modules', 'inst', 'contents'),
Book('dist','Distributing Python Modules', 'dist', 'contents'),
],
# <dnagata@creo.com> Apr 17/99: library for 1.5.2 version:
# <hernan.foffani@iname.com> May 01/99: library for 1.5.2 (04/30/99):
'1.5.2':
[
Book('tut','Tutorial','tut','node2'),
Book('lib','Library Reference','lib','contents','genindex'),
Book('ref','Language Reference','ref','contents','genindex'),
Book('mac','Macintosh Reference','mac','contents','genindex'),
Book('ext','Extending and Embedding','ext','contents'),
Book('api','Python/C API','api','contents','genindex'),
Book('doc','Documenting Python','doc','contents')
],
# library for 1.5.1 version:
'1.5.1':
[
Book('tut','Tutorial','tut','contents'),
Book('lib','Library Reference','lib','contents','genindex'),
Book('ref','Language Reference','ref-1','ref-2','ref-11'),
Book('ext','Extending and Embedding','ext','contents'),
Book('api','Python/C API','api','contents','genindex')
],
# library for 1.5 version:
'1.5':
[
Book('tut','Tutorial','tut','node1'),
Book('lib','Library Reference','lib','node1','node268'),
Book('ref','Language Reference','ref-1','ref-2','ref-11'),
Book('ext','Extending and Embedding','ext','node1'),
Book('api','Python/C API','api','node1','node48')
]
}
# AlmostNullWriter doesn't print anything; it just arranges to save the
# text sent to send_flowing_data(). This is used to capture the text
# between an anchor begin/end pair, e.g. for TOC entries.
class AlmostNullWriter(NullWriter):
def __init__(self):
NullWriter.__init__(self)
self.saved_clear()
def send_flowing_data(self, data):
stripped = data.strip()
if stripped: # don't bother to save runs of whitespace
self.saved.append(stripped)
# Forget all saved text.
def saved_clear(self):
self.saved = []
# Return all saved text as a string.
def saved_get(self):
return ' '.join(self.saved)
class HelpHtmlParser(HTMLParser):
def __init__(self, formatter, path, output):
HTMLParser.__init__(self, formatter)
self.path = path # relative path
self.ft = output # output file
self.indent = 0 # number of tabs for pretty printing of files
self.proc = False # True when actively processing, else False
# (headers, footers, etc)
# XXX This shouldn't need to be a stack -- anchors shouldn't nest.
# XXX See SF bug <http://www.python.org/sf/546579>.
self.hrefstack = [] # stack of hrefs from anchor begins
def begin_group(self):
self.indent += 1
self.proc = True
def finish_group(self):
self.indent -= 1
# stop processing when back to top level
self.proc = self.indent > 0
def anchor_bgn(self, href, name, type):
if self.proc:
# XXX See SF bug <http://www.python.org/sf/546579>.
# XXX index.html for the 2.2.1 language reference manual contains
# XXX nested <a></a> tags in the entry for the section on blank
# XXX lines. We want to ignore the nested part completely.
if len(self.hrefstack) == 0:
self.saved_clear()
self.hrefstack.append(href)
def anchor_end(self):
if self.proc:
# XXX See XXX above.
if self.hrefstack:
title = cgi.escape(self.saved_get(), True)
path = self.path + '/' + self.hrefstack.pop()
self.tab(object_sitemap % (title, path))
def start_dl(self, atr_val):
self.begin_group()
def end_dl(self):
self.finish_group()
def do_dt(self, atr_val):
# no trailing newline on purpose!
self.tab("<LI>")
# Write text to output file.
def write(self, text):
self.ft.write(text)
# Write text to output file after indenting by self.indent tabs.
def tab(self, text=''):
self.write('\t' * self.indent)
if text:
self.write(text)
# Forget all saved text.
def saved_clear(self):
self.formatter.writer.saved_clear()
# Return all saved text as a string.
def saved_get(self):
return self.formatter.writer.saved_get()
class IdxHlpHtmlParser(HelpHtmlParser):
# nothing special here, seems enough with parent class
pass
class TocHlpHtmlParser(HelpHtmlParser):
def start_dl(self, atr_val):
self.begin_group()
self.tab('<UL>\n')
def end_dl(self):
self.finish_group()
self.tab('</UL>\n')
def start_ul(self, atr_val):
self.begin_group()
self.tab('<UL>\n')
def end_ul(self):
self.finish_group()
self.tab('</UL>\n')
def do_li(self, atr_val):
# no trailing newline on purpose!
self.tab("<LI>")
def index(path, indexpage, output):
parser = IdxHlpHtmlParser(AbstractFormatter(AlmostNullWriter()),
path, output)
f = open(path + '/' + indexpage)
parser.feed(f.read())
parser.close()
f.close()
def content(path, contentpage, output):
parser = TocHlpHtmlParser(AbstractFormatter(AlmostNullWriter()),
path, output)
f = open(path + '/' + contentpage)
parser.feed(f.read())
parser.close()
f.close()
def do_index(library, output):
output.write('<UL>\n')
for book in library:
print '\t', book.title, '-', book.indexpage
if book.indexpage:
index(book.directory, book.indexpage, output)
output.write('</UL>\n')
def do_content(library, version, output):
output.write(contents_header)
for book in library:
print '\t', book.title, '-', book.firstpage
path = book.directory + "/" + book.firstpage
output.write('<LI>')
output.write(object_sitemap % (book.title, path))
if book.contentpage:
content(book.directory, book.contentpage, output)
output.write(contents_footer)
# Fill in the [FILES] section of the project (.hhp) file.
# 'library' is the list of directory description tuples from
# supported_libraries for the version of the docs getting generated.
def do_project(library, output, arch, version):
output.write(project_template % locals())
pathseen = {}
for book in library:
directory = book.directory
path = directory + '\\%s\n'
for page in os.listdir(directory):
if page.endswith('.html') or page.endswith('.css'):
fullpath = path % page
if fullpath not in pathseen:
output.write(fullpath)
pathseen[fullpath] = True
def openfile(file):
try:
p = open(file, "w")
except IOError, msg:
print file, ":", msg
sys.exit(1)
return p
def usage():
print usage_mode
sys.exit(0)
def do_it(args = None):
if not args:
args = sys.argv[1:]
if not args:
usage()
try:
optlist, args = getopt.getopt(args, 'ckpv:')
except getopt.error, msg:
print msg
usage()
if not args or len(args) > 1:
usage()
arch = args[0]
version = None
for opt in optlist:
if opt[0] == '-v':
version = opt[1]
break
if not version:
usage()
library = supported_libraries[version]
if not (('-p','') in optlist):
fname = arch + '.stp'
f = openfile(fname)
print "Building stoplist", fname, "..."
words = stop_list.split()
words.sort()
for word in words:
print >> f, word
f.close()
f = openfile(arch + '.hhp')
print "Building Project..."
do_project(library, f, arch, version)
if version == '2.0.0':
for image in os.listdir('icons'):
f.write('icons'+ '\\' + image + '\n')
f.close()
if not (('-c','') in optlist):
f = openfile(arch + '.hhc')
print "Building Table of Content..."
do_content(library, version, f)
f.close()
if not (('-k','') in optlist):
f = openfile(arch + '.hhk')
print "Building Index..."
do_index(library, f)
f.close()
if __name__ == '__main__':
do_it()

90
doc/tools/push-docs.sh Executable file
View File

@ -0,0 +1,90 @@
#! /bin/sh
# Script to push docs from my development area to SourceForge, where the
# update-docs.sh script unpacks them into their final destination.
TARGETHOST=www.python.org
TARGETDIR=/usr/home/fdrake/tmp
TARGET="$TARGETHOST:$TARGETDIR"
ADDRESSES='python-dev@python.org doc-sig@python.org python-list@python.org'
VERSION=`echo '$Revision$' | sed 's/[$]Revision: \(.*\) [$]/\1/'`
EXTRA=`echo "$VERSION" | sed 's/^[0-9][0-9]*\.[0-9][0-9]*//'`
if [ "$EXTRA" ] ; then
DOCLABEL="maintenance"
DOCTYPE="maint"
else
DOCLABEL="development"
DOCTYPE="devel"
fi
EXPLANATION=''
ANNOUNCE=true
while [ "$#" -gt 0 ] ; do
case "$1" in
-m)
EXPLANATION="$2"
shift 2
;;
-q)
ANNOUNCE=false
shift 1
;;
-t)
DOCTYPE="$2"
shift 2
;;
-F)
EXPLANATION="`cat $2`"
shift 2
;;
-*)
echo "Unknown option: $1" >&2
exit 2
;;
*)
break
;;
esac
done
if [ "$1" ] ; then
if [ "$EXPLANATION" ] ; then
echo "Explanation may only be given once!" >&2
exit 2
fi
EXPLANATION="$1"
shift
fi
START="`pwd`"
MYDIR="`dirname $0`"
cd "$MYDIR"
MYDIR="`pwd`"
cd ..
# now in .../Doc/
make --no-print-directory bziphtml || exit $?
RELEASE=`grep '^RELEASE=' Makefile | sed 's|RELEASE=||'`
PACKAGE="html-$RELEASE.tar.bz2"
scp "$PACKAGE" tools/update-docs.sh $TARGET/ || exit $?
ssh "$TARGETHOST" tmp/update-docs.sh $DOCTYPE $PACKAGE '&&' rm tmp/update-docs.sh || exit $?
if $ANNOUNCE ; then
sendmail $ADDRESSES <<EOF
To: $ADDRESSES
From: "Fred L. Drake" <fdrake@acm.org>
Subject: [$DOCLABEL doc updates]
X-No-Archive: yes
The $DOCLABEL version of the documentation has been updated:
http://$TARGETHOST/dev/doc/$DOCTYPE/
$EXPLANATION
EOF
exit $?
fi

920
doc/tools/py2texi.el Normal file
View File

@ -0,0 +1,920 @@
;;; py2texi.el -- Conversion of Python LaTeX documentation to Texinfo
;; Copyright (C) 1998, 1999, 2001, 2002 Milan Zamazal
;; Author: Milan Zamazal <pdm@zamazal.org>
;; Version: $Id$
;; Keywords: python
;; COPYRIGHT NOTICE
;;
;; This program is free software; you can redistribute it and/or modify it
;; under the terms of the GNU General Public License as published by the Free
;; Software Foundation; either version 2, or (at your option) any later
;; version.
;;
;; This program is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
;; for more details.
;;
;; You can find the GNU General Public License at
;; http://www.gnu.org/copyleft/gpl.html
;; or you can write to the Free Software Foundation, Inc., 59 Temple Place,
;; Suite 330, Boston, MA 02111-1307, USA.
;;; Commentary:
;; This is a Q&D hack for conversion of Python manuals to on-line help format.
;; I desperately needed usable online documenta for Python, so I wrote this.
;; The result code is ugly and need not contain complete information from
;; Python manuals. I apologize for my ignorance, especially ignorance to
;; python.sty. Improvements of this convertor are welcomed.
;; How to use it:
;; Load this file and apply `M-x py2texi'. You will be asked for name of a
;; file to be converted.
;; Where to find it:
;; New versions of this code might be found at
;; http://www.zamazal.org/software/python/py2texi/ .
;;; Code:
(require 'texinfo)
(eval-when-compile
(require 'cl))
(defvar py2texi-python-version "2.2"
"What to substitute for the \\version macro.")
(defvar py2texi-python-short-version
(progn
(string-match "[0-9]+\\.[0-9]+" py2texi-python-version)
(match-string 0 py2texi-python-version))
"Short version number, usually set by the LaTeX commands.")
(defvar py2texi-texi-file-name nil
"If non-nil, that string is used as the name of the Texinfo file.
Otherwise a generated Texinfo file name is used.")
(defvar py2texi-info-file-name nil
"If non-nil, that string is used as the name of the Info file.
Otherwise a generated Info file name is used.")
(defvar py2texi-stop-on-problems nil
"*If non-nil, stop when you encouter soft problem.")
(defconst py2texi-environments
'(("abstract" 0 "@quotation" "@end quotation\n")
("center" 0 "" "")
("cfuncdesc" 3
(progn (setq findex t)
"\n@table @code\n@item \\1 \\2(\\3)\n@findex \\2\n")
"@end table\n")
("cmemberdesc" 3
"\n@table @code\n@item \\2 \\3\n"
"@end table\n")
("classdesc" 2
(progn (setq obindex t)
"\n@table @code\n@item \\1(\\2)\n@obindex \\1\n")
"@end table\n")
("classdesc*" 1
(progn (setq obindex t)
"\n@table @code\n@item \\1\n@obindex \\1\n")
"@end table\n")
("csimplemacrodesc" 1
(progn (setq cindex t)
"\n@table @code\n@item \\1\n@cindex \\1\n")
"@end table\n")
("ctypedesc" 1
(progn (setq cindex t)
"\n@table @code\n@item \\1\n@cindex \\1\n")
"@end table\n")
("cvardesc" 2
(progn (setq findex t)
"\n@table @code\n@item \\1 \\2\n@findex \\2\n")
"@end table\n")
("datadesc" 1
(progn (setq findex t)
"\n@table @code\n@item \\1\n@findex \\1\n")
"@end table\n")
("datadescni" 1 "\n@table @code\n@item \\1\n" "@end table\n")
("definitions" 0 "@table @dfn" "@end table\n")
("description" 0 "@table @samp" "@end table\n")
("displaymath" 0 "" "")
("document" 0
(concat "@defcodeindex mo\n"
"@defcodeindex ob\n"
"@titlepage\n"
(format "@title " title "\n")
(format "@author " author "\n")
"@page\n"
author-address
"@end titlepage\n"
"@node Top, , , (dir)\n")
(concat "@indices\n"
"@contents\n"
"@bye\n"))
("enumerate" 0 "@enumerate" "@end enumerate")
("excdesc" 1
(progn (setq obindex t)
"\n@table @code\n@item \\1\n@obindex \\1\n")
"@end table\n")
("excclassdesc" 2
(progn (setq obindex t)
"\n@table @code\n@item \\1(\\2)\n@obindex \\1\n")
"@end table\n")
("flushleft" 0 "" "")
("fulllineitems" 0 "\n@table @code\n" "@end table\n")
("funcdesc" 2
(progn (setq findex t)
"\n@table @code\n@item \\1(\\2)\n@findex \\1\n")
"@end table\n")
("funcdescni" 2 "\n@table @code\n@item \\1(\\2)\n" "@end table\n")
("itemize" 0 "@itemize @bullet" "@end itemize\n")
("list" 2 "\n@table @code\n" "@end table\n")
("longtableii" 4 (concat "@multitable @columnfractions .5 .5\n"
"@item \\3 @tab \\4\n"
"@item ------- @tab ------ \n")
"@end multitable\n")
("longtableiii" 5 (concat "@multitable @columnfractions .33 .33 .33\n"
"@item \\3 @tab \\4 @tab \\5\n"
"@item ------- @tab ------ @tab ------\n")
"@end multitable\n")
("memberdesc" 1
(progn (setq findex t)
"\n@table @code\n@item \\1\n@findex \\1\n")
"@end table\n")
("memberdescni" 1 "\n@table @code\n@item \\1\n" "@end table\n")
("methoddesc" 2
(progn (setq findex t)
"\n@table @code\n@item \\1(\\2)\n@findex \\1\n")
"@end table\n")
("methoddescni" 2 "\n@table @code\n@item \\1(\\2)\n" "@end table\n")
("notice" 0 "@emph{Notice:} " "")
("opcodedesc" 2
(progn (setq findex t)
"\n@table @code\n@item \\1 \\2\n@findex \\1\n")
"@end table\n")
("productionlist" 0 "\n@table @code\n" "@end table\n")
("quotation" 0 "@quotation" "@end quotation")
("seealso" 0 "See also:\n@table @emph\n" "@end table\n")
("seealso*" 0 "@table @emph\n" "@end table\n")
("sloppypar" 0 "" "")
("small" 0 "" "")
("tableii" 4 (concat "@multitable @columnfractions .5 .5\n"
"@item \\3 @tab \\4\n"
"@item ------- @tab ------ \n")
"@end multitable\n")
("tableiii" 5 (concat "@multitable @columnfractions .33 .33 .33\n"
"@item \\3 @tab \\4 @tab \\5\n"
"@item ------- @tab ------ @tab ------\n")
"@end multitable\n")
("tableiv" 6 (concat
"@multitable @columnfractions .25 .25 .25 .25\n"
"@item \\3 @tab \\4 @tab \\5 @tab \\6\n"
"@item ------- @tab ------- @tab ------- @tab -------\n")
"@end multitable\n")
("tablev" 7 (concat
"@multitable @columnfractions .20 .20 .20 .20 .20\n"
"@item \\3 @tab \\4 @tab \\5 @tab \\6 @tab \\7\n"
"@item ------- @tab ------- @tab ------- @tab ------- @tab -------\n")
"@end multitable\n"))
"Associative list defining substitutions for environments.
Each list item is of the form (ENVIRONMENT ARGNUM BEGIN END) where:
- ENVIRONMENT is LaTeX environment name
- ARGNUM is number of (required) macro arguments
- BEGIN is substitution for \begin{ENVIRONMENT}
- END is substitution for \end{ENVIRONMENT}
Both BEGIN and END are evaled. Moreover, you can reference arguments through
\N regular expression notation in strings of BEGIN.")
(defconst py2texi-commands
'(("ABC" 0 "ABC")
("appendix" 0 (progn (setq appendix t) ""))
("ASCII" 0 "ASCII")
("author" 1 (progn (setq author (match-string 1 string)) ""))
("authoraddress" 1
(progn (setq author-address (match-string 1 string)) ""))
("b" 1 "@w{\\1}")
("bf" 0 "@destroy")
("bifuncindex" 1 (progn (setq findex t) "@findex{\\1}"))
("C" 0 "C")
("c" 0 "@,")
("catcode" 0 "")
("cdata" 1 "@code{\\1}")
("centerline" 1 "@center \\1")
("cfuncline" 3 "@itemx \\1 \\2(\\3)\n@findex \\2")
("cfunction" 1 "@code{\\1}")
("chapter" 1 (format "@node \\1\n@%s \\1\n"
(if appendix "appendix" "chapter")))
("chapter*" 1 "@node \\1\n@unnumbered \\1\n")
("character" 1 "@samp{\\1}")
("citetitle" 1 "@ref{Top,,,\\1}")
("class" 1 "@code{\\1}")
("cmemberline" 3 "@itemx \\2 \\3\n")
("code" 1 "@code{\\1}")
("command" 1 "@command{\\1}")
("constant" 1 "@code{\\1}")
("copyright" 1 "@copyright{}")
("Cpp" 0 "C++")
("csimplemacro" 1 "@code{\\1}")
("ctype" 1 "@code{\\1}")
("dataline" 1 (progn (setq findex t) "@item \\1\n@findex \\1\n"))
("date" 1 "\\1")
("declaremodule" 2 (progn (setq cindex t) "@label{\\2}@cindex{\\2}"))
("deprecated" 2 "@emph{This is deprecated in Python \\1. \\2}")
("dfn" 1 "@dfn{\\1}")
("documentclass" 1 py2texi-magic)
("e" 0 "@backslash{}")
("else" 0 (concat "@end ifinfo\n@" (setq last-if "iftex")))
("EOF" 0 "@code{EOF}")
("email" 1 "@email{\\1}")
("emph" 1 "@emph{\\1}")
("envvar" 1 "@samp{\\1}")
("exception" 1 "@code{\\1}")
("exindex" 1 (progn (setq obindex t) "@obindex{\\1}"))
("fi" 0 (concat "@end " last-if))
("file" 1 "@file{\\1}")
("filevar" 1 "@file{@var{\\1}}")
("footnote" 1 "@footnote{\\1}")
("frac" 0 "")
("funcline" 2 (progn (setq findex t) "@item \\1 \\2\n@findex \\1"))
("funclineni" 2 "@item \\1 \\2")
("function" 1 "@code{\\1}")
("grammartoken" 1 "@code{\\1}")
("hline" 0 "")
("ifhtml" 0 (concat "@" (setq last-if "ifinfo")))
("iftexi" 0 (concat "@" (setq last-if "ifinfo")))
("index" 1 (progn (setq cindex t) "@cindex{\\1}"))
("indexii" 2 (progn (setq cindex t) "@cindex{\\1 \\2}"))
("indexiii" 3 (progn (setq cindex t) "@cindex{\\1 \\2 \\3}"))
("indexiv" 3 (progn (setq cindex t) "@cindex{\\1 \\2 \\3 \\4}"))
("infinity" 0 "@emph{infinity}")
("it" 0 "@destroy")
("kbd" 1 "@key{\\1}")
("keyword" 1 "@code{\\1}")
("kwindex" 1 (progn (setq cindex t) "@cindex{\\1}"))
("label" 1 "@label{\\1}")
("Large" 0 "")
("LaTeX" 0 "La@TeX{}")
("large" 0 "")
("ldots" 0 "@dots{}")
("leftline" 1 "\\1")
("lineii" 2 "@item \\1 @tab \\2")
("lineiii" 3 "@item \\1 @tab \\2 @tab \\3")
("lineiv" 4 "@item \\1 @tab \\2 @tab \\3 @tab \\4")
("linev" 5 "@item \\1 @tab \\2 @tab \\3 @tab \\4 @tab \\5")
("localmoduletable" 0 "")
("longprogramopt" 1 "@option{--\\1}")
("mailheader" 1 "@code{\\1}")
("makeindex" 0 "")
("makemodindex" 0 "")
("maketitle" 0 (concat "@top " title "\n"))
("makevar" 1 "@code{\\1}")
("manpage" 2 "@samp{\\1(\\2)}")
("mbox" 1 "@w{\\1}")
("member" 1 "@code{\\1}")
("memberline" 1 "@item \\1\n@findex \\1\n")
("menuselection" 1 "@samp{\\1}")
("method" 1 "@code{\\1}")
("methodline" 2 (progn (setq moindex t) "@item \\1(\\2)\n@moindex \\1\n"))
("methodlineni" 2 "@item \\1(\\2)\n")
("mimetype" 1 "@samp{\\1}")
("module" 1 "@samp{\\1}")
("moduleauthor" 2 "")
("modulesynopsis" 1 "\\1")
("moreargs" 0 "@dots{}")
("n" 0 "@backslash{}n")
("newcommand" 2 "")
("newsgroup" 1 "@samp{\\1}")
("nodename" 1
(save-excursion
(save-match-data
(re-search-backward "^@node "))
(delete-region (point) (save-excursion (end-of-line) (point)))
(insert "@node " (match-string 1 string))
""))
("noindent" 0 "@noindent ")
("note" 1 "@emph{Note:} \\1")
("NULL" 0 "@code{NULL}")
("obindex" 1 (progn (setq obindex t) "@obindex{\\1}"))
("opindex" 1 (progn (setq cindex t) "@cindex{\\1}"))
("option" 1 "@option{\\1}")
("optional" 1 "[\\1]")
("pep" 1 (progn (setq cindex t) "PEP@ \\1@cindex PEP \\1\n"))
("pi" 0 "pi")
("platform" 1 "")
("plusminus" 0 "+-")
("POSIX" 0 "POSIX")
("production" 2 "@item \\1 \\2")
("productioncont" 1 "@item @w{} \\1")
("program" 1 "@command{\\1}")
("programopt" 1 "@option{\\1}")
("protect" 0 "")
("pytype" 1 "@code{\\1}")
("ref" 1 "@ref{\\1}")
("refbimodindex" 1 (progn (setq moindex t) "@moindex{\\1}"))
("refmodindex" 1 (progn (setq moindex t) "@moindex{\\1}"))
("refmodule" 1 "@samp{\\1}")
("refstmodindex" 1 (progn (setq moindex t) "@moindex{\\1}"))
("regexp" 1 "\"\\1\"")
("release" 1
(progn (setq py2texi-python-version (match-string 1 string)) ""))
("renewcommand" 2 "")
("rfc" 1 (progn (setq cindex t) "RFC@ \\1@cindex RFC \\1\n"))
("rm" 0 "@destroy")
("samp" 1 "@samp{\\1}")
("section" 1 (let ((str (match-string 1 string)))
(save-match-data
(if (string-match "\\(.*\\)[ \t\n]*---[ \t\n]*\\(.*\\)"
str)
(format
"@node %s\n@section %s\n"
(py2texi-backslash-quote (match-string 1 str))
(py2texi-backslash-quote (match-string 2 str)))
"@node \\1\n@section \\1\n"))))
("sectionauthor" 2 "")
("seemodule" 2 "@ref{\\1} \\2")
("seepep" 3 "\n@table @strong\n@item PEP\\1 \\2\n\\3\n@end table\n")
("seerfc" 3 "\n@table @strong\n@item RFC\\1 \\2\n\\3\n@end table\n")
("seetext" 1 "\\1")
("seetitle" 1 "@cite{\\1}")
("seeurl" 2 "\n@table @url\n@item \\1\n\\2\n@end table\n")
("setindexsubitem" 1 (progn (setq cindex t) "@cindex \\1"))
("setreleaseinfo" 1 (progn (setq py2texi-releaseinfo "")))
("setshortversion" 1
(progn (setq py2texi-python-short-version (match-string 1 string)) ""))
("shortversion" 0 py2texi-python-short-version)
("sqrt" 0 "")
("stindex" 1 (progn (setq cindex t) "@cindex{\\1}"))
("stmodindex" 1 (progn (setq moindex t) "@moindex{\\1}"))
("strong" 1 "@strong{\\1}")
("sub" 0 "/")
("subsection" 1 "@node \\1\n@subsection \\1\n")
("subsubsection" 1 "@node \\1\n@subsubsection \\1\n")
("sum" 0 "")
("tableofcontents" 0 "")
("term" 1 "@item \\1")
("textasciitilde" 0 "~")
("textasciicircum" 0 "^")
("textbackslash" 0 "@backslash{}")
("textgreater" 0 ">")
("textless" 0 "<")
("textrm" 1 "\\1")
("texttt" 1 "@code{\\1}")
("textunderscore" 0 "_")
("title" 1 (progn (setq title (match-string 1 string)) "@settitle \\1"))
("today" 0 "@today{}")
("token" 1 "@code{\\1}")
("tt" 0 "@destroy")
("ttindex" 1 (progn (setq cindex t) "@cindex{\\1}"))
("u" 0 "@backslash{}u")
("ulink" 2 "\\1")
("UNIX" 0 "UNIX")
("unspecified" 0 "@dots{}")
("url" 1 "@url{\\1}")
("usepackage" 1 "")
("var" 1 "@var{\\1}")
("verbatiminput" 1 "@code{\\1}")
("version" 0 py2texi-python-version)
("versionadded" 1 "@emph{Added in Python version \\1}")
("versionchanged" 1 "@emph{Changed in Python version \\1}")
("vskip" 1 "")
("vspace" 1 "")
("warning" 1 "@emph{\\1}")
("withsubitem" 2 "\\2")
("XXX" 1 "@strong{\\1}"))
"Associative list of command substitutions.
Each list item is of the form (COMMAND ARGNUM SUBSTITUTION) where:
- COMMAND is LaTeX command name
- ARGNUM is number of (required) command arguments
- SUBSTITUTION substitution for the command. It is evaled and you can
reference command arguments through the \\N regexp notation in strings.")
(defvar py2texi-magic "@documentclass\n"
"\"Magic\" string for auxiliary insertion at the beginning of document.")
(defvar py2texi-dirs '("./" "../texinputs/")
"Where to search LaTeX input files.")
(defvar py2texi-buffer "*py2texi*"
"The name of a buffer where Texinfo is generated.")
(defconst py2texi-xemacs (string-match "^XEmacs" (emacs-version))
"Running under XEmacs?")
(defmacro py2texi-search (regexp &rest body)
`(progn
(goto-char (point-min))
(while (re-search-forward ,regexp nil t)
,@body)))
(defmacro py2texi-search-safe (regexp &rest body)
`(py2texi-search ,regexp
(unless (py2texi-protected)
,@body)))
(defun py2texi-message (message)
"Report message and stop if `py2texi-stop-on-problems' is non-nil."
(if py2texi-stop-on-problems
(error message)
(message message)))
(defun py2texi-backslash-quote (string)
"Double backslahes in STRING."
(let ((i 0))
(save-match-data
(while (setq i (string-match "\\\\" string i))
(setq string (replace-match "\\\\\\\\" t nil string))
(setq i (+ i 2))))
string))
(defun py2texi (file)
"Convert Python LaTeX documentation FILE to Texinfo."
(interactive "fFile to convert: ")
(switch-to-buffer (get-buffer-create py2texi-buffer))
(erase-buffer)
(insert-file file)
(let ((case-fold-search nil)
(title "")
(author "")
(author-address "")
(appendix nil)
(findex nil)
(obindex nil)
(cindex nil)
(moindex nil)
last-if)
(py2texi-process-verbatims)
(py2texi-process-comments)
(py2texi-process-includes)
(py2texi-process-funnyas)
(py2texi-process-environments)
(py2texi-process-commands)
(py2texi-fix-indentation)
(py2texi-fix-nodes)
(py2texi-fix-references)
(py2texi-fix-indices)
(py2texi-process-simple-commands)
(py2texi-fix-fonts)
(py2texi-fix-braces)
(py2texi-fix-backslashes)
(py2texi-destroy-empties)
(py2texi-fix-newlines)
(py2texi-adjust-level))
(let* ((texi-file-name (or py2texi-texi-file-name
(py2texi-texi-file-name file)))
(info-file-name (or py2texi-info-file-name
(py2texi-info-file-name texi-file-name))))
(goto-char (point-min))
(when (looking-at py2texi-magic)
(delete-region (point) (progn (beginning-of-line 2) (point)))
(insert "\\input texinfo @c -*-texinfo-*-\n")
(insert "@setfilename " info-file-name))
(when (re-search-forward "@chapter" nil t)
(texinfo-all-menus-update t))
(goto-char (point-min))
(write-file texi-file-name)
(message (format "You can apply `makeinfo %s' now." texi-file-name))))
(defun py2texi-texi-file-name (filename)
"Generate name of Texinfo file from original file name FILENAME."
(concat filename
(if (string-match "\\.tex$" filename) "i" ".texi")))
(defun py2texi-info-file-name (filename)
"Generate name of info file from original file name FILENAME."
(setq filename (expand-file-name filename))
(let ((directory (file-name-directory filename))
(basename (file-name-nondirectory filename)))
(concat directory "python-"
(substring basename 0 (- (length basename) 4)) "info")))
(defun py2texi-process-verbatims ()
"Process and protect verbatim environments."
(let (delimiter
beg
end)
(py2texi-search-safe "\\\\begin{\\(verbatim\\|displaymath\\)}"
(replace-match "@example")
(setq beg (copy-marker (point) nil))
(re-search-forward "\\\\end{\\(verbatim\\|displaymath\\)}")
(setq end (copy-marker (match-beginning 0) nil))
(replace-match "@end example")
(py2texi-texinfo-escape beg end)
(put-text-property (- beg (length "@example"))
(+ end (length "@end example"))
'py2texi-protected t))
(py2texi-search-safe "\\\\verb\\([^a-z]\\)"
(setq delimiter (match-string 1))
(replace-match "@code{")
(setq beg (copy-marker (point) nil))
(re-search-forward (regexp-quote delimiter))
(setq end (copy-marker (match-beginning 0) nil))
(replace-match "}")
(put-text-property (- beg (length "@code{")) (+ end (length "}"))
'py2texi-protected t)
(py2texi-texinfo-escape beg end))))
(defun py2texi-process-comments ()
"Remove comments."
(let (point)
(py2texi-search-safe "%"
(setq point (point))
(when (save-excursion
(re-search-backward "\\(^\\|[^\\]\\(\\\\\\\\\\)*\\)%\\=" nil t))
(delete-region (1- point)
(save-excursion (beginning-of-line 2) (point)))))))
(defun py2texi-process-includes ()
"Include LaTeX input files.
Do not include .ind files."
(let ((path (file-name-directory file))
filename
dirs
includefile)
(py2texi-search-safe "\\\\input{\\([^}]+\\)}"
(setq filename (match-string 1))
(unless (save-match-data (string-match "\\.tex$" filename))
(setq filename (concat filename ".tex")))
(setq includefile (save-match-data
(string-match "\\.ind\\.tex$" filename)))
(setq dirs py2texi-dirs)
(while (and (not includefile) dirs)
(setq includefile (concat path (car dirs) filename))
(unless (file-exists-p includefile)
(setq includefile nil)
(setq dirs (cdr dirs))))
(if includefile
(save-restriction
(narrow-to-region (match-beginning 0) (match-end 0))
(delete-region (point-min) (point-max))
(when (stringp includefile)
(insert-file-contents includefile)
(goto-char (point-min))
(insert "\n")
(py2texi-process-verbatims)
(py2texi-process-comments)
(py2texi-process-includes)))
(replace-match (format "\\\\emph{Included file %s}" filename))
(py2texi-message (format "Input file %s not found" filename))))))
(defun py2texi-process-funnyas ()
"Convert @s."
(py2texi-search-safe "@"
(replace-match "@@")))
(defun py2texi-process-environments ()
"Process LaTeX environments."
(let ((stack ())
kind
environment
parameter
arguments
n
string
description)
(py2texi-search-safe (concat "\\\\\\(begin\\|end\\|item\\)"
"\\({\\([^}]*\\)}\\|[[]\\([^]]*\\)[]]\\|\\)")
(setq kind (match-string 1)
environment (match-string 3)
parameter (match-string 4))
(replace-match "")
(cond
((string= kind "begin")
(setq description (assoc environment py2texi-environments))
(if description
(progn
(setq n (cadr description))
(setq description (cddr description))
(setq string (py2texi-tex-arguments n))
(string-match (py2texi-regexp n) string)
; incorrect but sufficient
(insert (replace-match (eval (car description))
t nil string))
(setq stack (cons (cadr description) stack)))
(py2texi-message (format "Unknown environment: %s" environment))
(setq stack (cons "" stack))))
((string= kind "end")
(insert (eval (car stack)))
(setq stack (cdr stack)))
((string= kind "item")
(insert "\n@item " (or parameter "") "\n"))))
(when stack
(py2texi-message (format "Unclosed environment: %s" (car stack))))))
(defun py2texi-process-commands ()
"Process LaTeX commands."
(let (done
command
command-info
string
n)
(while (not done)
(setq done t)
(py2texi-search-safe "\\\\\\([a-zA-Z*]+\\)\\(\\[[^]]*\\]\\)?"
(setq command (match-string 1))
(setq command-info (assoc command py2texi-commands))
(if command-info
(progn
(setq done nil)
(replace-match "")
(setq command-info (cdr command-info))
(setq n (car command-info))
(setq string (py2texi-tex-arguments n))
(string-match (py2texi-regexp n) string)
; incorrect but sufficient
(insert (replace-match (eval (cadr command-info))
t nil string)))
(py2texi-message (format "Unknown command: %s (not processed)"
command)))))))
(defun py2texi-argument-pattern (count)
(let ((filler "\\(?:[^{}]\\|\\\\{\\|\\\\}\\)*"))
(if (<= count 0)
filler
(concat filler "\\(?:{"
(py2texi-argument-pattern (1- count))
"}" filler "\\)*" filler))))
(defconst py2texi-tex-argument
(concat
"{\\("
(py2texi-argument-pattern 10) ;really at least 10!
"\\)}[ \t%@c\n]*")
"Regexp describing LaTeX command argument including argument separators.")
(defun py2texi-regexp (n)
"Make regexp matching N LaTeX command arguments."
(if (= n 0)
""
(let ((regexp "^[^{]*"))
(while (> n 0)
(setq regexp (concat regexp py2texi-tex-argument))
(setq n (1- n)))
regexp)))
(defun py2texi-tex-arguments (n)
"Remove N LaTeX command arguments and return them as a string."
(let ((point (point))
(i 0)
result
match)
(if (= n 0)
(progn
(when (re-search-forward "\\=\\({}\\| *\\)" nil t)
(replace-match ""))
"")
(while (> n 0)
(unless (re-search-forward
"\\(\\=\\|[^\\\\]\\)\\(\\\\\\\\\\)*\\([{}]\\)" nil t)
(debug))
(if (string= (match-string 3) "{")
(setq i (1+ i))
(setq i (1- i))
(when (<= i 0)
(setq n (1- n)))))
(setq result (buffer-substring-no-properties point (point)))
(while (string-match "\n[ \t]*" result)
(setq result (replace-match " " t nil result)))
(delete-region point (point))
result)))
(defun py2texi-process-simple-commands ()
"Replace single character LaTeX commands."
(let (char)
(py2texi-search-safe "\\\\\\([^a-z]\\)"
(setq char (match-string 1))
(replace-match (format "%s%s"
(if (or (string= char "{")
(string= char "}")
(string= char " "))
"@"
"")
(if (string= char "\\")
"\\\\"
char))))))
(defun py2texi-fix-indentation ()
"Remove white space at the beginning of lines."
(py2texi-search-safe "^[ \t]+"
(replace-match "")))
(defun py2texi-fix-nodes ()
"Remove unwanted characters from nodes and make nodes unique."
(let ((nodes (make-hash-table :test 'equal))
id
counter
string
label
index)
(py2texi-search "^@node +\\(.*\\)$"
(setq string (match-string 1))
(if py2texi-xemacs
(replace-match "@node " t)
(replace-match "" t nil nil 1))
(while (string-match "@label{[^}]*}" string)
(setq label (match-string 0 string))
(setq string (replace-match "" t nil string)))
(while (string-match "@..?index{[^}]*}" string)
(setq index (match-string 0 string))
(setq string (replace-match "" t nil string)))
(while (string-match "@[a-zA-Z]+\\|[{}():]\\|``\\|''" string)
(setq string (replace-match "" t nil string)))
(while (string-match " -- " string)
(setq string (replace-match " - " t nil string)))
(while (string-match "\\." string)
(setq string (replace-match "" t nil string)))
(when (string-match " +$" string)
(setq string (replace-match "" t nil string)))
(when (string-match "^\\(Built-in\\|Standard\\) Module \\|The " string)
(setq string (replace-match "" t nil string)))
(string-match "^[^,]+" string)
(setq id (match-string 0 string))
(setq counter (gethash id nodes))
(if counter
(progn
(setq counter (1+ counter))
(setq string (replace-match (format "\\& %d" counter)
t nil string)))
(setq counter 1))
(setf (gethash id nodes) counter)
(insert string)
(beginning-of-line 3)
(when label
(insert label "\n"))
(when index
(insert index "\n")))))
(defun py2texi-fix-references ()
"Process labels and make references to point to appropriate nodes."
(let ((labels ())
node)
(py2texi-search-safe "@label{\\([^}]*\\)}"
(setq node (save-excursion
(save-match-data
(and (re-search-backward "@node +\\([^,\n]+\\)" nil t)
(match-string 1)))))
(when node
(setq labels (cons (cons (match-string 1) node) labels)))
(replace-match ""))
(py2texi-search-safe "@ref{\\([^}]*\\)}"
(setq node (assoc (match-string 1) labels))
(replace-match "")
(when node
(insert (format "@ref{%s}" (cdr node)))))))
(defun py2texi-fix-indices ()
"Remove unwanted characters from @*index commands and create final indices."
(py2texi-search-safe "@..?index\\>[^\n]*\\(\\)\n"
(replace-match "" t nil nil 1))
(py2texi-search-safe "@..?index\\>[^\n]*\\(\\)"
(replace-match "\n" t nil nil 1))
(py2texi-search-safe "@..?index\\({\\)\\([^}]+\\)\\(}+\\)"
(replace-match " " t nil nil 1)
(replace-match "" t nil nil 3)
(let ((string (match-string 2)))
(save-match-data
(while (string-match "@[a-z]+{" string)
(setq string (replace-match "" nil nil string)))
(while (string-match "{" string)
(setq string (replace-match "" nil nil string))))
(replace-match string t t nil 2)))
(py2texi-search-safe "@..?index\\>.*\\([{}]\\|@[a-z]*\\)"
(replace-match "" t nil nil 1)
(goto-char (match-beginning 0)))
(py2texi-search-safe "[^\n]\\(\\)@..?index\\>"
(replace-match "\n" t nil nil 1))
(goto-char (point-max))
(re-search-backward "@indices")
(replace-match "")
(insert (if moindex
(concat "@node Module Index\n"
"@unnumbered Module Index\n"
"@printindex mo\n")
"")
(if obindex
(concat "@node Class-Exception-Object Index\n"
"@unnumbered Class, Exception, and Object Index\n"
"@printindex ob\n")
"")
(if findex
(concat "@node Function-Method-Variable Index\n"
"@unnumbered Function, Method, and Variable Index\n"
"@printindex fn\n")
"")
(if cindex
(concat "@node Miscellaneous Index\n"
"@unnumbered Miscellaneous Index\n"
"@printindex cp\n")
"")))
(defun py2texi-fix-backslashes ()
"Make backslashes from auxiliary commands."
(py2texi-search-safe "@backslash{}"
(replace-match "\\\\")))
(defun py2texi-fix-fonts ()
"Remove garbage after unstructured font commands."
(let (string)
(py2texi-search-safe "@destroy"
(replace-match "")
(when (eq (preceding-char) ?{)
(forward-char -1)
(setq string (py2texi-tex-arguments 1))
(insert (substring string 1 (1- (length string))))))))
(defun py2texi-fix-braces ()
"Escape braces for Texinfo."
(let (string)
(py2texi-search "{"
(unless (or (py2texi-protected)
(save-excursion
(re-search-backward
"@\\([a-zA-Z]*\\|multitable.*\\){\\=" nil t)))
(forward-char -1)
(setq string (py2texi-tex-arguments 1))
(insert "@" (substring string 0 (1- (length string))) "@}")))))
(defun py2texi-fix-newlines ()
"Remove extra newlines."
(py2texi-search "\n\n\n+"
(replace-match "\n\n"))
(py2texi-search-safe "@item.*\n\n"
(delete-backward-char 1))
(py2texi-search "@end example"
(unless (looking-at "\n\n")
(insert "\n"))))
(defun py2texi-destroy-empties ()
"Remove all comments.
This avoids some makeinfo errors."
(py2texi-search "@c\\>"
(unless (eq (py2texi-protected) t)
(delete-region (- (point) 2) (save-excursion (end-of-line) (point)))
(cond
((looking-at "\n\n")
(delete-char 1))
((save-excursion (re-search-backward "^[ \t]*\\=" nil t))
(delete-region (save-excursion (beginning-of-line) (point))
(1+ (point))))))))
(defun py2texi-adjust-level ()
"Increase heading level to @chapter, if needed.
This is only needed for distutils, so it has a very simple form only."
(goto-char (point-min))
(unless (re-search-forward "@chapter\\>" nil t)
(py2texi-search-safe "@section\\>"
(replace-match "@chapter" t))
(py2texi-search-safe "@\\(sub\\)\\(sub\\)?section\\>"
(replace-match "" nil nil nil 1))))
(defun py2texi-texinfo-escape (beg end)
"Escape Texinfo special characters in region."
(save-excursion
(goto-char beg)
(while (re-search-forward "[@{}]" end t)
(replace-match "@\\&"))))
(defun py2texi-protected ()
"Return protection status of the point before current point."
(get-text-property (1- (point)) 'py2texi-protected))
;;; Announce
(provide 'py2texi)
;;; py2texi.el ends here

98
doc/tools/refcounts.py Normal file
View File

@ -0,0 +1,98 @@
"""Support functions for loading the reference count data file."""
__version__ = '$Revision$'
import os
import sys
# Determine the expected location of the reference count file:
try:
p = os.path.dirname(__file__)
except NameError:
p = os.path.dirname(sys.argv[0])
p = os.path.normpath(os.path.join(os.getcwd(), p, os.pardir,
"api", "refcounts.dat"))
DEFAULT_PATH = p
del p
def load(path=DEFAULT_PATH):
return loadfile(open(path))
def loadfile(fp):
d = {}
while 1:
line = fp.readline()
if not line:
break
line = line.strip()
if line[:1] in ("", "#"):
# blank lines and comments
continue
parts = line.split(":", 4)
if len(parts) != 5:
raise ValueError("Not enough fields in " + `line`)
function, type, arg, refcount, comment = parts
if refcount == "null":
refcount = None
elif refcount:
refcount = int(refcount)
else:
refcount = None
#
# Get the entry, creating it if needed:
#
try:
entry = d[function]
except KeyError:
entry = d[function] = Entry(function)
#
# Update the entry with the new parameter or the result information.
#
if arg:
entry.args.append((arg, type, refcount))
else:
entry.result_type = type
entry.result_refs = refcount
return d
class Entry:
def __init__(self, name):
self.name = name
self.args = []
self.result_type = ''
self.result_refs = None
def dump(d):
"""Dump the data in the 'canonical' format, with functions in
sorted order."""
items = d.items()
items.sort()
first = 1
for k, entry in items:
if first:
first = 0
else:
print
s = entry.name + ":%s:%s:%s:"
if entry.result_refs is None:
r = ""
else:
r = entry.result_refs
print s % (entry.result_type, "", r)
for t, n, r in entry.args:
if r is None:
r = ""
print s % (t, n, r)
def main():
d = load()
dump(d)
if __name__ == "__main__":
main()

54
doc/tools/rewrite.py Normal file
View File

@ -0,0 +1,54 @@
"""Simple script to replace @DATE@ and friends with real information.
Usage: rewrite.py boilerplate.tex [VAR=value] ... <template >output
"""
import sys
import time
def get_info(fp):
s = fp.read()
d = {}
start = s.find(r"\date{")
if start >= 0:
end = s.find("}", start)
date = s[start+6:end]
if date == r"\today":
date = time.strftime("%B %d, %Y", time.localtime(time.time()))
d["DATE"] = date
return d
def main():
s = sys.stdin.read()
if "@" in s:
# yes, we actully need to load the replacement values
d = get_info(open(sys.argv[1]))
for arg in sys.argv[2:]:
name, value = arg.split("=", 1)
d[name] = value
start = 0
while 1:
start = s.find("@", start)
if start < 0:
break
end = s.find("@", start+1)
name = s[start+1:end]
if name:
value = d.get(name)
if value is None:
start = end + 1
else:
s = s[:start] + value + s[end+1:]
start = start + len(value)
else:
# "@@" --> "@"
s = s[:start] + s[end:]
start = end
sys.stdout.write(s)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,67 @@
# Simple makefile to control XML generation for the entire document tree.
# This should be used from the top-level directory (Doc/), not the directory
# that actually contains this file:
#
# $ pwd
# .../Doc
# $ make -f tools/sgmlconv/Makefile
TOPDIR=.
TOOLSDIR=tools
SGMLRULES=../$(TOOLSDIR)/sgmlconv/make.rules
# The 'inst' and 'tut' directories break the conversion, so skip them for now.
SUBDIRS=api dist ext lib mac ref
SUBMAKE=$(MAKE) -f $(SGMLRULES) TOOLSDIR=../$(TOOLSDIR)
all: xml
.PHONY: esis xml
.PHONY: $(SUBDIRS)
xml:
for DIR in $(SUBDIRS) ; do \
(cd $$DIR && $(SUBMAKE) xml) || exit $$? ; done
esis:
for DIR in $(SUBDIRS) ; do \
(cd $$DIR && $(SUBMAKE) esis) || exit $$? ; done
esis1:
for DIR in $(SUBDIRS) ; do \
(cd $$DIR && $(SUBMAKE) esis1) || exit $$? ; done
tarball: xml
tar cf - tools/sgmlconv */*.xml | gzip -9 >xml-1.5.2b2.tgz
api:
cd api && $(SUBMAKE)
dist:
cd dist && $(SUBMAKE)
ext:
cd ext && $(SUBMAKE)
inst:
cd inst && $(SUBMAKE)
lib:
cd lib && $(SUBMAKE)
mac:
cd mac && $(SUBMAKE)
ref:
cd ref && $(SUBMAKE)
tut:
cd tut && $(SUBMAKE)
clean:
for DIR in $(SUBDIRS) ; do \
(cd $$DIR && $(SUBMAKE) clean) || exit $$? ; done
clobber:
for DIR in $(SUBDIRS) ; do \
(cd $$DIR && $(SUBMAKE) clobber) || exit $$? ; done

58
doc/tools/sgmlconv/README Normal file
View File

@ -0,0 +1,58 @@
These scripts and Makefile fragment are used to convert the Python
documentation in LaTeX format to XML.
This material is preliminary and incomplete. Python 2.0 is required.
To convert all documents to XML:
cd Doc/
make -f tools/sgmlconv/Makefile
To convert one document to XML:
cd Doc/<document-dir>
make -f ../tools/sgmlconv/make.rules TOOLSDIR=../tools
Please send comments and bug reports to python-docs@python.org.
What do the tools do?
---------------------
latex2esis.py
Reads in a conversion specification written in XML
(conversion.xml), reads a LaTeX document fragment, and interprets
the markup according to the specification. The output is a stream
of ESIS events like those created by the nsgmls SGML parser, but
is *not* guaranteed to represent a single tree! This is done to
allow conversion per entity rather than per document. Since many
of the LaTeX files for the Python documentation contain two
sections on closely related modules, it is important to allow both
of the resulting <section> elements to exist in the same output
stream. Additionally, since comments are not supported in ESIS,
comments are converted to <COMMENT> elements, which might exist at
the same level as the top-level content elements.
The output of latex2esis.py gets saved as <filename>.esis1.
docfixer.py
This is the really painful part of the conversion. Well, it's the
second really painful part, but more of the pain is specific to
the structure of the Python documentation and desired output
rather than to the parsing of LaTeX markup.
This script loads the ESIS data created by latex2esis.py into a
DOM document *fragment* (remember, the latex2esis.py output may
not be well-formed). Once loaded, it walks over the tree many
times looking for a variety of possible specific
micro-conversions. Most of the code is not in any way "general".
After processing the fragment, a new ESIS data stream is written
out. Like the input, it may not represent a well-formed
document, but does represent a parsed entity.
The output of docfixer.py is what gets saved in <filename>.esis.
esis2sgml.py
Reads an ESIS stream and convert to SGML or XML. This also
converts <COMMENT> elements to real comments. This works quickly
because there's not much to actually do.

View File

@ -0,0 +1,914 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<conversion>
<!-- Miscellaneous. -->
<macro name="declaremodule">
<attribute name="id" optional="yes"/>
<attribute name="type"/>
<attribute name="name"/>
</macro>
<macro name="modulesynopsis">
<content/>
</macro>
<macro name="platform">
<content/>
</macro>
<macro name="deprecated">
<attribute name="version"/>
<content/>
</macro>
<macro name="label">
<attribute name="id"/>
</macro>
<macro name="nodename" outputname="label">
<attribute name="id"/>
</macro>
<macro name="localmoduletable"/>
<macro name="manpage">
<attribute name="name"/>
<attribute name="section"/>
</macro>
<macro name="module">
<content/>
</macro>
<macro name="moduleauthor">
<attribute name="name"/>
<attribute name="email"/>
</macro>
<macro name="citetitle">
<attribute name="href" optional="yes"/>
<content/>
</macro>
<macro name="pep">
<attribute name="num"/>
</macro>
<macro name="rfc">
<attribute name="num"/>
</macro>
<macro name="sectionauthor" outputname="author">
<attribute name="name"/>
<attribute name="email"/>
</macro>
<macro name="author">
<attribute name="name"/>
</macro>
<macro name="authoraddress">
<content/>
</macro>
<macro name="shortversion"/>
<macro name="note">
<content/>
</macro>
<macro name="warning">
<content/>
</macro>
<environment name="notice">
<attribute name="role" optional="yes"/>
</environment>
<macro name="menuselection">
<content/>
</macro>
<macro name="sub"/>
<!-- These are broken: we need to re-order the optional and required
parameters, making the optional parameter the content for the
element. latex2esis.py is not powerful enough to handle this.
-->
<macro name="versionadded">
<attribute name="info" optional="yes"/>
<attribute name="version"/>
</macro>
<macro name="versionchanged">
<attribute name="info" optional="yes"/>
<attribute name="version"/>
</macro>
<!-- Module referencing. -->
<macro name="refmodule" outputname="module">
<!-- this causes the optional parameter to \refmodule to be
discarded -->
<attribute name="" optional="yes"/>
<content/>
</macro>
<!-- Information units. -->
<!-- C things. -->
<environment name="cfuncdesc">
<attribute name="type"/>
<attribute name="name"/>
<child name="args"/>
</environment>
<environment name="csimplemacrodesc">
<attribute name="name"/>
</environment>
<environment name="ctypedesc">
<attribute name="tag" optional="yes"/>
<attribute name="name"/>
</environment>
<environment name="cvardesc">
<attribute name="type"/>
<attribute name="name"/>
</environment>
<!-- Python things. -->
<macro name="optional">
<content/>
</macro>
<macro name="unspecified"/>
<macro name="moreargs"/>
<environment name="classdesc">
<attribute name="name"/>
<child name="args"/>
</environment>
<environment name="classdesc*" outputname="classdesc">
<attribute name="name"/>
</environment>
<environment name="datadesc">
<attribute name="name"/>
</environment>
<environment name="datadescni" outputname="datadesc">
<attribute name="index">no</attribute>
<attribute name="name"/>
</environment>
<macro name="dataline">
<attribute name="name"/>
</macro>
<environment name="excclassdesc">
<attribute name="name"/>
<child name="args"/>
</environment>
<environment name="excdesc">
<attribute name="name"/>
</environment>
<environment name="funcdesc">
<attribute name="name"/>
<child name="args"/>
</environment>
<macro name="funcline">
<attribute name="name"/>
<child name="args"/>
</macro>
<environment name="funcdescni" outputname="funcdesc">
<attribute name="index">no</attribute>
<attribute name="name"/>
<child name="args"/>
</environment>
<macro name="funclineni" outputname="funcline">
<attribute name="index">no</attribute>
<attribute name="name"/>
<child name="args"/>
</macro>
<environment name="memberdesc">
<attribute name="class" optional="yes"/>
<attribute name="name"/>
</environment>
<environment name="memberdescni" outputname="memberdesc">
<attribute name="index">no</attribute>
<attribute name="class" optional="yes"/>
<attribute name="name"/>
</environment>
<macro name="memberline">
<attribute name="name"/>
</macro>
<environment name="methoddesc">
<attribute name="class" optional="yes"/>
<attribute name="name"/>
<child name="args"/>
</environment>
<macro name="methodline">
<attribute name="class" optional="yes"/>
<attribute name="name"/>
<child name="args"/>
</macro>
<environment name="methoddescni">
<attribute name="index">no</attribute>
<attribute name="class" optional="yes"/>
<attribute name="name"/>
<child name="args"/>
</environment>
<macro name="methodlineni" outputname="methodline">
<attribute name="index">no</attribute>
<attribute name="class" optional="yes"/>
<attribute name="name"/>
<child name="args"/>
</macro>
<environment name="opcodedesc">
<attribute name="name"/>
<attribute name="var"/>
</environment>
<!-- "See also:" sections. -->
<environment name="seealso*" outputname="seealso">
<attribute name="sidebar">no</attribute>
</environment>
<macro name="seemodule">
<!-- this causes the optional parameter to \seemodule to be
discarded -->
<attribute name="" optional="yes"/>
<attribute name="name"/>
<child name="description"/>
</macro>
<macro name="seepep">
<attribute name="number"/>
<child name="title"/>
<child name="description"/>
</macro>
<macro name="seerfc">
<attribute name="number"/>
<child name="title"/>
<child name="description"/>
</macro>
<macro name="seetext">
<child name="description"/>
</macro>
<macro name="seetitle">
<attribute name="href" optional="yes"/>
<child name="title"/>
<child name="description"/>
</macro>
<macro name="seeurl">
<attribute name="href"/>
<child name="description"/>
</macro>
<!-- Index-generating markup. -->
<macro name="index" outputname="indexterm">
<attribute name="term1"/>
</macro>
<macro name="indexii" outputname="indexterm">
<attribute name="term1"/>
<attribute name="term2"/>
</macro>
<macro name="indexiii" outputname="indexterm">
<attribute name="term1"/>
<attribute name="term2"/>
<attribute name="term3"/>
</macro>
<macro name="indexiv" outputname="indexterm">
<attribute name="term1"/>
<attribute name="term2"/>
<attribute name="term3"/>
<attribute name="term4"/>
</macro>
<macro name="ttindex" outputname="indexterm">
<attribute name="style">tt</attribute>
<attribute name="term1"/>
</macro>
<macro name="refmodindex">
<attribute name="module"/>
</macro>
<macro name="stmodindex">
<attribute name="module"/>
</macro>
<macro name="refbimodindex" outputname="refmodindex">
<attribute name="module"/>
</macro>
<macro name="refexmodindex" outputname="refmodindex">
<attribute name="module"/>
</macro>
<macro name="refstmodindex" outputname="refmodindex">
<attribute name="module"/>
</macro>
<macro name="bifuncindex">
<attribute name="name"/>
</macro>
<macro name="exindex">
<attribute name="name"/>
</macro>
<macro name="obindex">
<attribute name="name"/>
</macro>
<macro name="kwindex">
<attribute name="name"/>
</macro>
<macro name="opindex">
<attribute name="type"/>
</macro>
<macro name="stindex">
<attribute name="type"/>
</macro>
<macro name="withsubitem">
<attribute name="text"/>
<content/>
</macro>
<macro name="setindexsubitem">
<attribute name="text"/>
</macro>
<!-- Entity management. -->
<macro name="include" outputname="xi:include">
<attribute name="href"/>
</macro>
<macro name="input" outputname="xi:include">
<attribute name="href"/>
</macro>
<!-- Large-scale document structure. -->
<macro name="documentclass">
<attribute name="classname"/>
</macro>
<macro name="usepackage">
<attribute name="options" optional="yes"/>
<attribute name="pkg"/>
</macro>
<environment name="document"
endcloses="chapter chapter* section section*
subsection subsection*
subsubsection subsubsection*
paragraph paragraph* subparagraph
subparagraph*">
<attribute name="xmlns:xi"
>http://www.w3.org/2001/XInclude</attribute>
</environment>
<macro name="chapter"
closes="chapter chapter* section section* subsection subsection*
subsubsection subsubsection*
paragraph paragraph* subparagraph subparagraph*">
<text>
</text>
<child name="title"/>
<content implied="yes"/>
</macro>
<macro name="chapter*" outputname="chapter"
closes="chapter chapter* section section* subsection subsection*
subsubsection subsubsection*
paragraph paragraph* subparagraph subparagraph*">
<attribute name="numbered">no</attribute>
<text>
</text>
<child name="title"/>
<content implied="yes"/>
</macro>
<macro name="section"
closes="section section* subsection subsection*
subsubsection subsubsection*
paragraph paragraph* subparagraph subparagraph*">
<text>
</text>
<child name="title"/>
<content implied="yes"/>
</macro>
<macro name="section*" outputname="section"
closes="section section* subsection subsection*
subsubsection subsubsection*
paragraph paragraph* subparagraph subparagraph*">
<attribute name="numbered">no</attribute>
<text>
</text>
<child name="title"/>
<content implied="yes"/>
</macro>
<macro name="subsection"
closes="subsection subsection* subsubsection subsubsection*
paragraph paragraph* subparagraph subparagraph*">
<text>
</text>
<child name="title"/>
<content implied="yes"/>
</macro>
<macro name="subsection*" outputname="subsection"
closes="subsection subsection* subsubsection subsubsection*
paragraph paragraph* subparagraph subparagraph*">
<attribute name="numbered">no</attribute>
<text>
</text>
<child name="title"/>
<content implied="yes"/>
</macro>
<macro name="subsubsection"
closes="subsubsection subsubsection*
paragraph paragraph* subparagraph subparagraph*">
<text>
</text>
<child name="title"/>
<content implied="yes"/>
</macro>
<macro name="subsubsection*" outputname="subsubsection"
closes="subsubsection subsubsection*
paragraph paragraph* subparagraph subparagraph*">
<attribute name="numbered">no</attribute>
<text>
</text>
<child name="title"/>
<content implied="yes"/>
</macro>
<macro name="paragraph"
closes="paragraph paragraph* subparagraph subparagraph*">
<text>
</text>
<child name="title"/>
<content implied="yes"/>
</macro>
<macro name="paragraph*" outputname="paragraph"
closes="paragraph paragraph* subparagraph subparagraph*">
<attribute name="numbered">no</attribute>
<text>
</text>
<child name="title"/>
<content implied="yes"/>
</macro>
<macro name="subparagraph"
closes="subparagraph subparagraph*">
<text>
</text>
<child name="title"/>
<content implied="yes"/>
</macro>
<macro name="subparagraph*" outputname="subparagraph"
closes="subparagraph subparagraph*">
<attribute name="numbered">no</attribute>
<text>
</text>
<child name="title"/>
<content implied="yes"/>
</macro>
<macro name="title">
<content/>
</macro>
<macro name="appendix" outputname="back-matter"
closes="chapter chapter* section subsection subsubsection
paragraph subparagraph"/>
<environment name="list"
endcloses="item">
<attribute name="bullet"/>
<attribute name="init"/>
</environment>
<macro name="item" closes="item">
<child name="leader" optional="yes"/>
<content implied="yes"/>
</macro>
<macro name="ref">
<attribute name="ref"/>
</macro>
<environment name="description" outputname="descriptionlist"
endcloses="item"/>
<environment name="enumerate" outputname="enumeration"
endcloses="item"/>
<environment name="fulllineitems"
endcloses="item"/>
<environment name="itemize"
endcloses="item"/>
<environment name="definitions" outputname="definitionlist"
encloses="term"/>
<macro name="term" closes="definition">
<!-- not really optional, but uses the [] syntax -->
<child name="term" optional="yes"/>
<child name="definition" implied="yes"/>
</macro>
<environment name="alltt" outputname="verbatim"/>
<environment name="comment" verbatim="yes"/>
<environment name="verbatim" verbatim="yes"/>
<environment name="verbatim*" verbatim="yes">
<!-- not used anywhere, but it's a standard LaTeXism -->
<attribute name="spaces">visible</attribute>
</environment>
<macro name="verbatiminput" ouptutname="xi:include">
<attribute name="parse">text</attribute>
<attribute name="href"/>
</macro>
<!-- Table markup. -->
<macro name="hline"/>
<environment name="tableii" outputname="table">
<attribute name="cols">2</attribute>
<attribute name="colspec"/>
<attribute name="style"/>
<child name="entry"/>
<text>
</text>
<child name="entry"/>
</environment>
<environment name="longtableii" outputname="table">
<attribute name="cols">2</attribute>
<attribute name="colspec"/>
<attribute name="style"/>
<child name="entry"/>
<text>
</text>
<child name="entry"/>
</environment>
<macro name="lineii" outputname="row">
<child name="entry"/>
<text>
</text>
<child name="entry"/>
</macro>
<environment name="tableiii" outputname="table">
<attribute name="cols">3</attribute>
<attribute name="colspec"/>
<attribute name="style"/>
<child name="entry"/>
<text>
</text>
<child name="entry"/>
<text>
</text>
<child name="entry"/>
</environment>
<environment name="longtableiii" outputname="table">
<attribute name="cols">3</attribute>
<attribute name="colspec"/>
<attribute name="style"/>
<child name="entry"/>
<text>
</text>
<child name="entry"/>
<text>
</text>
<child name="entry"/>
</environment>
<macro name="lineiii" outputname="row">
<child name="entry"/>
<text>
</text>
<child name="entry"/>
<text>
</text>
<child name="entry"/>
</macro>
<environment name="tableiv" outputname="table">
<attribute name="cols">4</attribute>
<attribute name="colspec"/>
<attribute name="style"/>
<child name="entry"/>
<text>
</text>
<child name="entry"/>
<text>
</text>
<child name="entry"/>
<text>
</text>
<child name="entry"/>
</environment>
<environment name="longtableiv" outputname="table">
<attribute name="cols">4</attribute>
<attribute name="colspec"/>
<attribute name="style"/>
<child name="entry"/>
<text>
</text>
<child name="entry"/>
<text>
</text>
<child name="entry"/>
<text>
</text>
<child name="entry"/>
</environment>
<macro name="lineiv" outputname="row">
<child name="entry"/>
<text>
</text>
<child name="entry"/>
<text>
</text>
<child name="entry"/>
<text>
</text>
<child name="entry"/>
</macro>
<environment name="tablev" outputname="table">
<attribute name="cols">4</attribute>
<attribute name="colspec"/>
<attribute name="style"/>
<child name="entry"/>
<text>
</text>
<child name="entry"/>
<text>
</text>
<child name="entry"/>
<text>
</text>
<child name="entry"/>
</environment>
<environment name="longtablev" outputname="table">
<attribute name="cols">4</attribute>
<attribute name="colspec"/>
<attribute name="style"/>
<child name="entry"/>
<text>
</text>
<child name="entry"/>
<text>
</text>
<child name="entry"/>
<text>
</text>
<child name="entry"/>
<text>
</text>
<child name="entry"/>
</environment>
<macro name="linev" outputname="row">
<child name="entry"/>
<text>
</text>
<child name="entry"/>
<text>
</text>
<child name="entry"/>
<text>
</text>
<child name="entry"/>
<text>
</text>
<child name="entry"/>
</macro>
<!-- These are handled at a later translation stage, at least for now. -->
<macro name="Cpp" outputname="">
<text>C++</text>
</macro>
<macro name="geq" outputname="">
<entityref name="geq"/>
</macro>
<macro name="infinity" outputname="">
<entityref name="infin"/>
</macro>
<macro name="LaTeX" outputname="">
<text>LaTeX</text>
</macro>
<macro name="ldots" outputname="">
<text>...</text>
</macro>
<macro name="leq" outputname="">
<entityref name="leq"/>
</macro>
<macro name="plusminus" outputname="">
<entityref name="plusmn"/>
</macro>
<macro name="TeX" outputname="">
<text>TeX</text>
</macro>
<macro name="version"/>
<!-- Distutils things. -->
<macro name="command">
<content/>
</macro>
<macro name="option">
<content/>
</macro>
<macro name="filevar" outputname="var">
<content/>
</macro>
<macro name="XXX" outputname="editorial-comment">
<content/>
</macro>
<!-- Grammar production lists -->
<environment name="productionlist">
<attribute name="grammar" optional="yes"/>
</environment>
<macro name="production">
<attribute name="token"/>
<content/>
</macro>
<macro name="productioncont">
<content/>
</macro>
<macro name="token" outputname="grammartoken">
<content/>
</macro>
<macro name="grammartoken">
<content/>
</macro>
<!-- Misc. -->
<macro name="emph">
<content/>
</macro>
<macro name="strong">
<content/>
</macro>
<macro name="textrm">
<content/>
</macro>
<macro name="texttt">
<content/>
</macro>
<macro name="code">
<content/>
</macro>
<macro name="exception">
<content/>
</macro>
<macro name="keyword">
<content/>
</macro>
<macro name="samp">
<content/>
</macro>
<macro name="class">
<content/>
</macro>
<macro name="cdata">
<content/>
</macro>
<macro name="cfunction">
<content/>
</macro>
<macro name="csimplemacro">
<content/>
</macro>
<macro name="ctype">
<content/>
</macro>
<macro name="pytype">
<content/>
</macro>
<macro name="character">
<content/>
</macro>
<macro name="constant">
<content/>
</macro>
<macro name="envvar" outputname="envar">
<content/>
</macro>
<macro name="file" outputname="filename">
<content/>
</macro>
<macro name="filenq" outputname="filename">
<attribute name="quote">no</attribute>
<content/>
</macro>
<macro name="function">
<content/>
</macro>
<macro name="kbd" outputname="keysym">
<content/>
</macro>
<macro name="mailheader">
<content/>
</macro>
<macro name="makevar">
<content/>
</macro>
<macro name="method">
<content/>
</macro>
<macro name="member">
<content/>
</macro>
<macro name="mimetype">
<content/>
</macro>
<macro name="newsgroup">
<content/>
</macro>
<macro name="program" outputname="command">
<content/>
</macro>
<macro name="programopt" outputname="option">
<content/>
</macro>
<macro name="longprogramopt" outputname="longoption">
<content/>
</macro>
<macro name="regexp">
<content/>
</macro>
<macro name="var">
<content/>
</macro>
<macro name="email">
<content/>
</macro>
<macro name="ulink">
<!-- order of the parameters makes this difficult;
we'll need to fix it up to <ulink href="...">...</ulink>
in docfixer.py.
-->
<child name="text"/>
<child name="href"/>
</macro>
<macro name="url">
<content/>
</macro>
<macro name="footnote">
<content/>
</macro>
<macro name="dfn" outputname="definedterm">
<content/>
</macro>
<macro name="mbox">
<content/>
</macro>
<!-- minimal math stuff to get by -->
<macro name="pi"/>
<macro name="sqrt">
<content/>
</macro>
<macro name="frac" outputname="fraction">
<child name="numerator"/>
<child name="denominator"/>
</macro>
<macro name="sum">
<content/>
</macro>
<macro name="leftline" outputname="">
<content/>
</macro>
<!-- Conversions to text; perhaps could be different? There's -->
<!-- no way for a style sheet to work with these this way. -->
<macro name="ABC" outputname="">
<text>ABC</text>
</macro>
<macro name="ASCII" outputname="">
<text>ASCII</text>
</macro>
<macro name="C" outputname="">
<text>C</text>
</macro>
<macro name="EOF" outputname="">
<text>EOF</text>
</macro>
<macro name="e" outputname="">
<text>\</text>
</macro>
<macro name="NULL" outputname="constant">
<text>NULL</text>
</macro>
<macro name="POSIX" outputname="">
<text>POSIX</text>
</macro>
<macro name="UNIX" outputname="">
<text>Unix</text>
</macro>
<macro name="textasciicircum" outputname="">
<text>^</text>
</macro>
<macro name="textasciitilde" outputname="">
<text>~</text>
</macro>
<macro name="textbackslash" outputname="">
<text>\</text>
</macro>
<macro name="textbar" outputname="">
<text>|</text>
</macro>
<macro name="textgreater" outputname="">
<text>&gt;</text>
</macro>
<macro name="textless" outputname="">
<text>&lt;</text>
</macro>
<!-- These will end up disappearing as well! -->
<macro name="catcode" outputname=""/>
<macro name="fi" outputname=""/>
<macro name="ifhtml" outputname=""/>
<macro name="indexname" outputname=""/>
<macro name="labelwidth" outputname=""/>
<macro name="large" outputname=""/>
<macro name="leftmargin" outputname=""/>
<macro name="makeindex" outputname=""/>
<macro name="makemodindex" outputname=""/>
<macro name="maketitle" outputname=""/>
<macro name="noindent" outputname=""/>
<macro name="protect" outputname=""/>
<macro name="textwidth"/>
<macro name="renewcommand">
<attribute name="macro"/>
<attribute name="nargs" optional="yes"/>
<content/>
</macro>
<macro name="tableofcontents" outputname=""/>
<macro name="vspace">
<attribute name="size"/>
</macro>
</conversion>

1073
doc/tools/sgmlconv/docfixer.py Executable file

File diff suppressed because it is too large Load Diff

264
doc/tools/sgmlconv/esis2sgml.py Executable file
View File

@ -0,0 +1,264 @@
#! /usr/bin/env python
"""Convert ESIS events to SGML or XML markup.
This is limited, but seems sufficient for the ESIS generated by the
latex2esis.py script when run over the Python documentation.
"""
# This should have an explicit option to indicate whether the *INPUT* was
# generated from an SGML or an XML application.
import errno
import os
import re
import string
from xml.sax.saxutils import escape
import esistools
AUTOCLOSE = ()
EMPTIES_FILENAME = "../sgml/empties.dat"
LIST_EMPTIES = 0
_elem_map = {}
_attr_map = {}
_token_map = {}
_normalize_case = str
def map_gi(sgmlgi, map):
uncased = _normalize_case(sgmlgi)
try:
return map[uncased]
except IndexError:
map[uncased] = sgmlgi
return sgmlgi
def null_map_gi(sgmlgi, map):
return sgmlgi
def format_attrs(attrs, xml=0):
attrs = attrs.items()
attrs.sort()
parts = []
append = parts.append
for name, value in attrs:
if xml:
append('%s="%s"' % (name, escape(value)))
else:
# this is a little bogus, but should do for now
if name == value and isnmtoken(value):
append(value)
elif istoken(value):
if value == "no" + name:
append(value)
else:
append("%s=%s" % (name, value))
else:
append('%s="%s"' % (name, escape(value)))
if parts:
parts.insert(0, '')
return " ".join(parts)
_nmtoken_rx = re.compile("[a-z][-._a-z0-9]*$", re.IGNORECASE)
def isnmtoken(s):
return _nmtoken_rx.match(s) is not None
_token_rx = re.compile("[a-z0-9][-._a-z0-9]*$", re.IGNORECASE)
def istoken(s):
return _token_rx.match(s) is not None
def convert(ifp, ofp, xml=0, autoclose=(), verbatims=()):
if xml:
autoclose = ()
attrs = {}
lastopened = None
knownempties = []
knownempty = 0
lastempty = 0
inverbatim = 0
while 1:
line = ifp.readline()
if not line:
break
type = line[0]
data = line[1:]
if data and data[-1] == "\n":
data = data[:-1]
if type == "-":
data = esistools.decode(data)
data = escape(data)
if not inverbatim:
data = data.replace("---", "&mdash;")
ofp.write(data)
if "\n" in data:
lastopened = None
knownempty = 0
lastempty = 0
elif type == "(":
if data == "COMMENT":
ofp.write("<!--")
continue
data = map_gi(data, _elem_map)
if knownempty and xml:
ofp.write("<%s%s/>" % (data, format_attrs(attrs, xml)))
else:
ofp.write("<%s%s>" % (data, format_attrs(attrs, xml)))
if knownempty and data not in knownempties:
# accumulate knowledge!
knownempties.append(data)
attrs = {}
lastopened = data
lastempty = knownempty
knownempty = 0
inverbatim = data in verbatims
elif type == ")":
if data == "COMMENT":
ofp.write("-->")
continue
data = map_gi(data, _elem_map)
if xml:
if not lastempty:
ofp.write("</%s>" % data)
elif data not in knownempties:
if data in autoclose:
pass
elif lastopened == data:
ofp.write("</>")
else:
ofp.write("</%s>" % data)
lastopened = None
lastempty = 0
inverbatim = 0
elif type == "A":
name, type, value = data.split(" ", 2)
name = map_gi(name, _attr_map)
attrs[name] = esistools.decode(value)
elif type == "e":
knownempty = 1
elif type == "&":
ofp.write("&%s;" % data)
knownempty = 0
else:
raise RuntimeError, "unrecognized ESIS event type: '%s'" % type
if LIST_EMPTIES:
dump_empty_element_names(knownempties)
def dump_empty_element_names(knownempties):
d = {}
for gi in knownempties:
d[gi] = gi
knownempties.append("")
if os.path.isfile(EMPTIES_FILENAME):
fp = open(EMPTIES_FILENAME)
while 1:
line = fp.readline()
if not line:
break
gi = line.strip()
if gi:
d[gi] = gi
fp = open(EMPTIES_FILENAME, "w")
gilist = d.keys()
gilist.sort()
fp.write("\n".join(gilist))
fp.write("\n")
fp.close()
def update_gi_map(map, names, fromsgml=1):
for name in names.split(","):
if fromsgml:
uncased = name.lower()
else:
uncased = name
map[uncased] = name
def main():
import getopt
import sys
#
autoclose = AUTOCLOSE
xml = 1
xmldecl = 0
elem_names = ''
attr_names = ''
value_names = ''
verbatims = ('verbatim', 'interactive-session')
opts, args = getopt.getopt(sys.argv[1:], "adesx",
["autoclose=", "declare", "sgml", "xml",
"elements-map=", "attributes-map",
"values-map="])
for opt, arg in opts:
if opt in ("-d", "--declare"):
xmldecl = 1
elif opt == "-e":
global LIST_EMPTIES
LIST_EMPTIES = 1
elif opt in ("-s", "--sgml"):
xml = 0
elif opt in ("-x", "--xml"):
xml = 1
elif opt in ("-a", "--autoclose"):
autoclose = arg.split(",")
elif opt == "--elements-map":
elem_names = ("%s,%s" % (elem_names, arg))[1:]
elif opt == "--attributes-map":
attr_names = ("%s,%s" % (attr_names, arg))[1:]
elif opt == "--values-map":
value_names = ("%s,%s" % (value_names, arg))[1:]
#
# open input streams:
#
if len(args) == 0:
ifp = sys.stdin
ofp = sys.stdout
elif len(args) == 1:
ifp = open(args[0])
ofp = sys.stdout
elif len(args) == 2:
ifp = open(args[0])
ofp = open(args[1], "w")
else:
usage()
sys.exit(2)
#
# setup the name maps:
#
if elem_names or attr_names or value_names:
# assume the origin was SGML; ignore case of the names from the ESIS
# stream but set up conversion tables to get the case right on output
global _normalize_case
_normalize_case = string.lower
update_gi_map(_elem_map, elem_names.split(","))
update_gi_map(_attr_map, attr_names.split(","))
update_gi_map(_values_map, value_names.split(","))
else:
global map_gi
map_gi = null_map_gi
#
# run the conversion:
#
try:
if xml and xmldecl:
opf.write('<?xml version="1.0" encoding="iso8859-1"?>\n')
convert(ifp, ofp, xml=xml, autoclose=autoclose, verbatims=verbatims)
except IOError, (err, msg):
if err != errno.EPIPE:
raise
if __name__ == "__main__":
main()

View File

@ -0,0 +1,312 @@
"""Miscellaneous utility functions useful for dealing with ESIS streams."""
import re
import xml.dom.pulldom
import xml.sax
import xml.sax.handler
import xml.sax.xmlreader
_data_match = re.compile(r"[^\\][^\\]*").match
def decode(s):
r = ''
while s:
m = _data_match(s)
if m:
r = r + m.group()
s = s[m.end():]
elif s[1] == "\\":
r = r + "\\"
s = s[2:]
elif s[1] == "n":
r = r + "\n"
s = s[2:]
elif s[1] == "%":
s = s[2:]
n, s = s.split(";", 1)
r = r + unichr(int(n))
else:
raise ValueError, "can't handle " + `s`
return r
_charmap = {}
for c in range(128):
_charmap[chr(c)] = chr(c)
_charmap[unichr(c + 128)] = chr(c + 128)
_charmap["\n"] = r"\n"
_charmap["\\"] = r"\\"
del c
_null_join = ''.join
def encode(s):
try:
return _null_join(map(_charmap.get, s))
except TypeError:
raise Exception("could not encode %r: %r" % (s, map(_charmap.get, s)))
class ESISReader(xml.sax.xmlreader.XMLReader):
"""SAX Reader which reads from an ESIS stream.
No verification of the document structure is performed by the
reader; a general verifier could be used as the target
ContentHandler instance.
"""
_decl_handler = None
_lexical_handler = None
_public_id = None
_system_id = None
_buffer = ""
_is_empty = 0
_lineno = 0
_started = 0
def __init__(self, contentHandler=None, errorHandler=None):
xml.sax.xmlreader.XMLReader.__init__(self)
self._attrs = {}
self._attributes = Attributes(self._attrs)
self._locator = Locator()
self._empties = {}
if contentHandler:
self.setContentHandler(contentHandler)
if errorHandler:
self.setErrorHandler(errorHandler)
def get_empties(self):
return self._empties.keys()
#
# XMLReader interface
#
def parse(self, source):
raise RuntimeError
self._locator._public_id = source.getPublicId()
self._locator._system_id = source.getSystemId()
fp = source.getByteStream()
handler = self.getContentHandler()
if handler:
handler.startDocument()
lineno = 0
while 1:
token, data = self._get_token(fp)
if token is None:
break
lineno = lineno + 1
self._locator._lineno = lineno
self._handle_token(token, data)
handler = self.getContentHandler()
if handler:
handler.startDocument()
def feed(self, data):
if not self._started:
handler = self.getContentHandler()
if handler:
handler.startDocument()
self._started = 1
data = self._buffer + data
self._buffer = None
lines = data.split("\n")
if lines:
for line in lines[:-1]:
self._lineno = self._lineno + 1
self._locator._lineno = self._lineno
if not line:
e = xml.sax.SAXParseException(
"ESIS input line contains no token type mark",
None, self._locator)
self.getErrorHandler().error(e)
else:
self._handle_token(line[0], line[1:])
self._buffer = lines[-1]
else:
self._buffer = ""
def close(self):
handler = self.getContentHandler()
if handler:
handler.endDocument()
self._buffer = ""
def _get_token(self, fp):
try:
line = fp.readline()
except IOError, e:
e = SAXException("I/O error reading input stream", e)
self.getErrorHandler().fatalError(e)
return
if not line:
return None, None
if line[-1] == "\n":
line = line[:-1]
if not line:
e = xml.sax.SAXParseException(
"ESIS input line contains no token type mark",
None, self._locator)
self.getErrorHandler().error(e)
return
return line[0], line[1:]
def _handle_token(self, token, data):
handler = self.getContentHandler()
if token == '-':
if data and handler:
handler.characters(decode(data))
elif token == ')':
if handler:
handler.endElement(decode(data))
elif token == '(':
if self._is_empty:
self._empties[data] = 1
self._is_empty = 0
if handler:
handler.startElement(data, self._attributes)
self._attrs.clear()
elif token == 'A':
name, value = data.split(' ', 1)
if value != "IMPLIED":
type, value = value.split(' ', 1)
self._attrs[name] = (decode(value), type)
elif token == '&':
# entity reference in SAX?
pass
elif token == '?':
if handler:
if ' ' in data:
target, data = data.split(None, 1)
else:
target, data = data, ""
handler.processingInstruction(target, decode(data))
elif token == 'N':
handler = self.getDTDHandler()
if handler:
handler.notationDecl(data, self._public_id, self._system_id)
self._public_id = None
self._system_id = None
elif token == 'p':
self._public_id = decode(data)
elif token == 's':
self._system_id = decode(data)
elif token == 'e':
self._is_empty = 1
elif token == 'C':
pass
else:
e = SAXParseException("unknown ESIS token in event stream",
None, self._locator)
self.getErrorHandler().error(e)
def setContentHandler(self, handler):
old = self.getContentHandler()
if old:
old.setDocumentLocator(None)
if handler:
handler.setDocumentLocator(self._locator)
xml.sax.xmlreader.XMLReader.setContentHandler(self, handler)
def getProperty(self, property):
if property == xml.sax.handler.property_lexical_handler:
return self._lexical_handler
elif property == xml.sax.handler.property_declaration_handler:
return self._decl_handler
else:
raise xml.sax.SAXNotRecognizedException("unknown property %s"
% `property`)
def setProperty(self, property, value):
if property == xml.sax.handler.property_lexical_handler:
if self._lexical_handler:
self._lexical_handler.setDocumentLocator(None)
if value:
value.setDocumentLocator(self._locator)
self._lexical_handler = value
elif property == xml.sax.handler.property_declaration_handler:
if self._decl_handler:
self._decl_handler.setDocumentLocator(None)
if value:
value.setDocumentLocator(self._locator)
self._decl_handler = value
else:
raise xml.sax.SAXNotRecognizedException()
def getFeature(self, feature):
if feature == xml.sax.handler.feature_namespaces:
return 1
else:
return xml.sax.xmlreader.XMLReader.getFeature(self, feature)
def setFeature(self, feature, enabled):
if feature == xml.sax.handler.feature_namespaces:
pass
else:
xml.sax.xmlreader.XMLReader.setFeature(self, feature, enabled)
class Attributes(xml.sax.xmlreader.AttributesImpl):
# self._attrs has the form {name: (value, type)}
def getType(self, name):
return self._attrs[name][1]
def getValue(self, name):
return self._attrs[name][0]
def getValueByQName(self, name):
return self._attrs[name][0]
def __getitem__(self, name):
return self._attrs[name][0]
def get(self, name, default=None):
if self._attrs.has_key(name):
return self._attrs[name][0]
return default
def items(self):
L = []
for name, (value, type) in self._attrs.items():
L.append((name, value))
return L
def values(self):
L = []
for value, type in self._attrs.values():
L.append(value)
return L
class Locator(xml.sax.xmlreader.Locator):
_lineno = -1
_public_id = None
_system_id = None
def getLineNumber(self):
return self._lineno
def getPublicId(self):
return self._public_id
def getSystemId(self):
return self._system_id
def parse(stream_or_string, parser=None):
if type(stream_or_string) in [type(""), type(u"")]:
stream = open(stream_or_string)
else:
stream = stream_or_string
if not parser:
parser = ESISReader()
return xml.dom.pulldom.DOMEventStream(stream, parser, (2 ** 14) - 20)

565
doc/tools/sgmlconv/latex2esis.py Executable file
View File

@ -0,0 +1,565 @@
#! /usr/bin/env python
"""Generate ESIS events based on a LaTeX source document and
configuration data.
The conversion is not strong enough to work with arbitrary LaTeX
documents; it has only been designed to work with the highly stylized
markup used in the standard Python documentation. A lot of
information about specific markup is encoded in the control table
passed to the convert() function; changing this table can allow this
tool to support additional LaTeX markups.
The format of the table is largely undocumented; see the commented
headers where the table is specified in main(). There is no provision
to load an alternate table from an external file.
"""
import errno
import getopt
import os
import re
import sys
import xml.sax
import xml.sax.saxutils
from esistools import encode
DEBUG = 0
class LaTeXFormatError(Exception):
pass
class LaTeXStackError(LaTeXFormatError):
def __init__(self, found, stack):
msg = "environment close for %s doesn't match;\n stack = %s" \
% (found, stack)
self.found = found
self.stack = stack[:]
LaTeXFormatError.__init__(self, msg)
_begin_env_rx = re.compile(r"[\\]begin{([^}]*)}")
_end_env_rx = re.compile(r"[\\]end{([^}]*)}")
_begin_macro_rx = re.compile(r"[\\]([a-zA-Z]+[*]?) ?({|\s*\n?)")
_comment_rx = re.compile("%+ ?(.*)\n[ \t]*")
_text_rx = re.compile(r"[^]~%\\{}]+")
_optional_rx = re.compile(r"\s*[[]([^]]*)[]]", re.MULTILINE)
# _parameter_rx is this complicated to allow {...} inside a parameter;
# this is useful to match tabular layout specifications like {c|p{24pt}}
_parameter_rx = re.compile("[ \n]*{(([^{}}]|{[^}]*})*)}")
_token_rx = re.compile(r"[a-zA-Z][a-zA-Z0-9.-]*$")
_start_group_rx = re.compile("[ \n]*{")
_start_optional_rx = re.compile("[ \n]*[[]")
ESCAPED_CHARS = "$%#^ {}&~"
def dbgmsg(msg):
if DEBUG:
sys.stderr.write(msg + "\n")
def pushing(name, point, depth):
dbgmsg("pushing <%s> at %s" % (name, point))
def popping(name, point, depth):
dbgmsg("popping </%s> at %s" % (name, point))
class _Stack(list):
def append(self, entry):
if not isinstance(entry, str):
raise LaTeXFormatError("cannot push non-string on stack: "
+ `entry`)
#dbgmsg("%s<%s>" % (" "*len(self.data), entry))
list.append(self, entry)
def pop(self, index=-1):
entry = self[index]
del self[index]
#dbgmsg("%s</%s>" % (" " * len(self), entry))
def __delitem__(self, index):
entry = self[index]
list.__delitem__(self, index)
#dbgmsg("%s</%s>" % (" " * len(self), entry))
def new_stack():
if DEBUG:
return _Stack()
else:
return []
class Conversion:
def __init__(self, ifp, ofp, table):
self.write = ofp.write
self.ofp = ofp
self.table = table
L = [s.rstrip() for s in ifp.readlines()]
L.append("")
self.line = "\n".join(L)
self.preamble = 1
def convert(self):
self.subconvert()
def subconvert(self, endchar=None, depth=0):
#
# Parses content, including sub-structures, until the character
# 'endchar' is found (with no open structures), or until the end
# of the input data is endchar is None.
#
stack = new_stack()
line = self.line
while line:
if line[0] == endchar and not stack:
self.line = line
return line
m = _comment_rx.match(line)
if m:
text = m.group(1)
if text:
self.write("(COMMENT\n- %s \n)COMMENT\n-\\n\n"
% encode(text))
line = line[m.end():]
continue
m = _begin_env_rx.match(line)
if m:
name = m.group(1)
entry = self.get_env_entry(name)
# re-write to use the macro handler
line = r"\%s %s" % (name, line[m.end():])
continue
m = _end_env_rx.match(line)
if m:
# end of environment
envname = m.group(1)
entry = self.get_entry(envname)
while stack and envname != stack[-1] \
and stack[-1] in entry.endcloses:
self.write(")%s\n" % stack.pop())
if stack and envname == stack[-1]:
self.write(")%s\n" % entry.outputname)
del stack[-1]
else:
raise LaTeXStackError(envname, stack)
line = line[m.end():]
continue
m = _begin_macro_rx.match(line)
if m:
# start of macro
macroname = m.group(1)
if macroname == "c":
# Ugh! This is a combining character...
endpos = m.end()
self.combining_char("c", line[endpos])
line = line[endpos + 1:]
continue
entry = self.get_entry(macroname)
if entry.verbatim:
# magic case!
pos = line.find("\\end{%s}" % macroname)
text = line[m.end(1):pos]
stack.append(entry.name)
self.write("(%s\n" % entry.outputname)
self.write("-%s\n" % encode(text))
self.write(")%s\n" % entry.outputname)
stack.pop()
line = line[pos + len("\\end{%s}" % macroname):]
continue
while stack and stack[-1] in entry.closes:
top = stack.pop()
topentry = self.get_entry(top)
if topentry.outputname:
self.write(")%s\n-\\n\n" % topentry.outputname)
#
if entry.outputname and entry.empty:
self.write("e\n")
#
params, optional, empty = self.start_macro(macroname)
# rip off the macroname
if params:
line = line[m.end(1):]
elif empty:
line = line[m.end(1):]
else:
line = line[m.end():]
opened = 0
implied_content = 0
# handle attribute mappings here:
for pentry in params:
if pentry.type == "attribute":
if pentry.optional:
m = _optional_rx.match(line)
if m and entry.outputname:
line = line[m.end():]
self.dump_attr(pentry, m.group(1))
elif pentry.text and entry.outputname:
# value supplied by conversion spec:
self.dump_attr(pentry, pentry.text)
else:
m = _parameter_rx.match(line)
if not m:
raise LaTeXFormatError(
"could not extract parameter %s for %s: %s"
% (pentry.name, macroname, `line[:100]`))
if entry.outputname:
self.dump_attr(pentry, m.group(1))
line = line[m.end():]
elif pentry.type == "child":
if pentry.optional:
m = _optional_rx.match(line)
if m:
line = line[m.end():]
if entry.outputname and not opened:
opened = 1
self.write("(%s\n" % entry.outputname)
stack.append(macroname)
stack.append(pentry.name)
self.write("(%s\n" % pentry.name)
self.write("-%s\n" % encode(m.group(1)))
self.write(")%s\n" % pentry.name)
stack.pop()
else:
if entry.outputname and not opened:
opened = 1
self.write("(%s\n" % entry.outputname)
stack.append(entry.name)
self.write("(%s\n" % pentry.name)
stack.append(pentry.name)
self.line = skip_white(line)[1:]
line = self.subconvert(
"}", len(stack) + depth + 1)[1:]
self.write(")%s\n" % stack.pop())
elif pentry.type == "content":
if pentry.implied:
implied_content = 1
else:
if entry.outputname and not opened:
opened = 1
self.write("(%s\n" % entry.outputname)
stack.append(entry.name)
line = skip_white(line)
if line[0] != "{":
raise LaTeXFormatError(
"missing content for " + macroname)
self.line = line[1:]
line = self.subconvert("}", len(stack) + depth + 1)
if line and line[0] == "}":
line = line[1:]
elif pentry.type == "text" and pentry.text:
if entry.outputname and not opened:
opened = 1
stack.append(entry.name)
self.write("(%s\n" % entry.outputname)
#dbgmsg("--- text: %s" % `pentry.text`)
self.write("-%s\n" % encode(pentry.text))
elif pentry.type == "entityref":
self.write("&%s\n" % pentry.name)
if entry.outputname:
if not opened:
self.write("(%s\n" % entry.outputname)
stack.append(entry.name)
if not implied_content:
self.write(")%s\n" % entry.outputname)
stack.pop()
continue
if line[0] == endchar and not stack:
self.line = line[1:]
return self.line
if line[0] == "}":
# end of macro or group
macroname = stack[-1]
if macroname:
conversion = self.table[macroname]
if conversion.outputname:
# otherwise, it was just a bare group
self.write(")%s\n" % conversion.outputname)
del stack[-1]
line = line[1:]
continue
if line[0] == "~":
# don't worry about the "tie" aspect of this command
line = line[1:]
self.write("- \n")
continue
if line[0] == "{":
stack.append("")
line = line[1:]
continue
if line[0] == "\\" and line[1] in ESCAPED_CHARS:
self.write("-%s\n" % encode(line[1]))
line = line[2:]
continue
if line[:2] == r"\\":
self.write("(BREAK\n)BREAK\n")
line = line[2:]
continue
if line[:2] == r"\_":
line = "_" + line[2:]
continue
if line[:2] in (r"\'", r'\"'):
# combining characters...
self.combining_char(line[1], line[2])
line = line[3:]
continue
m = _text_rx.match(line)
if m:
text = encode(m.group())
self.write("-%s\n" % text)
line = line[m.end():]
continue
# special case because of \item[]
# XXX can we axe this???
if line[0] == "]":
self.write("-]\n")
line = line[1:]
continue
# avoid infinite loops
extra = ""
if len(line) > 100:
extra = "..."
raise LaTeXFormatError("could not identify markup: %s%s"
% (`line[:100]`, extra))
while stack:
entry = self.get_entry(stack[-1])
if entry.closes:
self.write(")%s\n-%s\n" % (entry.outputname, encode("\n")))
del stack[-1]
else:
break
if stack:
raise LaTeXFormatError("elements remain on stack: "
+ ", ".join(stack))
# otherwise we just ran out of input here...
# This is a really limited table of combinations, but it will have
# to do for now.
_combinations = {
("c", "c"): 0x00E7,
("'", "e"): 0x00E9,
('"', "o"): 0x00F6,
}
def combining_char(self, prefix, char):
ordinal = self._combinations[(prefix, char)]
self.write("-\\%%%d;\n" % ordinal)
def start_macro(self, name):
conversion = self.get_entry(name)
parameters = conversion.parameters
optional = parameters and parameters[0].optional
return parameters, optional, conversion.empty
def get_entry(self, name):
entry = self.table.get(name)
if entry is None:
dbgmsg("get_entry(%s) failing; building default entry!" % `name`)
# not defined; build a default entry:
entry = TableEntry(name)
entry.has_content = 1
entry.parameters.append(Parameter("content"))
self.table[name] = entry
return entry
def get_env_entry(self, name):
entry = self.table.get(name)
if entry is None:
# not defined; build a default entry:
entry = TableEntry(name, 1)
entry.has_content = 1
entry.parameters.append(Parameter("content"))
entry.parameters[-1].implied = 1
self.table[name] = entry
elif not entry.environment:
raise LaTeXFormatError(
name + " is defined as a macro; expected environment")
return entry
def dump_attr(self, pentry, value):
if not (pentry.name and value):
return
if _token_rx.match(value):
dtype = "TOKEN"
else:
dtype = "CDATA"
self.write("A%s %s %s\n" % (pentry.name, dtype, encode(value)))
def convert(ifp, ofp, table):
c = Conversion(ifp, ofp, table)
try:
c.convert()
except IOError, (err, msg):
if err != errno.EPIPE:
raise
def skip_white(line):
while line and line[0] in " %\n\t\r":
line = line[1:].lstrip()
return line
class TableEntry:
def __init__(self, name, environment=0):
self.name = name
self.outputname = name
self.environment = environment
self.empty = not environment
self.has_content = 0
self.verbatim = 0
self.auto_close = 0
self.parameters = []
self.closes = []
self.endcloses = []
class Parameter:
def __init__(self, type, name=None, optional=0):
self.type = type
self.name = name
self.optional = optional
self.text = ''
self.implied = 0
class TableHandler(xml.sax.handler.ContentHandler):
def __init__(self):
self.__table = {}
self.__buffer = ''
self.__methods = {}
def get_table(self):
for entry in self.__table.values():
if entry.environment and not entry.has_content:
p = Parameter("content")
p.implied = 1
entry.parameters.append(p)
entry.has_content = 1
return self.__table
def startElement(self, tag, attrs):
try:
start, end = self.__methods[tag]
except KeyError:
start = getattr(self, "start_" + tag, None)
end = getattr(self, "end_" + tag, None)
self.__methods[tag] = (start, end)
if start:
start(attrs)
def endElement(self, tag):
start, end = self.__methods[tag]
if end:
end()
def endDocument(self):
self.__methods.clear()
def characters(self, data):
self.__buffer += data
def start_environment(self, attrs):
name = attrs["name"]
self.__current = TableEntry(name, environment=1)
self.__current.verbatim = attrs.get("verbatim") == "yes"
if attrs.has_key("outputname"):
self.__current.outputname = attrs.get("outputname")
self.__current.endcloses = attrs.get("endcloses", "").split()
def end_environment(self):
self.end_macro()
def start_macro(self, attrs):
name = attrs["name"]
self.__current = TableEntry(name)
self.__current.closes = attrs.get("closes", "").split()
if attrs.has_key("outputname"):
self.__current.outputname = attrs.get("outputname")
def end_macro(self):
name = self.__current.name
if self.__table.has_key(name):
raise ValueError("name %s already in use" % `name`)
self.__table[name] = self.__current
self.__current = None
def start_attribute(self, attrs):
name = attrs.get("name")
optional = attrs.get("optional") == "yes"
if name:
p = Parameter("attribute", name, optional=optional)
else:
p = Parameter("attribute", optional=optional)
self.__current.parameters.append(p)
self.__buffer = ''
def end_attribute(self):
self.__current.parameters[-1].text = self.__buffer
def start_entityref(self, attrs):
name = attrs["name"]
p = Parameter("entityref", name)
self.__current.parameters.append(p)
def start_child(self, attrs):
name = attrs["name"]
p = Parameter("child", name, attrs.get("optional") == "yes")
self.__current.parameters.append(p)
self.__current.empty = 0
def start_content(self, attrs):
p = Parameter("content")
p.implied = attrs.get("implied") == "yes"
if self.__current.environment:
p.implied = 1
self.__current.parameters.append(p)
self.__current.has_content = 1
self.__current.empty = 0
def start_text(self, attrs):
self.__current.empty = 0
self.__buffer = ''
def end_text(self):
p = Parameter("text")
p.text = self.__buffer
self.__current.parameters.append(p)
def load_table(fp):
ch = TableHandler()
xml.sax.parse(fp, ch)
return ch.get_table()
def main():
global DEBUG
#
opts, args = getopt.getopt(sys.argv[1:], "D", ["debug"])
for opt, arg in opts:
if opt in ("-D", "--debug"):
DEBUG += 1
if len(args) == 0:
ifp = sys.stdin
ofp = sys.stdout
elif len(args) == 1:
ifp = open(args[0])
ofp = sys.stdout
elif len(args) == 2:
ifp = open(args[0])
ofp = open(args[1], "w")
else:
usage()
sys.exit(2)
table = load_table(open(os.path.join(sys.path[0], 'conversion.xml')))
convert(ifp, ofp, table)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,48 @@
# -*- makefile -*-
#
# Extra magic needed by the LaTeX->XML conversion process. This requires
# $(TOOLSDIR) to be properly defined.
DOCFIXER= $(TOOLSDIR)/sgmlconv/docfixer.py
ESIS2ML= $(TOOLSDIR)/sgmlconv/esis2sgml.py
LATEX2ESIS= $(TOOLSDIR)/sgmlconv/latex2esis.py
CONVERSION= $(TOOLSDIR)/sgmlconv/conversion.xml
ESISTARGETS= $(patsubst %.tex,%.esis,$(wildcard *.tex))
ESIS1TARGETS= $(patsubst %.tex,%.esis1,$(wildcard *.tex))
XMLTARGETS= $(patsubst %.tex,%.xml,$(wildcard *.tex))
L2EFLAGS=
all: xml
esis: $(ESISTARGETS)
esis1: $(ESIS1TARGETS)
xml: $(XMLTARGETS)
ESISTOOLS= $(TOOLSDIR)/sgmlconv/esistools.py
$(ESISTARGETS): $(LATEX2ESIS) $(DOCFIXER) $(ESISTOOLS) $(CONVERSION)
$(ESIS1TARGETS): $(LATEX2ESIS) $(CONVERSION)
# This variant is easier to work with while debugging the conversion spec:
#$(ESISTARGETS): $(LATEX2ESIS) $(DOCFIXER) $(ESISTOOLS)
$(XMLTARGETS): $(ESIS2ML)
.SUFFIXES: .esis .esis1 .tex .xml
.tex.esis1:
$(LATEX2ESIS) $(L2EFLAGS) $< $@
.esis1.esis:
$(DOCFIXER) $< $@
.esis.xml:
$(ESIS2ML) --xml $< $@
clean:
rm -f *.esis *.esis1
clobber: clean
rm -f *.xml

198
doc/tools/support.py Normal file
View File

@ -0,0 +1,198 @@
"""Miscellaneous support code shared by some of the tool scripts.
This includes option parsing code, HTML formatting code, and a couple of
useful helpers.
"""
__version__ = '$Revision$'
import getopt
import os.path
import sys
class Options:
__short_args = "a:c:ho:"
__long_args = [
# script controls
"columns=", "help", "output=",
# content components
"address=", "iconserver=", "favicon=",
"title=", "uplink=", "uptitle="]
outputfile = "-"
columns = 1
letters = 0
uplink = "index.html"
uptitle = "Python Documentation Index"
favicon = None
# The "Aesop Meta Tag" is poorly described, and may only be used
# by the Aesop search engine (www.aesop.com), but doesn't hurt.
#
# There are a number of values this may take to roughly categorize
# a page. A page should be marked according to its primary
# category. Known values are:
# 'personal' -- personal-info
# 'information' -- information
# 'interactive' -- interactive media
# 'multimedia' -- multimedia presenetation (non-sales)
# 'sales' -- sales material
# 'links' -- links to other information pages
#
# Setting the aesop_type value to one of these strings will cause
# get_header() to add the appropriate <meta> tag to the <head>.
#
aesop_type = None
def __init__(self):
self.args = []
self.variables = {"address": "",
"iconserver": "icons",
"imgtype": "gif",
"title": "Global Module Index",
}
def add_args(self, short=None, long=None):
if short:
self.__short_args = self.__short_args + short
if long:
self.__long_args = self.__long_args + long
def parse(self, args):
try:
opts, args = getopt.getopt(args, self.__short_args,
self.__long_args)
except getopt.error:
sys.stdout = sys.stderr
self.usage()
sys.exit(2)
self.args = self.args + args
for opt, val in opts:
if opt in ("-a", "--address"):
val = val.strip()
if val:
val = "<address>\n%s\n</address>\n" % val
self.variables["address"] = val
elif opt in ("-h", "--help"):
self.usage()
sys.exit()
elif opt in ("-o", "--output"):
self.outputfile = val
elif opt in ("-c", "--columns"):
self.columns = int(val)
elif opt == "--title":
self.variables["title"] = val.strip()
elif opt == "--uplink":
self.uplink = val.strip()
elif opt == "--uptitle":
self.uptitle = val.strip()
elif opt == "--iconserver":
self.variables["iconserver"] = val.strip() or "."
elif opt == "--favicon":
self.favicon = val.strip()
else:
self.handle_option(opt, val)
if self.uplink and self.uptitle:
self.variables["uplinkalt"] = "up"
self.variables["uplinkicon"] = "up"
else:
self.variables["uplinkalt"] = ""
self.variables["uplinkicon"] = "blank"
self.variables["uplink"] = self.uplink
self.variables["uptitle"] = self.uptitle
def handle_option(self, opt, val):
raise getopt.error("option %s not recognized" % opt)
def get_header(self):
s = HEAD % self.variables
if self.uplink:
if self.uptitle:
link = ('<link rel="up" href="%s" title="%s">\n '
'<link rel="start" href="%s" title="%s">'
% (self.uplink, self.uptitle,
self.uplink, self.uptitle))
else:
link = ('<link rel="up" href="%s">\n '
'<link rel="start" href="%s">'
% (self.uplink, self.uplink))
repl = " %s\n</head>" % link
s = s.replace("</head>", repl, 1)
if self.aesop_type:
meta = '<meta name="aesop" content="%s">\n ' % self.aesop_type
# Insert this in the middle of the head that's been
# generated so far, keeping <meta> and <link> elements in
# neat groups:
s = s.replace("<link ", meta + "<link ", 1)
if self.favicon:
ext = os.path.splitext(self.favicon)[1]
if ext in (".gif", ".png"):
type = ' type="image/%s"' % ext[1:]
else:
type = ''
link = ('<link rel="SHORTCUT ICON" href="%s"%s>\n '
% (self.favicon, type))
s = s.replace("<link ", link + "<link ", 1)
return s
def get_footer(self):
return TAIL % self.variables
def get_output_file(self, filename=None):
if filename is None:
filename = self.outputfile
if filename == "-":
return sys.stdout
else:
return open(filename, "w")
NAVIGATION = '''\
<div class="navigation">
<table width="100%%" cellpadding="0" cellspacing="2">
<tr>
<td><img width="32" height="32" align="bottom" border="0" alt=""
src="%(iconserver)s/blank.%(imgtype)s"></td>
<td><a href="%(uplink)s"
title="%(uptitle)s"><img width="32" height="32" align="bottom" border="0"
alt="%(uplinkalt)s"
src="%(iconserver)s/%(uplinkicon)s.%(imgtype)s"></a></td>
<td><img width="32" height="32" align="bottom" border="0" alt=""
src="%(iconserver)s/blank.%(imgtype)s"></td>
<td align="center" width="100%%">%(title)s</td>
<td><img width="32" height="32" align="bottom" border="0" alt=""
src="%(iconserver)s/blank.%(imgtype)s"></td>
<td><img width="32" height="32" align="bottom" border="0" alt=""
src="%(iconserver)s/blank.%(imgtype)s"></td>
<td><img width="32" height="32" align="bottom" border="0" alt=""
src="%(iconserver)s/blank.%(imgtype)s"></td>
</tr></table>
<b class="navlabel">Up:</b> <span class="sectref"><a href="%(uplink)s"
title="%(uptitle)s">%(uptitle)s</A></span>
<br></div>
'''
HEAD = '''\
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>%(title)s</title>
<meta name="description" content="%(title)s">
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<link rel="STYLESHEET" href="lib/lib.css">
</head>
<body>
''' + NAVIGATION + '''\
<hr>
<h2>%(title)s</h2>
'''
TAIL = "<hr>\n" + NAVIGATION + '''\
%(address)s</body>
</html>
'''

View File

@ -58,7 +58,7 @@ def parse_toc(fp, bigpart=None):
if m:
stype, snum, title, pageno = m.group(1, 2, 3, 4)
title = clean_title(title)
entry = (stype, snum, title, string.atoi(pageno), [])
entry = (stype, snum, title, int(pageno), [])
if stype == level:
toc.append(entry)
else:
@ -99,7 +99,7 @@ def clean_title(title):
pos = start + 1
else:
break
title = string.translate(title, title_trans, "{}")
title = title.translate(title_trans, "{}")
return title

View File

@ -0,0 +1,94 @@
#! /usr/bin/env python
"""\
This script prints out a list of undocumented symbols found in
Python include files, prefixed by their tag kind.
Pass Python's include files to ctags, parse the output into a
dictionary mapping symbol names to tag kinds.
Then, the .tex files from Python docs are read into a giant string.
Finally all symbols not found in the docs are written to standard
output, prefixed with their tag kind.
"""
# Which kind of tags do we need?
TAG_KINDS = "dpst"
# Doc sections to use
DOCSECTIONS = ["api"]# ["api", "ext"]
# Only print symbols starting with this prefix,
# to get all symbols, use an empty string
PREFIXES = ("Py", "PY")
INCLUDEPATTERN = "*.h"
# end of customization section
# Tested with EXUBERANT CTAGS
# see http://ctags.sourceforge.net
#
# ctags fields are separated by tabs.
# The first field is the name, the last field the type:
# d macro definitions (and #undef names)
# e enumerators
# f function definitions
# g enumeration names
# m class, struct, or union members
# n namespaces
# p function prototypes and declarations
# s structure names
# t typedefs
# u union names
# v variable definitions
# x extern and forward variable declarations
import os, glob, re, sys
def findnames(file, prefixes=()):
names = {}
for line in file.xreadlines():
if line[0] == '!':
continue
fields = line.split()
name, tag = fields[0], fields[-1]
if tag == 'd' and name.endswith('_H'):
continue
if prefixes:
sw = name.startswith
for prefix in prefixes:
if sw(prefix):
names[name] = tag
else:
names[name] = tag
return names
def print_undoc_symbols(prefix, docdir, incdir):
docs = []
for sect in DOCSECTIONS:
for file in glob.glob(os.path.join(docdir, sect, "*.tex")):
docs.append(open(file).read())
docs = "\n".join(docs)
incfiles = os.path.join(incdir, INCLUDEPATTERN)
fp = os.popen("ctags -IDL_IMPORT --c-types=%s -f - %s"
% (TAG_KINDS, incfiles))
dict = findnames(fp, prefix)
names = dict.keys()
names.sort()
for name in names:
if not re.search("%s\\W" % name, docs):
print dict[name], name
if __name__ == '__main__':
srcdir = os.path.dirname(sys.argv[0])
incdir = os.path.normpath(os.path.join(srcdir, "../../Include"))
docdir = os.path.normpath(os.path.join(srcdir, ".."))
print_undoc_symbols(PREFIXES, docdir, incdir)

31
doc/tools/update-docs.sh Executable file
View File

@ -0,0 +1,31 @@
#! /bin/sh
# Script which installs a development snapshot of the documentation
# into the development website.
#
# The push-docs.sh script pushes this to the server when needed
# and removes it when done.
if [ -z "$HOME" ] ; then
HOME=`grep fdrake /etc/passwd | sed 's|^.*:\([^:]*\):[^:]*$|\1|'`
export HOME
fi
DOCTYPE="$1"
UPDATES="$HOME/tmp/$2"
TMPDIR="$$-docs"
cd /ftp/ftp.python.org/pub/www.python.org/dev/doc/ || exit $?
mkdir $TMPDIR || exit $?
cd $TMPDIR || exit $?
(bzip2 -dc "$UPDATES" | tar xf -) || exit $?
cd .. || exit $?
if [ -d $DOCTYPE ] ; then
mv $DOCTYPE $DOCTYPE-temp
fi
mv $TMPDIR/Python-Docs-* $DOCTYPE
rmdir $TMPDIR
rm -rf $DOCTYPE-temp || exit $?
rm "$UPDATES" || exit $?

2
doc/tools/whichlibs Executable file
View File

@ -0,0 +1,2 @@
#!/bin/sh
sed -n 's%^\\input{\(lib[a-zA-Z0-9_]*\)}.*%../lib/\1.tex%p' ../lib/lib.tex