diff options
Diffstat (limited to 'sandbox/tibs')
44 files changed, 0 insertions, 8060 deletions
diff --git a/sandbox/tibs/pysource/__init__.py b/sandbox/tibs/pysource/__init__.py deleted file mode 100755 index 66239ca01..000000000 --- a/sandbox/tibs/pysource/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Extract information from Python files, for DOCUTILS/reST purposes - -:Author: Tibs <tibs@tibsnjoan.co.uk> -:Version: 0.1 -""" diff --git a/sandbox/tibs/pysource/buildhtml.py b/sandbox/tibs/pysource/buildhtml.py deleted file mode 100755 index f620a7a02..000000000 --- a/sandbox/tibs/pysource/buildhtml.py +++ /dev/null @@ -1,354 +0,0 @@ -"""Convenience mechanisms for writing HTML - -Similar in concept to buildtree.py (qv), but very different in the details. -""" - -import sys -import string - -__docformat__ = "reST" - -EMPTY = ["br","hr"] -"""Elements that do not (may not) have content. -""" - -INLINE = ["em","strong","samp","code","tt","text"] -"""Elements that may occur 'inline' - within a paragraph, etc. - -Note that we include the 'pseudo-element' "text". -""" - -NEWLINE_AFTER = ["html","head","body","table","address"] - -LISTS = ["ol","ul","dl"] - - -# ---------------------------------------------------------------------- -class BuildHTML: - - def __init__(self,stream=None): - """Instantiate a BuildHTML instance. - - `stream` should be either something that "looks like" a file - instance (specifically, it has to have a "write" method), or - else `None` if we want to default to sys.stdout - """ - - self.stream = stream or sys.stdout - self.stack = [] - """A stack of tag names (e.g., ["html","body","h1","p"]) - """ - self.last = None - """The last element we were told to add to our output. - """ - self.fancy = 0 - - def write_doctype(self): - """Write out the DOCTYPE element at the start of the HTML file. - - For the moment, we don't provide any flexibility in this... - """ - self.stream.write('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01' - ' Transitional//EN"' - ' "http://www.w3.org/TR/html4/loose.dtd">\n') - - def finish(self): - """Call this to indicate we have finished. - - It will grumble if anything is left unclosed - i.e., if there - is still stuff on the internal stack. - """ - if len(self.stack) > 0: - raise ValueError,"Items still outstanding on stack: %s"%\ - self._stack_as_string() - - def _maybe_write_newline(self,tag,before=1): - """Decide whether to write a newline before or after an element. - """ - if before: - if tag not in INLINE: - self.stream.write("\n") - else: - if tag in NEWLINE_AFTER: - self.stream.write("\n") - - def add(self,tag,*args,**keywords): - """Write an HTML element at the current level. - - For instance:: - - build.add("em","Some simple text.") - - If `tag` is "text" then it will automagically be converted - to ordinary inline text (even though there is no such HTML - element). - - Otherwise, this produces (for instance):: - - <em>Some simple text.</em> - - See `write` (which this uses) for more details of the arguments. - """ - self._maybe_write_newline(tag) - self.stream.write(self.element(tag,*args,**keywords)) - self.last = "/"+tag - - def start(self,tag,*args,**keywords): - """Write out the start of an HTML element, and start a new level. - - `tag` should be the name of an HTML element (a tag), or "comment". - - For instance:: - - build.start("li","some text") - - might cause:: - - <li>some text - - to be written out. Note that the element's closing tag is *not* - written out - see `end()` for that. - - If `args` are given, they are assumed to be (things that resolve - to) more text elements (i.e., strings). For instance:: - - build.start("li","some text", - build.element("strong","and emphasis"), - "and plain text again") - - See `write` (which this uses) for more details of the use of the - `tag` and `keywords` arguments. - """ - if tag in EMPTY: - raise ValueError,"Cannot start an 'empty' element (%s)"%tag - elif tag == "text": - raise ValueError,"Cannot start 'text'" - elif tag == "html" and len(self.stack) > 0: - raise ValueError,\ - "Cannot insert 'html' except at root of stack" - - self._maybe_write_newline(tag,before=1) - self.stream.write(self.start_tag(tag,**keywords)) - content = self._content(tag,args) - if content: - self.stream.write(content) - self._stack_add(tag) - self.last = tag - - def end(self,tag,*args): - """Write out the end of an HTML element, and finish the current level. - - `tag` should be the name of an HTML element (a tag), or "comment". - - For instance:: - - build.end("ul") - - Otherwise, for the moment at least, the `tag` being ended - must be the last tag that was begun (in the future, we *might* - support automatic "unrolling" of the stack, but not at the - moment). - - NB: if `args` are given, they will also be treated as closing - tags, in order - thus, for example:: - - build.end("td","tr","table") - - is exactly equivalent to:: - - build.end("td") - build.end("tr") - build.end("table") - - (Hmm - I'm not sure if that last is a good idea. Still, I *do* - use it for that specific instance, which is a relatively common - thing to want to do, and it does save "wasting" two fairly - uninteresting lines of code.) - """ - if tag in EMPTY: - raise ValueError,"Cannot start an 'empty' element (%s)"%tag - - if tag == "text": - raise ValueError,"Cannot end 'text'" - - self._stack_remove(tag) - self.stream.write(self.end_tag(tag)) - self._maybe_write_newline(tag,before=0) - - if args: - for item in args: - self.end(item) - self.last = "/"+args[-1] - else: - self.last = "/"+tag - - def start_tag(self,tag,**keywords): - """Construct and return a start tag. - - `tag` should be the name of an HTML element (a tag) - - `tag` may not be "text". - - `keywords` should be attributes for the tag. - """ - if tag == "comment": - return "<!-- " - - str = "<%s"%tag - if keywords: - keys = keywords.keys() - keys.sort() - for key in keys: - value = self.escape(keywords[key]) - str += " %s='%s'"%(key,value) - return str + ">" - - def end_tag(self,tag): - """Construct and return an end tag. - - `tag` should be the name of an HTML element (a tag) - - `tag` may not be "text". - """ - if tag == "comment": - return " -->" - else: - return "</%s>"%tag - - def element(self,tag,*args,**keywords): - """Construct and return a complete HTML element. - - `tag` should be the name of an HTML element (a tag), or "text". - - If `tag` is "text" then `keywords` is ignored, and the result - of concatenating `args` is returned. - - Otherwise: - - - an opening tag is composed from `tag` and `keywords` - (see `start_tag()`) - - the result of concatentating `args` is appended to that - - a closing tag (see `end_tag()`) is appended to that - - and the result is returned. - - Within `args`, non-strings are coerced to their representations. - """ - content = self._content(tag,args) - if tag == "text": - return content - else: - return self.start_tag(tag,**keywords) + content + \ - self.end_tag(tag) - - - def _content(self,tag,args): - """Return the *content* of an element. - - `tag` is not currently used, but *might* be useful later on? - """ - content = "" - if args: - for item in args: - if type(item) == type(""): - content += item - else: - content += `item` - return content - - def escape(self,text): - """Return `text` as valid HTML - - (that is, with any "special" characters escaped) - """ - # Hmm - paranoia, just in case - if type(text) != type(""): return text - - text = string.replace(text, "&", "&") - text = string.replace(text, "<", "<") - text = string.replace(text, '"', """) - text = string.replace(text, ">", ">") - - if self.fancy: - text = string.replace(text, " ", "°") - text = string.replace(text, "\n", "¶\n") - return text - - def last_tag(self): - """Return the last element we were asked to add to our output. - - Note that if we just closed element "XX" (for instance), then - we will return "/XX". - """ - return self.last - - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # Stack queries - - def _in_list(self): - """Are we *immediately* within a list - - (i.e., the first child element of a list) - """ - return self.stack[-1] in LISTS - - - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # Stack maintenance - - def _stack_ends(self,name): - """Return true if the stack ends with the named entity. - """ - return self.stack[-1] == name - - def _stack_add(self,name): - """Add a new level to the stack. - """ - self.stack.append(name) - - def _stack_remove(self,name): - """Remove the last level from the stack - - (but only if it is of the right sort). - """ - if len(self.stack) == 0: - raise ValueError,"Cannot end %s - nothing outstanding to end"%\ - (name) - if name != self.stack[-1]: - raise ValueError,"Cannot end %s - last thing begun was %s"%\ - (name,self.stack[-1]) - del self.stack[-1] - - def _stack_as_string(self): - names = [] - for name in self.stack: - names.append(name) - return string.join(names,",") - - -# ---------------------------------------------------------------------- -if __name__ == "__main__": - build = BuildHTML() - - print "Building a page" - build.start("html") - build.start("body") - build.add("h1","Fred") - build.start("p") - build.add("text","This is some text.") - build.add("strong","Really.") - build.start("p","Another paragraph") - build.end("body") - build.end("html") - build.finish() - - print - print "Building a broken page" - try: - build.start("html") - build.start("body") - build.add("h1","Fred") - build.start("p") - build.finish() - except ValueError,detail: - print "ValueError:",detail diff --git a/sandbox/tibs/pysource/buildtree.py b/sandbox/tibs/pysource/buildtree.py deleted file mode 100755 index 977226f8d..000000000 --- a/sandbox/tibs/pysource/buildtree.py +++ /dev/null @@ -1,403 +0,0 @@ -"""This module explores a different approach to building doctree elements... - -The "normal" way of building a DOCUTILS tree involves, well, building a tree -structure. So one might do:: - - section = docutils.nodes.section() - section += docutils.nodes.title(text="Title") - para = docutils.nodes.paragraph() - section += para - para += docutils.nodes.Text("Some ") - para += docutils.nodes.strong(text="strong text.") - -That's all very nice, if one is *thinking* in terms of a tree structure, -but it is not, for me, a very natural way to construct a text. - - (OK - I *know* in practice one would also have imported `paragraph`, - etc., from `docutils.nodes`, but that is not my point.) - -This module allows one to use a more LaTex style of construction, with -begin and end delimitors for DOCUTILS nodes. Thus the above example becomes:: - - build.start("section") - build.add("title","Title") - build.start("paragraph","Some ") - build.add("strong","Strong text.") - build.end("section") - -(As a convenience, paragraphs are automatically ended.) - -A slightly shorter, and possibly more obfuscated, way of writing this -would be:: - - build.start("section",build.make("title","Title") - build.start("paragraph","Some ",build.make("strong","Strong text.")) - build.end("section") - -Sometimes I think that sort of approach makes more sense, sometimes not. -""" # we need a " to keep [X]Emacs Python mode happy. - -import string -import docutils.nodes -import docutils.utils - -__docformat__ = "reST" - - -# ---------------------------------------------------------------------- -class group(docutils.nodes.Element): - """Group is a way of grouping together elements. - - Compare it to HTML <div> and <span>, or to TeX (?check?) - \begingroup and \endgroup. - - It takes the special attribute `style`, which indicates what - sort of thing it is grouping - for instance, "docstring" or - "attributes". - - Although it (should be) supplied by the standard DOCUTILS tree, - reST itself does not use `group`. It is solely used by - extensions, such as ``pysource``. - - In the default HTML Writer, `group` renders invisibly - (that is, it has no effect at all on the formatted output). - """ - - pass - - -# ---------------------------------------------------------------------- -class BuildTree: - - def __init__(self, with_groups=1, root=None): - self.stack = [] - """A stack of tuples of the form ("classname",classinstance). - """ - - self.root = root - """A memory of the first item on the stack (notionally, the - "document") - we need this because if we `start` a document, - fill it up, and then `end` it, that final `end` will remove - the appropriate instance from the stack, leaving no record. - Thus this is that record. - """ - if root is not None: - self._stack_add(root) - - self.with_groups = with_groups - - def finish(self): - """Call this to indicate we have finished. - - It will grumble if anything is left unclosed, but will - return the "root" instance of the DOCUTILS tree we've been - building if all is well... - """ - if len(self.stack) > 0: - raise ValueError,"Items still outstanding on stack: %s"%\ - self._stack_as_string() - else: - return self.root - - def add(self,thing,*args,**keywords): - """Add a `thing` DOCUTILS node at the current level. - - For instance:: - - build.add("paragraph","Some simple text.") - - If `thing` is "text" then it will automagically be converted - to "Text" (this makes life easier for the user, as all of the - other DOCUTILS node classes they are likely to use start with a - lowercase letter, and "Text" is the sole exception). - - See `make` (which this uses) for more details of the arguments. - """ - if thing == "group" and not self.with_groups: - return - - instance = self.make(thing,*args,**keywords) - self._stack_append(instance) - - def addsubtree(self,subtree): - """Add a DOCUTILS subtree to the current item. - """ - self._stack_append(subtree) - - def current(self): - """Return the "current" item. - - That is, return the item to which `add()` will add DOCUTILS nodes. - """ - return self._stack_current() - - def start(self,thing,*args,**keywords): - """Add a `thing` DOCUTILS node, starting a new level. - - `thing` should be either the name of a docutils.nodes class, or - else a class itself. - - If `thing` is "text" then it will automagically be converted - to "Text" (this makes life easier for the user, as all of the - other DOCUTILS node classes they are likely to use start with a - lowercase letter, and "Text" is the sole exception). - - For instance:: - - build.start("bullet_list") - - As a convenience, if `thing` is a paragraph, and if the current - item is another paragraph, this method will end the old paragraph - before starting the new. - - Note that if `thing` is "document", some extra magic is worked - internally. If the keywords `warninglevel` and `errorlevel` are - given, they will be passed to a docutils.utils.Reporter instance, - as well as being passed down to the `document` class's initialiser. - - See `make` (which this uses) for more details of the arguments. - """ - name = self._nameof(thing) - - if name == "group" and not self.with_groups: - return - - if name == "paragraph" and self._stack_ends("paragraph"): - self.end("paragraph") - - if name == "document": - if self.root: - return - if len(self.stack) > 0: - raise ValueError,\ - "Cannot insert 'document' except at root of stack" - warninglevel = keywords.get("warninglevel",2) - errorlevel = keywords.get("errorlevel",4) - reporter = docutils.utils.Reporter('fubar', warninglevel, - errorlevel) - instance = docutils.nodes.document(reporter,"en") - else: - instance = self.make(thing,*args,**keywords) - - if len(self.stack) == 0: - self.root = instance - else: - self._stack_append(instance) - - self._stack_add(instance) - - def end(self,thing): - """End the level started below a `thing` DOCUTILS node. - - `thing` should be either the name of a docutils.nodes class, or - else a class itself. - - For instance:: - - build.end("bullet_list") - - As a convenience, if the last item constructed was actually - a paragraph, and `thing` is the container for said paragraph, - then the paragraph will be automatically ended. - - Otherwise, for the moment at least, the `thing` being ended - must be the last thing that was begun (in the future, we *might* - support automatic "unrolling" of the stack, but not at the - moment). - """ - name = self._nameof(thing) - - if thing == "group" and not self.with_groups: - return - - if self._stack_ends("paragraph") and name != "paragraph": - self.end("paragraph") - - self._stack_remove(name) - - def make(self,thing,*args,**keywords): - """Return an instance of `docutils.nodes.thing` - - Attempts to regularise the initialisation of putting initial - text into an Element and a TextElement... - - `thing` should be either the name of a docutils.nodes class, or - else a class itself (so, for instance, one might call - ``build.make("paragraph")`` or - ``build.make(docutils.nodes.paragraph)``), - or else None. - - If `thing` is "text" then it will automagically be converted - to "Text" (this makes life easier for the user, as all of the - other DOCUTILS node classes they are likely to use start with a - lowercase letter, and "Text" is the sole exception). - - If `thing` is an Element subclass, then the arguments are just - passed straight through - any *args list is taken to be children - for the element (strings are coerced to Text instances), and any - **keywords are taken as attributes. - - If `thing` is an TextElement subclass, then if the first - item in *args is a string, it is passed down as the `text` - parameter. Any remaining items from *args are used as child - nodes, and any **keywords as attributes. - - If `thing` is a Text subclass, then a single argument is expected - within *args, which must be a string, to be used as the Text's - content. - - For instance:: - - n1 = build.make("paragraph","Some ", - build.make("emphasis","text"), - ".",align="center") - n2 = build.make(None,"Just plain text") - """ - - #print "make: %s, %s, %s"%(thing,args,keywords) - - # Temporary special case - since group is not (yet) in docutils.nodes... - if thing == "group": - thing = group - - if thing == None: - dps_class = docutils.nodes.Text - elif type(thing) == type(""): - if thing == "text": - thing = "Text" - try: - dps_class = getattr(docutils.nodes,thing) - except AttributeError: - raise ValueError,"docutils.nodes does not define '%s'"%thing - else: - dps_class = thing - - # NB: check for TextElement before checking for Element, - # since TextElement is itself a subclass of Element! - if issubclass(dps_class,docutils.nodes.TextElement): - # Force the use of the argument list as such, by insisting - # that the `rawsource` and `text` arguments are empty strings - args = self._convert_args(args) - dps_instance = dps_class("","",*args,**keywords) - elif issubclass(dps_class,docutils.nodes.Element): - # Force the use of the argument list as such, by insisting - # that the `rawsource` arguments is an empty string - args = self._convert_args(args) - dps_instance = dps_class("",*args,**keywords) - elif issubclass(dps_class,docutils.nodes.Text): - if len(args) > 1: - raise ValueError,\ - "Text subclass %s may only take one argument"%\ - self._nameof(thing) - elif len(args) == 1: - text = args[0] - else: - text = "" - if keywords: - raise ValueError,\ - "Text subclass %s cannot use keyword arguments"%\ - self._nameof(thing) - dps_instance = dps_class(text) - else: - raise ValueError,"%s is not an Element or TextElement"%\ - self._nameof(thing) - - #print " ",dps_instance - return dps_instance - - def _convert_args(self,args): - """Return the arguments, with strings converted to Texts. - """ - newargs = [] - for arg in args: - if type(arg) == type(""): - newargs.append(docutils.nodes.Text(arg)) - else: - newargs.append(arg) - return newargs - - def __getattr__(self,name): - """Return an appropriate DOCUTILS class, for instantiation. - """ - return getattr(docutils.nodes,name) - - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - def _nameof(self,thing): - if thing is None: - return "Text" - elif type(thing) == type(""): - return thing - else: - return thing.__name__ - - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # Stack maintenance - - def _stack_ends(self,name): - """Return true if the stack ends with the named entity. - """ - return self.stack[-1][0] == name - - def _stack_add(self,instance): - """Add a new level to the stack. - """ - self.stack.append((instance.__class__.__name__,instance)) - - def _stack_remove(self,name): - """Remove the last level from the stack - - (but only if it is of the right sort). - """ - if len(self.stack) == 0: - raise ValueError,"Cannot end %s - nothing outstanding to end"%\ - (name) - if name != self.stack[-1][0]: - raise ValueError,"Cannot end %s - last thing begun was %s"%\ - (name,self.stack[-1][0]) - del self.stack[-1] - - def _stack_append(self,instance): - """Append an instance to the last item on the stack. - """ - if len(self.stack) > 0: - self.stack[-1][1].append(instance) - else: - raise ValueError,"Cannot add %s to current level" \ - " - nothing current"%(instance.__class__.__name__) - - def _stack_current(self): - """Return the "current" element from the stack - - That is, the element to which we would append any new instances - with `_stack_append()` - """ - return self.stack[-1][1] - - def _stack_as_string(self): - names = [] - for name,inst in self.stack: - names.append(name) - return string.join(names,",") - - -# ---------------------------------------------------------------------- -if __name__ == "__main__": - build = BuildTree() - #print build.make("paragraph",text="fred") - #print build.paragraph(text="fred") - - print "Building a section" - build.start("section") - build.add("title","Fred") - build.start("paragraph") - build.add("text","This is some text.") - build.add("strong","Really.") - build.start("paragraph","Another paragraph") - build.end("section") - print build.finish() - - #print "Building a broken section" - #build.start("section") - #build.add("title","Fred") - #build.start("paragraph") - #print build.finish() diff --git a/sandbox/tibs/pysource/doc/readme.rtxt b/sandbox/tibs/pysource/doc/readme.rtxt deleted file mode 100644 index 5710eaa53..000000000 --- a/sandbox/tibs/pysource/doc/readme.rtxt +++ /dev/null @@ -1,189 +0,0 @@ -pysource -======== - -:Author: Tibs (tibs@tibsnjoan.co.uk) -:Date: $Date$ -:Revision: $Revision$ -:Version: 0.1 - -Ahem ----- -This file needs updating - unless this notice has disappeared, treat the -rest of the file as out-of-date... - -History -------- -2002-04: - -* Name changed from 'pydps' to 'pysource' (in line with a suggestion - made a while back on the Doc-SIG by David Goodger). - - It's not that that's a great name, just that it's better than 'pydps' - (especially now that 'dps' has become 'docutils'), and the rather more - obvious 'pydoc' is already taken. Any suggestions for a better name will - be gratefully received! - -* Converted from the old 'dps' and 'restructuredtext' packages to the - new 'docutils' package. - -* All previous history is elided with the change of name and first release... - -Obtaining the package ---------------------- -The latest version of 'pysource' may be found in the Docutils sandbox -(tarball_ and browsable_). - -.. _tarball: http://docutils.sourceforge.net/docutils-sandbox-snapshot.tgz -.. _browsable: http://docutils.sourceforge.net/sandbox/tibs/pysource/ - -I hope to keep a copy of the current "released" version at - - http://www.tibsnjoan.co.uk/reST/pysource.tgz - -Purpose -------- -This module provides code to - - * parse a Python file using the Python Compiler module, which is - standard with Python 2.2 and later, and available in the Tools - directory in earlier versions, - * extract relevant information, including docstrings, - * and ultimately produce (in the first instance) HTML documentation - therefrom. - -As a subsidiary capability, it can read a restructuredtext file and produce -HTML from that. - -There are obviously other tools which perform similar tasks - see the -accompanying file whythis.rtxt for some comparisons, and an -explanation of why I think it is worth developing this tool -independently. - - -*** TO HERE *** - -Usage ------ -The command ``python pysource/pysource.py --help`` gives the following -information:: - - """The command line interface to docutil's Python documentation extractor. - - Usage: ``pysource.py <switches> <inpath> [<outfile>]`` - - <inpath> is the path to a package or module. - - <outfile> is the path to the output file. If it's not given, then - output will be written to a file with the same name as the input - file, but defaulting to the current directory, and with extension - derived from the type of output: - - - show -> ``.show`` - - ast -> ``.ast`` - - xml -> ``.xml`` - - html -> ``.html`` - - pretty -> ``.pretty`` - - (unless --stdout is requested). The default is --html. - - Note that progress messages (and ``verb`` information) are written - to ``sys.stderr``. - """ - - <switches> are: - - -v, --verbose Report on progress in more detail - -q, --quiet Suppress normal progress messages - -t, --text The input file is a plain (text) reST file - -s, --show Output basic information about the input - -a, --ast Output a representation of the AST - -x, --xml Output an XML representation of the input - -h, --html Output an HTML representation of the input [default] - -p, --pretty Output a 'pretty' representation of the input - -d, --doctest Treat a reST file as doctest input. - -h, --help Show 'help' information - -n, --new Use David Goodger's HTML Writer (sort of) - --stdout Write output to stdout, instead of a file - -I recommend use of the ``--pretty`` option for gaining an understanding of the -DPS tree itself. - -Limitations and dependencies ----------------------------- -This is beta software, and is still, to some extent, a proof and exploration of -concept. - -The following limitations are obvious: - - * Its concept of a "package" is rather limited - it doesn't understand - sub-packages (i.e., it only copes with a "flat" directory structure). - * It only produces a single HTML file - a more sophisticated approach - is clearly needed, particularly for large packages (or even large - modules). - * It is not fully integrated with the Docutils HTML Writer, which it - should be using in preference to my own home-grown approach. - * The Docutils tree that it produces could use some improvement - in - particular the Python specific nodes need various design decisions - to be made. - -Also: - - * It doesn't throw away as much information as it should. - * It doesn't check all assignments for use of global values. - * It doesn't handle all Python entities that it should. - * The HTML it produces is pretty yuck, and is *designed* not - to look terribly nice (although you should remember not to - ask my opinion of the HTML output by pydoc). - * Autonumbered footnote resolution is done by the HTML writer, - which means that it will likely go wrong if it needs to do - anything with Python source that contains autonumbered - footnotes in docstrings(!). But it does work (I believe) - for .rtxt files. - * Various other Docutils tree transforms that should be applied - are not yet performed. - -Subtler things: - - * The ``--doctest`` mode just pretends that the whole file is - a single doctest string (i.e., just as if doctest had found - it as a docstring in a Python file). - - That's quite sensible, except that the current doctest doesn't - know that it should ignore literal blocks, and thus may find - apparent Python code where it shouldn't. - -It depends on: - - * The latest versions of Docutils, as of the - time it was uploaded (I generally track these fairly well, - so am normally using the latest versions whilst developing). - These should have been installed (using the setup scripts - they provide). - * Python 2.0 or above - * Tools/compiler for the current Python. For Pythons before - 2.2a4 (I think it was) this should be installed using the - setup script it provides (after that it comes as standard). - -I develop it with Python 2.1 on Windows/NT and with Python 2.2 on -Debian GNU/Linux. - -Refactoring warning -------------------- -It is my aim to refactor this code to follow David Goodger's - - Reader - Transformer - Writer - -model more closely than it currently does. And there is also lots of -tidying up to do (especially in `visit.py`). - -Interesting things to do ------------------------- -Run it over docutils/spec/reStructuredText.txt. - -Run it over docutils/docutils. - -Run it over pysource/visit.py. - -Run it over the standard string module, and compare the result with that -of ``pydoc``. - diff --git a/sandbox/tibs/pysource/doc/whythis.rtxt b/sandbox/tibs/pysource/doc/whythis.rtxt deleted file mode 100644 index 0307654a2..000000000 --- a/sandbox/tibs/pysource/doc/whythis.rtxt +++ /dev/null @@ -1,122 +0,0 @@ -Why pysource? -============= - -There is the question of why a new tool is needed to go with Docutils and -reStructuredText, when there are several existing tools which could have been -adopted. - -Perhaps the main reason is simply as a "proof of concept" - even if pysource -itself is not adopted as *the* tool for use in documenting Python source files -(and I certainly would not advocate that it be the only one, as other tools -present their own particular advantages), it *is* worth doing as a "proof of -concept" - as an examplar that it is possible to do the job. - -Three existing tools deserve particular consideration: - - - pydoc - - HappyDoc - - PyPaSax - -pydoc ------ - -pydoc is Ka-Ping Yee's classic application. It is now (as of Python 2.1) -included in the standard Python library, where it also provides the backbone -for the "help" command. - -pydoc uses introspection of "live" objects to determine information - that is, -it requires that the modules to be documented be imported. This can be a -limiting factor - sometimes it is not advisable to import a module, and -sometimes it is simply not possible. - -It provides output as HTML, and also has a rather output mode for use at -a terminal. - -.. note: - I've still to work out how to make pydoc work as a standalone tool - (i.e., at the terminal prompt) for documenting my own Python code - - for instance, doing ``pydoc.py pysource/visit.py``, which, at best, - doesn't seem to see any documentation! - -HappyDoc --------- - - http://happydoc.sourceforge.net/ - -HappyDoc is a seriously neat tool, and one of the longest standing in the -arena. It is a mature tool, and: - - - supports several forms of structured text within docstrings - notably - plain text, "raw" text, StructuredTextClassic and StructuredTextNG. - - - allows the extraction of documentation from comments as well as from - docstrings. - - - supports several forms of output - notably HTML, SGML and XML DocBook, - plain text, and Dia files. - - - has documented mechanisms for adding new import and export filters. - - - has sophisticated control over the actual details of export (HTML options, - single or multiple files, etc.) - -I would expect it to add support for reStructuredText in the near future. - -PyPaSax -------- - - http://www.logilab.org/pypasax/ - -PyPaSax is a Python module that uses the python parser to generate Sax2 events, -and thus enables the visualisation of a python document as an XML tree. This is -used at Logilab for documenting the source code of Narval. Logilab are also -working on an XSL transformation to generate XMI from the generated XML, which -should allow interface to UML tools. - -This is a very interesting approach to the problem of extracting information -from Python modules, but it is currently relatively "coarse grained" - it does -not, for instance, pay attention to docstrings. - -pysource --------- - -http://docutils.sourceforge.net/sandbox/pysource/ (or from the tarball_). - -.. _tarball: http://docutils.sourceforge.net/docutils-sandbox-snapshot.tgz - -pysource is intended as a proof-of-concept implementation of the concepts in -the Docutils PEPs. - -It parses Python files using the Compiler module, honouring the __docformat__ -module value to decide whether docstrings are in reStructuredText or plain -text. - -It uses Docutils to organise the information so gained, and the results of -parsing the Python code are integrated with the parsed docstring text into a -single Docutils nodes tree. - -The initial version of pysource supports the output of XML (which is a native -ability of Docutils itself), and HTML. Since it is intended that pysource -integrate closely with Docutils, additional output formats will occur as new -Writers are added to Docutils itself. - -See also --------- -pep-0256: Docstring Processing System Framework - - This includes a closer analysis of why pydoc is not directly suitable - for the purposes of Docutils. - -pep-0257: Docstring conventions - - This describes the high-level structure of docstrings. - -pep-0258: Docutils Design Specification - - This includes a description of what a tool such as pysource should - actually do - what docstrings to extract and from where, and how to - decide what format is used within said docstrings. - -pep-0287: reStructuredText Standard Docstring Format - - The PEP for reStructuredText itself. diff --git a/sandbox/tibs/pysource/group-notes.txt b/sandbox/tibs/pysource/group-notes.txt deleted file mode 100644 index 6c3f281f9..000000000 --- a/sandbox/tibs/pysource/group-notes.txt +++ /dev/null @@ -1,220 +0,0 @@ -Adding <group>, losing <package_section> and their ilk -====================================================== - -:Author: Tibs -:Date: 2001-11-18 - -Background ----------- -I am currently writing software that will take information from -Python source files, produce a DPS node tree therefrom, and allow -the user to generate HTML from that. - -My initial implementation produced a *variant* of the DPS node -tree, which contained many structures that related closely to the -information derived from Python - for instance, something like:: - - <py_class name="Fred"> - <docstring> - This is a very silly class. - <py_attr_list> - <py_attr name="jim"> - <py_method "name="method"> - -For various reasons, the (implicit) DTD wasn't shaping up very -like that proposed by David Goodger, so I asked about the -possibility of amending the "standard" DTD. This led to a -discussion of how the flow of information through a DPS processor -should actually work, with the result being David's diagram -[#diagram]_:: - - +--------+ +--------+ +------------+ +--------+ - | READER | --> | linker | --> | transforms | --> | WRITER | - +--------+ +--------+ +------------+ +--------+ - | TOC, index, | - | etc. (optional) | - +--------+ +--------+ - | PARSER | | filer | - +--------+ +--------+ - -David also made the point that, within this plan, the result of -the ``linker`` phase is a normal DPS tree, which can be -transformed with any of the "standard" transformation tools (for -instance, to resolve automatic footnotes), and then output with -any writer. - -Whilst David's diagram is not *quite* how I see the process, it's -close enough for this purpose. Thus pydps [#pydps]_ might be shown -as:: - - +--------+ +--------------+ +------------+ +---------+ - | READER | --> | transform.py | --> | transforms | --> | html.py | - +--------+ +--------------+ +------------+ +---------+ - | | - +----------+ +---------------+ - | visit.py | | buildhtml.py | - +----------+ +---------------+ - -The "READER" is implicit in the main utility (currently -``pydps.py``), and locates the relevant Python files. It then -uses ``visit.py`` to generate a tree representing the Python code -(the Python ``compiler`` module, standard with Python 2.2 and -above, but in ``Tools`` before that, is used). - -``transform.py`` (which, by David's diagrams, should maybe be -called ``link``) transforms that information into a proper DPS -node tree. At the time of writing, no transformations are done. - -Finally, HTML is output from the DPS node tree by ``html.py``. - -So, in summary: - - 1. ``transform.py`` generates a *normal* DPS tree. It doesn't - use any "odd" nodes (except <group> - but we'll discuss - that later on). This means that it should be possible to - plug in any other writer, and produce a different format as - output - a very significant advantage. - - 2. ``html.py`` expects to be given a *normal* DPS tree. This - means that it should be usable by any other utility that - also provides a normal DPS tree - again an advantage. - -The problem ------------ -But there is a clash in requirements here. Whilst it is very nice -to be able to represent the Python information as "normal" DPS -(guaranteeing that anyone can do useful things with it), there is -some need to transfer information that goes beyond that. There -are two main reasons for wanting to do this: - - * Data mining - * Presentation - -For the first, although DPS/reST/pydps is primarily about -producing human-viewable documentation, it might also be nice to -be able to extract parts of it automatically, for various -purposes - for instance, retrieve just the information about -which classes inherit from other. This information will, in part, -be obvious from the text chosen within the document (a title like -"Class Fred" might be taken to be useful, for instance!), but it -would be nice to give a bit more help. - -For the second, it's relatively difficult to produce better -layout for DPS Python documentation without more information to -work on. If one uses the (rather garish) default presentation -produced by pydps (and no, I'm not saying that's a *nice* -presentation, but it is the one I've got as an example), it is -clearly useful to be able to: - - 1. group together the package/class/method/etc title and its - full name/path/whatever - - 2. group together a method or function's signature and its - docstring - -David's original approach to this was to introduce a host of -Python specific tags into ``nodes.py`` [#nodes]_ - for instance:: - - package_section - module_section - ... - instance_attribute_section - ... - parameter_item - parameter_tuple - ... - package - module - class_attribute - -There are several problems with approach. Perhaps the most -serious is that *all* generic DPS writers need to understand -this host of elements that are only relevant to Python. Clearly, -someone writing a writer for other purposes may be reluctant to -go to this (to them) redundant effort. - -From my point of view, an immediate problem is that the set of -elements is not *quite* what I want - which means working towards -a set of patches for ``nodes.py`` and the relevant DTD, and -getting David to agree to them (well, that last is a useful -process to have in place, but still). Since I'm not likely to get -it right immediately, this is a repetitive process. - -Lastly, one might imagine someone from another programming -language domain adopting DPS/reST. One can expect them to be -frustrated if the set of constructs provided for Python doesn't -quite match the set of constructs required to describe their -language in a natural manner. - -Luckily (!), I have a counter proposal, which hopefully keeps the -baby and just loses the bath water. - -Groups and "style" ------------------- -The first thing that I realised was that, for convenience of -output at least, I wanted to be able to group elements together - -or, in terms of the DPS tree, insert an arbitrary "subroot" -within the tree to 'abstract' a branch of the tree. - -This is particularly useful for linking together the parts of the -text that deal with (for instance) attribution, or unusual -globals, without having to embed yet another section. - -There is, of course, precedent. HTML provides <div> and <span> - -one for "structural" elements, and one for inline elements (I -forget which is which), and TeX and LaTeX are well-known for -their use of grouping (e.g., the ``\begin{xxx}`` and -``\end{xxx}`` in LaTeX). - -I don't consider it worth making the <div>/<span> distinction in -the context of a tree - it is perfectly evident what is beingp -grouped from the elements themselves. - -Once one has a <group> element, it is natural to annotate it with -*what* it is a group of/for. I chose the arbitrary term "style" - -partly because it is not used in HTML/XML for any purpose I am -aware of. - -And once one has the "style" attribute, it becomes possible to -use it elsewhere - most notably in <section> elements, saving the -need for a myriad of different sections for different purposes. - -In these instances, it is perhaps acting more like the "class" -attribute in HTML - indicating, to some extent, meaning, but -aimed mostly at presentation (via CSS). - -The other obvious place to use it is in the automatically -generated text for things like names, where (at least -pre-"combing"/transformation) one is "pretending" to have -assigned a role (just as a person might in a docstring) (but see -[#style]_). - -Summary -------- -Current DPS defines many element types for use in Python code -representation. - -However, there are major advantages in only using the "simple" -DPS nodes for all purposes. - -This becomes simple and practical given a single extra, general -purpose, element: <group>. - -Furthermore, adding a standard attribute called "style" (or -perhaps "role" - see [#style]_) seems to fulfil any other -outstanding requirements. - -References ----------- -.. [#diagram] in email by David Goodger to Doc-SIG, dated - 21 September 2001 04:31, "Re: [Doc-SIG] DPS components". - -.. [#style] Hmm - maybe "style" should be "role", to match - with the way that a ``:role:`of something``` gets handled... - -.. [#pydps] Normally to be found at - http://www.tibsnjoan.co.uk/reST/pydps.tgz, - although note that this is not currently up-to-date. - -.. [#nodes] dps/dps/nodes.py in the DPS distribution - (``dps.nodes`` if one is importing it). diff --git a/sandbox/tibs/pysource/html.py b/sandbox/tibs/pysource/html.py deleted file mode 100755 index 398151a0e..000000000 --- a/sandbox/tibs/pysource/html.py +++ /dev/null @@ -1,1163 +0,0 @@ -"""Output DOCUTILS nodes as HTML. - -This is a quick-and-dirty approach to writing out HTML derived from -a DOCUTILS node tree. It maintains a minimum of state, and doesn't attempt -any particular intelligence about the tree structure. - - Note: for debugging purposes some HTML elements are output with - "style" attributes - this is so I can track which elements were - written for what purpose, and is temporary. - - (Use of "class" attributes to make CSS usage easier is a - separate consideration, to be made later on.) - -Use of this should ultimately be replaced by use of David's new mechanisms -from the docutils module - but they didn't exist when I started, so we'll live -with this for a little longer. -""" - -import time -import buildhtml - -__docformat__ = "reST" - -class HTMLError(Exception): - pass - - -# ---------------------------------------------------------------------- -class Writer: - """Encapsulate the HTML writing stuff in a class - - - it makes it easier to handle values we want to keep around - """ - - colours = {"Information": "#FF0000", - "Warning" : "#FF0000", - "Error" : "#FF0000", - "Fatal" : "#FF0000", - "WarningBG" : "Silver", # (was "lightgrey" or #DDDDDD) - "default" : "#FFFFCC", - } - """Colours to use to distinguish various contexts - - Note that the HTML4 spec defines the following colours: - - * Black = "#000000" - * Silver = "#C0C0C0" - * Gray = "#808080" - * White = "#FFFFFF" - * Maroon = "#800000" - * Red = "#FF0000" - * Purple = "#800080" - * Fuchsia = "#FF00FF" - * Green = "#008000" - * Lime = "#00FF00" - * Olive = "#808000" - * Yellow = "#FFFF00" - * Navy = "#000080" - * Blue = "#0000FF" - * Teal = "#008080" - * Aqua = "#00FFFF" - """ #" - - role_text = {"package" : "Package", - "module" : "Module", - "class" : "Class", - "method" : "Method", - "function" : "Function", - "module_attribute" : "Module attribute", - "class_attribute" : "Class attribute", - "instance_attribute": "Instance attribute", - "variable" : "Name", - "parameter" : "Argument", - "type" : "Type", - "exception_class" : "Exception class", - "exception" : "Exception", - "warning_class" : "Warning class", - "warning" : "Warning"} - """If an interpreted text has a role, we want to write that role - out. We thus need a dictionary to relate role names to the text - to be written out. - """ - - fancy = 0 - """Do we want fancy presentation?""" - - want_contents = 0 - """Do we *want* contents for this document? - """ - - language = "en" - """The language that we believe our document to be - destined for. - """ - - showwarnings = 1 - """Should we show warnings, or try to continue silently? - """ - - showinforms = 1 - """Should we show informational messages, or ignore them? - """ - - visible_targets = 0 - """Show link target names - """ - - visible_links = 0 - """Show link references (although not all of the possible links - we produce). - """ - - def __init__(self): - - # The current document tree - unset it here just in case... - self.document = None - - # Our HTML builder - ditto - self.html = None - - # Our method cache - we seed it with the entry for "#text" - # because we can't deduce the method name from that tag - # ("write_#text" is not a valid Python name!) - self.method_cache = {"#text":self.write_text} - - def __call__(self,document,stream): - """Output an HTML representation of `document` to `stream`. - - Arguments: - - * document -- the DOCUTILS tree we are to output as HTML - * stream -- something like a File, with a write method - """ - - self.document = document - self.html = buildhtml.BuildHTML(stream) - - # Reset things (i.e., so we can be called more than once) - - # The header level to use for <section> titles - # (i.e., <h2>, <h3>, etc). - self.level = 0 - - # Have we output Contents for this document? - self.got_contents = 0 - - # Are we within the body of a field list item? - self.infield = 0 - - # Or in a paragraph? (note - only in the sense that the DOCUTILS - # tree says that we're in a paragraph) - self.in_paragraph = 0 - - # Footnote autonumbers - self.auto_footnote = 0 - """The current auto-numbered footnote's number. - This will be stored as attribute "auto-index" on - the footnote itself, by `find_auto_footnotes()`. - """ - self.auto_footnote_names = {} - """A dictionary linking an auto-numbered footnote label - to the corresponding (generated) footnote number. This - is populated by `find_auto_footnotes()`. - """ - self.auto_footnote_list = [] - """A list of the auto-numbered footnote numbers that - are used for non-named footnotes. This list is then - used to populate the [#]_ footnote references. It is - populated by `find_auto_footnotes()`. - """ - self.auto_footnote_index = 0 - """An index into `self.auto_footnote_list`. - """ - self.auto_footnote_target = 0 - """This is used to record the numbering for the "link" end - of footnotes - i.e., the number for autonumbered references. - """ - - self.find_auto_footnotes(document) - - # Table location - self.in_table_header = 0 - self.in_table_body = 0 - - # And now down to work... - self.html.write_doctype() - self.html.start("html") - - # Hmm - have we been handed a "document" rooted tree, - # or a DOM-like tree that has "document" as its single child? - if document.tagname == "document": - self.write_html(document,stream) - else: - for element in document: - self.write_html(element,stream) - - self.html.end("html") - self.html.finish() - - def find_auto_footnotes(self,element): - """Locate and number autonumbered footnotes... - """ - - if element.tagname == "#text": - return - elif element.tagname == "footnote": - # This is a footnote body - it is the footnote bodies - # that determine their order and numbering... - name = auto = None - if element.hasattr("name"): - name = element["name"] - if element.hasattr("auto"): - auto = element["auto"] - self.auto_footnote += 1 - element["auto-index"] = self.auto_footnote - if auto: - if name: - if not self.auto_footnote_names.has_key(name): - self.auto_footnote_names[name] = self.auto_footnote - else: - # Well, what should we do? - # Removing it seems the best bet... - del self.auto_footnote_names[name] - else: - self.auto_footnote_list.append(self.auto_footnote) - - for node in element: - self.find_auto_footnotes(node) - - def write_document(self,element,stream): - document_is_section = 0 - if element.hasattr("title"): - title = self.html.escape(element["title"]) - else: - firstchild = element[0] - if firstchild.tagname == "title": - title = self.html.escape(firstchild.astext()) - document_is_section = 1 - elif firstchild.hasattr("title"): - title = self.html.escape(firstchild["title"]) - else: - title = "Document produced by pysource" - - self.html.start("head") - self.html.add("title",title) - self.html.end("head") - - self.html.start("body") - if document_is_section: - # There is no internal <section> - instead we just - # have a <document> whose first element is <title> - # So, given that, pretend we ARE a section... - self.write_section(element,stream) - else: - for node in element: - self.write_html(node,stream) - - self.html.add("hr") - self.html.start("p") - self.html.add("em","Automatically generated by ", - self.html.element("code","pysource"), - " on %s\n"%time.ctime(time.time())) - self.html.end("p") - self.html.end("body") - - def write_text(self,element,stream): - """Write out plain text. - """ - self.html.add("text",self.html.escape(element.astext())) - - def write_html(self,element,stream): - """Write out the HTML representation of `element` on `stream`. - """ - - name = element.tagname - try: - method = self.method_cache[name] - except KeyError: - method = getattr(self,"write_%s"%name,self.write_unknown) - self.method_cache[name] = method - method(element,stream) - - def write_unknown(self,element,stream): - """Write out an element which we don't recognise. - """ - - self.html.add("p",self.html.element("comment","just a spacer")) - - self.html.start("font","<%s"%element.tagname,color="red") - for name,value in element.attlist(): - self.html.add("text"," %s='%s'"%(name,self.html.escape(value))) - self.html.add("text",">") - self.html.end("font") - - for node in element: - self.write_html(node,stream) - - self.html.add("font","</%s>"%element.tagname, - color="red") - - def write_section(self,element,stream): - """Write a section - i.e., something with a title - """ - - self.level += 1 - - if element.hasattr("name"): - # Lazily, escape the name so we don't have to worry - # about single quotes in it... - name=self.html.escape(element["name"]) - - # Hmm - we *want* to write "\n\n<a name='...'></a>" - # which isn't *quite* what this does - maybe have a specialised - # call in buildhtml.py? - self.html.add("text",self.html.element("a",name=name)) - if self.visible_links: - self.html.start("font",color="green") - self.html.add("text","<%s>"%name) - self.html.end("font") - - for node in element: - self.write_html(node,stream) - - self.level -= 1 - - def write_title(self,element,stream): - if 1 <= self.level <= 6: - self.html.start("h%d"%self.level) - for node in element: - self.write_html(node,stream) - self.html.end("h%d"%self.level) - else: - # Put a warning here? - self.html.start("p") - self.html.start("font",size="-1",color="red") - self.html.add("text","[problem: header level='%d']"%self.level) - self.html.end("font") - self.html.start("strong") - for node in element: - self.write_html(node,stream) - self.html.end("strong") - self.html.end("p") - - def write_transition(self,element,stream): - self.html.start("p", # hmm - strictly not legal... - self.html.element("hr")) - - def write_enumerated_list(self,element,stream): - typedict = {"arabic" : "1", - "roman" : "i", - "Roman" : "I", - "alpha" : "a", - "Alpha" : "A"} - try: - enumtype = typedict[element["enumtype"]] - except: - enumtype = "1" - - # Does this match how DOCUTILS nodes work? - if element.hasattr("start"): - self.html.start("ol",type=enumtype,start=element["start"]) - else: - self.html.start("ol",type=enumtype) - for node in element: - self.write_html(node,stream) - self.html.end("ol") - - def write_bullet_list(self,element,stream): - # Hmm - the translation is fairly arbitrary - # - but at least consistent - bulletdict = {"*" : "disc", - "-" : "circle", - "+" : "square"} - try: - bullet = bulletdict[element["bullet"]] - except: - bullet = None - - if bullet: - self.html.start("ul",type=bullet) - else: - self.html.start("ul") - for node in element: - self.write_html(node,stream) - self.html.end("ul") - - def write_definition_list(self,element,stream): - self.html.start("dl") - for node in element: - self.write_html(node,stream) - self.html.end("dl") - - def write_definition_list_item(self,element,stream): - # Nothing special to do for this one - for node in element: - self.write_html(node,stream) - - def write_term(self,element,stream): - self.html.start("dt") - self.html.start("strong") - for node in element: - self.write_html(node,stream) - self.html.add("text"," ") # to separate consecutive parts, - # in option lists - self.html.end("strong") - self.html.end("dt") - - def write_list_item(self,element,stream): - self.html.start("li") - for node in element: - self.write_html(node,stream) - self.html.end("li") - - def write_option_list(self,element,stream): - self.html.start("dl") - for node in element: - self.write_html(node,stream) - self.html.end("dl") - - def write_option_list_item(self,element,stream): - for node in element: - self.write_html(node,stream) - - def write_option(self,element,stream): - self.html.start("dt") - self.html.start("strong") - for node in element: - self.write_html(node,stream) - self.html.add("text"," ") # to separate consecutive parts, - # in option lists - self.html.end("strong") - self.html.end("dt") - - def write_definition(self,element,stream): - self.html.start("dd") - for node in element: - self.write_html(node,stream) - self.html.end("dd") - - def write_short_option(self,element,stream): - self.html.start("samp") - for node in element: - self.write_html(node,stream) - self.html.end("samp") - - def write_long_option(self,element,stream): - self.html.start("samp") - for node in element: - self.write_html(node,stream) - self.html.end("samp") - - def write_vms_option(self,element,stream): - self.html.start("samp") - for node in element: - self.write_html(node,stream) - self.html.end("samp") - - def write_option_argument(self,element,stream): - self.html.start("samp") - for node in element: - self.write_html(node,stream) - self.html.end("samp") - - def write_description(self,element,stream): - self.html.start("dd") - for node in element: - self.write_html(node,stream) - self.html.end("dd") - - def write_field_list(self,element,stream): - """Write out a fieldlist. - """ - # The colour is for debugging purposes only! - self.html.start("table",width="100%",bgcolor="palegreen") - - self.infield = 1 - for node in element: - self.write_html(node,stream) - self.infield = 0 - - self.html.end("table") - - def write_field(self,element,stream): - self.html.start("tr",valign="top",dps="field") - for node in element: - self.write_html(node,stream) - self.html.end("tr") - - def write_field_name(self,element,stream): - self.html.start("td",dps="field_name") - self.html.start("strong") - for node in element: - self.write_html(node,stream) - self.html.end("strong") - self.html.end("td") - - def write_field_body(self,element,stream): - self.infield = 1 - self.paranum = 0 - self.html.start("td",dps="field_body") - for node in element: - self.write_html(node,stream) - self.html.end("td") - self.infield = 0 - - def write_biblio_field(self,element,stream,name): - """Write out a document bibliographic datum. - """ - self.infield = 1 - # The colour is for debugging purposes only! - self.html.start("table",width="100%",bgcolor="palegreen") - self.html.start("tr",valign="top") - self.html.start("td",dps="biblio_field") - self.html.add("strong",name) - - if len(element) != 1: - raise HTMLError,"Found %d children in field %s"%\ - (len(element),name) - - self.write_field_body(element[0],stream) - - self.html.end("td","tr","table") - self.infield = 0 - - def write_subtitle(self,element,stream): - self.write_biblio_field(element,stream,"Subtitle") - - def write_author(self,element,stream): - self.write_biblio_field(element,stream,"Author") - - def write_authors(self,element,stream): - self.write_biblio_field(element,stream,"Authors") - - def write_organization(self,element,stream): - self.write_biblio_field(element,stream,"Organisation") - - def write_organisation(self,element,stream): - self.write_biblio_field(element,stream,"Organisation") - - def write_contact(self,element,stream): - self.write_biblio_field(element,stream,"Contact") - - def write_version(self,element,stream): - self.write_biblio_field(element,stream,"Version") - - def write_status(self,element,stream): - self.write_biblio_field(element,stream,"Status") - - def write_date(self,element,stream): - self.write_biblio_field(element,stream,"Date") - - def write_revision(self,element,stream): - self.write_biblio_field(element,stream,"Revision") - - def write_copyright(self,element,stream): - self.write_biblio_field(element,stream,"Copyright") - - def write_abstract(self,element,stream): - self.write_biblio_field(element,stream,"Abstract") - - def write_reference(self,element,stream): - """Write a link - the "pointer" to a target. - - Doesn't yet handle "indirect" links... - """ - if element.hasattr("refuri"): - name = self.html.escape(element["refuri"]) - self.html.start("a",href=name) - elif element.hasattr("refname"): - # Escape the name to match what we do with titles... - name = "#"+self.html.escape(element["refname"]) - self.html.start("a",href=name) - else: - self.write_unknown(element,stream) - return - for node in element: - self.write_html(node,stream) - self.html.end("a") - if self.visible_links: - self.html.start("font",color="green") - self.html.add("text","<%s>"%name) - self.html.end("font") - - def write_target(self,element,stream): - """Write the target end of a link. - - Ultimately, the user should be able to choose to fold these into - the document (i.e., into the "link" itself). - """ - try: - name = element["name"] - except: - name = "**no target name**" - - ##if not self.in_paragraph: - ## self.html.start("p") - - # Provide some "debugging" information - if self.visible_targets: - self.html.start("font",color="green") - self.html.start("strong") - self.html.start("em") - self.html.add("text",name) - self.html.end("em") - self.html.end("strong") - self.html.end("font") - - self.html.add("a",name=self.html.escape(name)) - - if element.has_key("refuri"): - uri = self.html.escape(element["refuri"]) - self.html.add("text",": ") - self.html.add("a",element["refuri"],href=uri) - - ##if not self.in_paragraph: - ## self.html.end("p") - - def write_footnote(self,element,stream): - """Write out the body of a footnote. - - It's not entirely clear how to do this in HTML, so we'll - just try for something distinctive... - """ - name = auto = index = None - if element.hasattr("name"): - name = element["name"] - if element.hasattr("auto"): - auto = element["auto"] - if element.hasattr("auto-index"): - index = element["auto-index"] - - if auto and index == None: - raise HTMLError,"Footnote auto-numbering has not been done" - - if name and auto: - self.html.start("p") - self.html.add("a",name=self.html.escape(name)) - self.html.add("text"," ") - self.html.add("a",name=`index`) - self.html.end("p") - elif name: - self.html.start("p") - self.html.add("a",name=self.html.escape(name)) - self.html.end("p") - elif auto: - self.html.start("p") - self.html.add("a",name=`index`) - self.html.end("p") - else: - self.write_message(stream,level=2, - text="Footnote doesn't have name" - " or auto attributes") - - self.html.start("table",align="center",width="80%") - self.html.start("tr") - self.html.start("td") - - # Automatically numbered footnotes don't contain an explicit - # <label> element, so we have to do it by hand... - if auto: - self.write_label(element,stream,index) - - for node in element: - self.write_html(node,stream) - - self.html.end("td","tr","table") - - def write_label(self,element,stream,index=None): - """Write out the label for a footnote. - """ - self.html.add("p", - self.html.element("hr")) - - self.html.start("p","Footnote ") - if index == None: - self.html.add("strong",element.astext()) - else: - self.html.add("strong",`index`) - self.html.end("p") - self.html.add("br") - self.html.add("hr") - - def write_footnote_reference(self,element,stream): - """Write out the link to a footnote. - """ - name = auto = None - if element.hasattr("refname"): - name = element["refname"] - if element.hasattr("auto"): - auto = element["auto"] - - if auto: - if name: - if self.auto_footnote_names.has_key(name): - number = self.auto_footnote_names[name] - else: - self.write_message(stream,level=2, - text="Autonumber footnote name" - " '%s' doesn't match a" - " footnote"%name) - number = 0 - else: - try: - number = self.auto_footnote_list[self.auto_footnote_index] - self.auto_footnote_index += 1 - except IndexError: - # Hmm - probably a [#]_ with no footnotes for it to - # point to - best we can do is write *something* out - self.html.start("font",color=self.colours["Error"]) - self.html.add("text","[") - self.html.add("strong","#") - self.html.add("text","]") - self.html.end("font") - return - - if auto: - self.html.start("a",href="#%d"%number) - self.html.add("text","[") - self.html.add("strong",`number`) - self.html.add("text","]") - self.html.end("a") - elif name: - self.html.start("a",href="#%s"%self.html.escape(name)) - self.html.add("text","[") - self.html.add("strong",element.astext()) - self.html.add("text","]") - self.html.end("a") - else: - self.write_unknown(element,stream) - return - - - def write_comment(self,element,stream): - """Write out a comment - """ - # Technically, this may not be enough, as we don't know what - # they might *have* in the comment - but in practice, it's - # likely to do... - self.html.start("comment") - for node in element: - self.write_html(node,stream) - self.html.end("comment") - - def write_paragraph(self,element,stream): - """Write a new paragraph. - - This is a method simply because I do odd things inside - a field body, to make it "look" better. - """ - if self.html.last_tag() not in ["li","dd","td","th"]: - self.html.start("p") - write_slash_p = 1 - else: - write_slash_p = 0 - - for node in element: - self.write_html(node,stream) - - if write_slash_p: - self.html.end("p") - return - - - self.in_paragraph = 1 - if self.infield: - self.write_field_paragraph(element,stream) - else: - self.html.start("p") - for node in element: - self.write_html(node,stream) - self.html.end("p") - self.in_paragraph = 0 - - def write_field_paragraph(self,element,stream): - """Write a new paragraph inside a field body. - """ - started_row = 0 - if self.paranum > 0: - self.html.start("tr") - self.html.add("td") # an empty column... - self.html.start("td",dps="paragraph") - started_row = 1 - self.paranum += 1 - for node in element: - self.write_html(node,stream) - if self.paranum > 1: - self.html.end("td") - if started_row: - self.html.end("tr") - - def write_table(self,element,stream): - """Write out a table - initially in a very visible manner - """ - self.html.start("table",border="1",align="center") - for node in element: - self.write_html(node,stream) - self.html.end("table") - - def write_tgroup(self,element,stream): - for node in element: - self.write_html(node,stream) - - def write_colspec(self,element,stream): - for node in element: - self.write_html(node,stream) - - def write_rowspec(self,element,stream): - for node in element: - self.write_html(node,stream) - - def write_thead(self,element,stream): - """Write out a table header section - """ - self.html.start("thead") - self.in_table_header = 1 - for node in element: - self.write_html(node,stream) - self.in_table_header = 0 - self.html.end("thead") - - def write_tbody(self,element,stream): - """Write out a table body section - """ - self.html.start("tbody") - self.in_table_body = 1 - for node in element: - self.write_html(node,stream) - self.in_table_body = 0 - self.html.end("tbody") - - def write_row(self,element,stream): - """Write out a table row. - """ - if self.in_table_header: - self.html.start("tr",valign="top",align="left") - else: - self.html.start("tr",valign="top") - for node in element: - self.write_html(node,stream) - self.html.end("tr") - - def write_entry(self,element,stream): - """Write out the equivalent of a table "entry" (e.g., HTML <td>) - """ - keywords = {} - if element.hasattr("morecols"): - keywords["colspan"] = element["morecols"]+1 - if element.hasattr("morerows"): - keywords["rowspan"] = element["morerows"]+1 - keywords["dps"] = "entry" - self.html.start("td",**keywords) - - if self.in_table_header: - self.html.start("strong") - - if len(element) == 0: - # Since we're writing a table with a border, it looks better if - # even an empty entry has *some* content - a non-breaking space - # will suffice to make sure the entry is properly bordered - self.html.add("text"," ") - else: - for node in element: - self.write_html(node,stream) - - if self.in_table_header: - self.html.end("strong") - - self.html.end("td") - - def write_message(self,stream,level=2,text=None,element=None): - names = {1: "Information", - 2: "Warning", - 3: "Error", - 4: "Fatal"} - - if level == 0 and not self.showinforms: - return - elif level == 1 and not self.showwarnings: - return - - try: - name = names[level] - colour = self.colours[name] - except: - name = "Unrecognised warning level %d"%level - colour = self.colours["Fatal"] - bgcolour = self.colours["WarningBG"] - - self.html.start("table",width="100%%",bgcolor=bgcolour) - self.html.start("tr") - self.html.start("td") - self.html.start("font",color=colour) - self.html.add("strong",name) - self.html.end("font") - self.html.end("td","tr") - - self.html.start("tr") - self.html.start("td") - if text: - self.html.add("text",text) - if element: - for node in element: - self.write_html(node,stream) - self.html.end("td","tr") - self.html.end("table") - - def write_problematic(self,element,stream): - self.html.start("a",href="#%s"%element["refid"]) - self.html.start("font",color="red") - for node in element: - self.write_html(node,stream) - self.html.end("font") - self.html.end("a") - - def write_system_message(self,element,stream): - try: - target = element["refid"] - except: - target = None - if target: - self.html.add("a","",name="%s"%target) - self.write_message(stream,level=element["level"],element=element) - - def write_group(self,element,stream): - """By default, we don't do anything with <group> tags. - """ - for node in element: - self.write_html(node,stream) - - def write_emphasis(self,element,stream): - self.html.start("em") - for node in element: - self.write_html(node,stream) - self.html.end("em") - - def write_strong(self,element,stream): - self.html.start("strong") - for node in element: - self.write_html(node,stream) - self.html.end("strong") - - def write_interpreted(self,element,stream): - if element.hasattr("refname"): - self.html.start("a",href="#"+self.html.escape(element["refname"])) - inref = 1 - else: - inref = 0 - - self.html.start("samp",style="interpreted") - if element.hasattr("role"): - role = element["role"] - if self.role_text.has_key(role): - self.html.add("text","%s "%self.role_text[role]) - - for node in element: - self.write_html(node,stream) - self.html.end("samp") - - if inref: - self.html.end("a") - if self.visible_links: - self.html.start("font",color="green") - self.html.add("text","<%s>"%element["refname"]) - self.html.end("font") - - def write_literal(self,element,stream): - self.html.start("samp",style="literal") - for node in element: - self.write_html(node,stream) - self.html.end("samp") - - def write_literal_block(self,element,stream): - self.html.start("pre") - for node in element: - self.write_html(node,stream) - self.html.end("pre") - - def write_doctest_block(self,element,stream): - self.html.start("pre",style="doctest") - for node in element: - self.write_html(node,stream) - self.html.end("pre") - - def write_block_quote(self,element,stream): - self.html.start("blockquote") - for node in element: - self.write_html(node,stream) - self.html.end("blockquote") - - -# ---------------------------------------------------------------------- -class PythonWriter(Writer): - """A Writer that understands how Python is represented using DOCUTILS. - - Note that as a princple, I don't believe that "modes" should add extra - DOCUTILS nodes - in other words, any DOCUTILS tree should be presentable by - the default writer (albeit maybe not very well). - """ - - python_colours = {"package" : "Aqua", - "module" : "#FFFF00", - "class" : "#99CCFF", # Python blue - "method" : "#AAFFAA", - "function" : "#FFAAFF", - "docstring" : "#FFFFCC", # pale yellow-ish - "generator" : "pink", - } - - def write_group(self,element,stream): - """Write out a group according to its style. - """ - - try: - style = element["style"] - except: - style = None - - if style == "docstring": - self.write_docstring(element,stream) - elif style == "generator": - self.html.start("table",bgcolor=self.python_colours["generator"], - width="100%") - self.html.start("tr") - self.html.start("td") - - for node in element: - self.write_html(node,stream) - - self.html.end("td") - self.html.end("tr") - self.html.end("table") - else: - for node in element: - self.write_html(node,stream) - - def write_docstring(self,element,stream): - """Write out a docstring - """ - - self.html.start("table",bgcolor=self.python_colours["docstring"], - width="100%") - self.html.start("tr") - self.html.start("td") - - # Within a docstring, we want traditional HTML headers, - # starting at <h2> and working downwards - self.level = 1 - - for node in element: - self.write_html(node,stream) - - self.html.end("td") - self.html.end("tr") - self.html.end("table") - - def write_section(self,element,stream): - """Write a section - i.e., something with a title - """ - - try: - style = element["style"] - except: - style = None - - if style in ["package","module","class","method","function"]: - self.write_py_section(element,stream,style) - else: - Writer.write_section(self,element,stream) - - def write_py_section(self,element,stream,style): - """Write out the HTML for a Python section. - """ - - self.level += 1 - - try: - colour = self.python_colours[style] - except: - colour = self.colours["default"] - - self.html.start("table",width="100%",cellspacing="0") - - # First row - full width, coloured - self.html.start("tr",bgcolor=colour) - self.html.add("td",self.html.element("hr"),colspan="2") - self.html.end("tr") - - # Now, if the section has a <title> and a <group> with - # style="details", we want to treat them specially - and - # we *know* that they should, if present, be the first - # two child elements... - # Optional first-and-a-halfth row - offset = self.write_py_section_details(element,stream,colour) - - if len(element.children) > offset: - # Second row - left hand margin coloured, "body" not - self.html.start("tr") - self.html.add("td"," ",width="1%",bgcolor=colour) - self.html.start("td",width="99%") - - # More detail needed here? - for node in element.children[offset:]: - self.write_html(node,stream) - - self.html.end("td") - self.html.end("tr") - - # Third row - full width coloured - self.html.start("tr",bgcolor=colour) - self.html.add("td",self.html.element("hr"),colspan="2") - self.html.end("tr") - - if self.got_contents: - # Fourth row - full width coloured - self.html.start("tr",bgcolor=colour) - self.html.add("td"," ") - self.html.start("td",align="right") - self.html.add("text","Return to ") - self.html.add("a","Contents",href="#contents") - self.html.end("td") - self.html.end("tr") - - self.html.end("table") - - self.level -= 1 - - def write_py_section_details(self,element,stream,colour): - """Deal with the title and details for a Python section. - - Returns the offset within the <section> elements children - at which we should continue processing... - """ - offset = 0 - if element[0].tagname == "title": - offset = 1 - self.html.start("tr",bgcolor=colour) - self.html.start("td",colspan="2") - self.html.start("table",width="100%") - self.html.start("tr",valign="top") - self.html.start("td") - # Hmm. It doesn't work too well using <h2>..<h6> as header - # elements for nested module, class, etc. Let's do this - # by "hand" - self.html.start("font",size="+2") - for node in element[0]: - self.write_html(node,stream) - self.html.end("font") - self.html.end("td") - self.html.end("tr") - - if element[1].tagname == "group" and \ - element[1].hasattr("style") and \ - element[1]["style"] == "details": - offset = 2 - self.html.start("tr") - self.html.start("td",align="right") - self.write_html(element[1],stream) - self.html.end("td") - self.html.end("tr") - self.html.end("table") - self.html.end("td") - self.html.end("tr") - return offset diff --git a/sandbox/tibs/pysource/notes/notes.py b/sandbox/tibs/pysource/notes/notes.py deleted file mode 100755 index a7fd81f51..000000000 --- a/sandbox/tibs/pysource/notes/notes.py +++ /dev/null @@ -1,115 +0,0 @@ -"""Notes (i.e., me working things out...) - -Given the following Python:: - - def func(a,b=1,c='jim',d=None,e=[],f={'a':1},g=(1,2,3)): - -the tree produced by compiler looks like:: - - <Function> 'func' 'a' 'b' 'c' 'd' 'e' 'f' 'g' - <Const> 1 - <Const> 'jim' - <Name> 'None' - <List> - <Dict> - <Const> 'a' - <Const> 1 - <Tuple> - <Const> 1 - <Const> 2 - <Const> 3 0 - -If one has:: - - def func2(a,*args,**kargs): - -one gets:: - - <Function> 'func2' 'a' 'args' 'kargs' 3 None - -and lastly:: - - def func3((a,b,c),d): - -gives:: - - <Function> 'func3' 'a' 'b' 'c' 'd' 0 None - - -`compiler.misc` defines `flatten(tup)` - maybe I should try it? - -""" - - -# compiler.transformer contains this useful set of comments: -# -# -# The output tree has the following nodes: -# -# Source Python line #'s appear at the end of each of all of these nodes -# If a line # doesn't apply, there will be a None instead. -# -# module: doc, node -# stmt: [ node1, ..., nodeN ] -# function: name, argnames, defaults, flags, doc, codeNode -# lambda: argnames, defaults, flags, codeNode -# classdef: name, bases, doc, codeNode -# pass: -# break: -# continue: -# for: assignNode, listNode, bodyNode, elseNode -# while: testNode, bodyNode, elseNode -# if: [ (testNode, suiteNode), ... ], elseNode -# exec: expr1Node, expr2Node, expr3Node -# from: modname, [ name1, ..., nameN ] -# import: [ name1, ..., nameN ] -# raise: expr1Node, expr2Node, expr3Node -# tryfinally: trySuiteNode, finSuiteNode -# tryexcept: trySuiteNode, [ (exprNode, assgnNode, suiteNode), ... ], elseNode -# return: valueNode -# const: value -# print: [ node1, ..., nodeN ] [, dest] -# printnl: [ node1, ..., nodeN ] [, dest] -# discard: exprNode -# augassign: node, op, expr -# assign: [ node1, ..., nodeN ], exprNode -# ass_tuple: [ node1, ..., nodeN ] -# ass_list: [ node1, ..., nodeN ] -# ass_name: name, flags -# ass_attr: exprNode, attrname, flags -# list: [ node1, ..., nodeN ] -# dict: [ (key1, val1), ..., (keyN, valN) ] -# not: exprNode -# compare: exprNode, [ (op, node), ..., (op, node) ] -# name: name -# global: [ name1, ..., nameN ] -# backquote: node -# getattr: exprNode, attrname -# call_func: node, [ arg1, ..., argN ] -# keyword: name, exprNode -# subscript: exprNode, flags, [ sub1, ..., subN ] -# ellipsis: -# sliceobj: [ node1, ..., nodeN ] -# slice: exprNode, flags, lowerNode, upperNode -# assert: expr1, expr2 -# -# Compiled as "binary" ops: -# tuple: [ node1, ..., nodeN ] -# or: [ node1, ..., nodeN ] -# and: [ node1, ..., nodeN ] -# bitor: [ node1, ..., nodeN ] -# bitxor: [ node1, ..., nodeN ] -# bitand: [ node1, ..., nodeN ] -# -# Operations easily evaluateable on constants: -# <<: exprNode, shiftNode -# >>: exprNode, shiftNode -# +: leftNode, rightNode -# -: leftNode, rightNode -# *: leftNode, rightNode -# /: leftNode, rightNode -# %: leftNode, rightNode -# power: leftNode, rightNode -# unary+: node -# unary-: node -# invert: node diff --git a/sandbox/tibs/pysource/notes/notes.txt b/sandbox/tibs/pysource/notes/notes.txt deleted file mode 100644 index 548d792a1..000000000 --- a/sandbox/tibs/pysource/notes/notes.txt +++ /dev/null @@ -1,7 +0,0 @@ -Paul Moore's summary of backquoted markup possibilities:: - - `xxxxx`_ - named hyperlink reference (type 2) - `xxxxx`__ - anonymous hyperlink reference (type 2) - _`xxxx` - inline hyperlink targets - `/xxx/` - substitution reference - `xxxxx` - interpreted text diff --git a/sandbox/tibs/pysource/notes/roles.txt b/sandbox/tibs/pysource/notes/roles.txt deleted file mode 100644 index b063e026d..000000000 --- a/sandbox/tibs/pysource/notes/roles.txt +++ /dev/null @@ -1,61 +0,0 @@ -The following is taken from the DPS document pysource-reader.txt:: - - Interpreted Text - ================ - - DTD elements: package, module, class, method, function, - module_attribute, class_attribute, instance_attribute, variable, - parameter, type, exception_class, warning_class. - - In Python docstrings, interpreted text is used to classify and mark up - program identifiers, such as the names of variables, functions, - classes, and modules. If the identifier alone is given, its role is - inferred implicitly according to the Python namespace lookup rules. - For functions and methods (even when dynamically assigned), - parentheses ('()') may be included:: - - This function uses `another()` to do its work. - - For class, instance and module attributes, dotted identifiers are used - when necessary:: - - class Keeper(Storer): - - """ - Extend `Storer`. Class attribute `instances` keeps track of - the number of `Keeper` objects instantiated. - """ - - instances = 0 - """How many `Keeper` objects are there?""" - - def __init__(self): - """ - Extend `Storer.__init__()` to keep track of instances. - - Keep count in `self.instances` and data in `self.data`. - """ - Storer.__init__(self) - self.instances += 1 - - self.data = [] - """Store data in a list, most recent last.""" - - def storedata(self, data): - """ - Extend `Storer.storedata()`; append new `data` to a list - (in `self.data`). - """ - self.data = data - - To classify identifiers explicitly, the role is given along with the - identifier in either prefix or suffix form:: - - Use :method:`Keeper.storedata` to store the object's data in - `Keeper.data`:instance_attribute:. - - The role may be one of 'package', 'module', 'class', 'method', - 'function', 'module_attribute', 'class_attribute', - 'instance_attribute', 'variable', 'parameter', 'type', - 'exception_class', 'exception', 'warning_class', or 'warning'. Other - roles may be defined. diff --git a/sandbox/tibs/pysource/notes/scope.txt b/sandbox/tibs/pysource/notes/scope.txt deleted file mode 100644 index 1ad889c1b..000000000 --- a/sandbox/tibs/pysource/notes/scope.txt +++ /dev/null @@ -1,19 +0,0 @@ -On finding names -================ - -We are not trying for a general solution to the problem of "find the item -that is being referred to" - we are just trying to provide useful links -between <interpreted> items in docstrings and those things that they -might reasonably be expected to be referring to. - -Some rules thus occur. - -1. We will not show attributes, docstring or not, if they are not - - a. At module level (so a ModuleValue) - b. Within a class (so a ClassValue) - c. Within a method called __init__ or __new__. - - So discard any attributes that do not match these criteria. - -Hmm - is that the only rule? Can it be so simple? diff --git a/sandbox/tibs/pysource/notes/string.html b/sandbox/tibs/pysource/notes/string.html deleted file mode 100755 index e09c30e2b..000000000 --- a/sandbox/tibs/pysource/notes/string.html +++ /dev/null @@ -1,225 +0,0 @@ - -<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN"> -<html><head><title>Python: module string</title> -<style type="text/css"><!-- -TT { font-family: lucidatypewriter, lucida console, courier } ---></style></head><body bgcolor="#f0f0f8"> - -<table width="100%" cellspacing=0 cellpadding=2 border=0> -<tr bgcolor="#7799ee"> -<td valign=bottom><small> <br></small -><font color="#ffffff" face="helvetica, arial"> <br><big><big><strong>string</strong></big></big></font></td -><td align=right valign=bottom -><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:///C|/python21/lib/string.py">c:\python21\lib\string.py</a></font></td></tr></table> - <p><small><tt>A collection of string operations (most are no longer used in Python 1.6).<br> - <br> -Warning: most of the code you see here isn't normally used nowadays. With<br> -Python 1.6, many of these functions are implemented as methods on the<br> -standard string object. They used to be implemented by a built-in module<br> -called strop, but strop is now obsolete itself.<br> - <br> -Public module variables:<br> - <br> -whitespace -- a string containing all characters considered whitespace<br> -lowercase -- a string containing all characters considered lowercase letters<br> -uppercase -- a string containing all characters considered uppercase letters<br> -letters -- a string containing all characters considered letters<br> -digits -- a string containing all characters considered decimal digits<br> -hexdigits -- a string containing all characters considered hexadecimal digits<br> -octdigits -- a string containing all characters considered octal digits<br> -punctuation -- a string containing all characters considered punctuation<br> -printable -- a string containing all characters considered printable</tt></small></p> - -<p><table width="100%" cellspacing=0 cellpadding=2 border=0> -<tr bgcolor="#eeaa77"> -<td colspan=3 valign=bottom><small><small> <br></small></small -><font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr> - -<tr><td bgcolor="#eeaa77"><tt> </tt></td><td> </td> -<td width="100%"><dl><dt><a name="-_float"><strong>_float</strong></a> = float(...)<dd><small><tt>float(x) -> floating point number<br> - <br> -Convert a string or number to a floating point number, if possible.</tt></small></dl> - <dl><dt><a name="-_int"><strong>_int</strong></a> = int(...)<dd><small><tt>int(x[, base]) -> integer<br> - <br> -Convert a string or number to an integer, if possible. A floating point<br> -argument will be truncated towards zero (this does not include a string<br> -representation of a floating point number!) When converting a string, use<br> -the optional base. It is an error to supply a base when converting a<br> -non-string.</tt></small></dl> - <dl><dt><a name="-_long"><strong>_long</strong></a> = long(...)<dd><small><tt>long(x) -> long integer<br> -long(x, base) -> long integer<br> - <br> -Convert a string or number to a long integer, if possible. A floating<br> -point argument will be truncated towards zero (this does not include a<br> -string representation of a floating point number!) When converting a<br> -string, use the given base. It is an error to supply a base when<br> -converting a non-string.</tt></small></dl> - <dl><dt><a name="-atof"><strong>atof</strong></a>(s)<dd><small><tt><a href="#-atof">atof</a>(s) -> float<br> - <br> -Return the floating point number represented by the string s.</tt></small></dl> - <dl><dt><a name="-atoi"><strong>atoi</strong></a>(s, base<small><font color="#909090">=10</font></small>)<dd><small><tt><a href="#-atoi">atoi</a>(s [,base]) -> int<br> - <br> -Return the integer represented by the string s in the given<br> -base, which defaults to 10. The string s must consist of one<br> -or more digits, possibly preceded by a sign. If base is 0, it<br> -is chosen from the leading characters of s, 0 for octal, 0x or<br> -0X for hexadecimal. If base is 16, a preceding 0x or 0X is<br> -accepted.</tt></small></dl> - <dl><dt><a name="-atol"><strong>atol</strong></a>(s, base<small><font color="#909090">=10</font></small>)<dd><small><tt><a href="#-atol">atol</a>(s [,base]) -> long<br> - <br> -Return the long integer represented by the string s in the<br> -given base, which defaults to 10. The string s must consist<br> -of one or more digits, possibly preceded by a sign. If base<br> -is 0, it is chosen from the leading characters of s, 0 for<br> -octal, 0x or 0X for hexadecimal. If base is 16, a preceding<br> -0x or 0X is accepted. A trailing L or l is not accepted,<br> -unless base is 0.</tt></small></dl> - <dl><dt><a name="-capitalize"><strong>capitalize</strong></a>(s)<dd><small><tt><a href="#-capitalize">capitalize</a>(s) -> string<br> - <br> -Return a copy of the string s with only its first character<br> -capitalized.</tt></small></dl> - <dl><dt><a name="-capwords"><strong>capwords</strong></a>(s, sep<small><font color="#909090">=None</font></small>)<dd><small><tt><a href="#-capwords">capwords</a>(s, [sep]) -> string<br> - <br> -Split the argument into words using split, capitalize each<br> -word using capitalize, and join the capitalized words using<br> -join. Note that this replaces runs of whitespace characters by<br> -a single space.</tt></small></dl> - <dl><dt><a name="-center"><strong>center</strong></a>(s, width)<dd><small><tt><a href="#-center">center</a>(s, width) -> string<br> - <br> -Return a center version of s, in a field of the specified<br> -width. padded with spaces as needed. The string is never<br> -truncated.</tt></small></dl> - <dl><dt><a name="-count"><strong>count</strong></a>(s, *args)<dd><small><tt><a href="#-count">count</a>(s, sub[, start[,end]]) -> int<br> - <br> -Return the number of occurrences of substring sub in string<br> -s[start:end]. Optional arguments start and end are<br> -interpreted as in slice notation.</tt></small></dl> - <dl><dt><a name="-expandtabs"><strong>expandtabs</strong></a>(s, tabsize<small><font color="#909090">=8</font></small>)<dd><small><tt><a href="#-expandtabs">expandtabs</a>(s [,tabsize]) -> string<br> - <br> -Return a copy of the string s with all tab characters replaced<br> -by the appropriate number of spaces, depending on the current<br> -column, and the tabsize (default 8).</tt></small></dl> - <dl><dt><a name="-find"><strong>find</strong></a>(s, *args)<dd><small><tt><a href="#-find">find</a>(s, sub [,start [,end]]) -> in<br> - <br> -Return the lowest index in s where substring sub is found,<br> -such that sub is contained within s[start,end]. Optional<br> -arguments start and end are interpreted as in slice notation.<br> - <br> -Return -1 on failure.</tt></small></dl> - <dl><dt><a name="-index"><strong>index</strong></a>(s, *args)<dd><small><tt><a href="#-index">index</a>(s, sub [,start [,end]]) -> int<br> - <br> -Like find but raises ValueError when the substring is not found.</tt></small></dl> - <dl><dt><a name="-join"><strong>join</strong></a>(words, sep<small><font color="#909090">=' '</font></small>)<dd><small><tt><a href="#-join">join</a>(list [,sep]) -> string<br> - <br> -Return a string composed of the words in list, with<br> -intervening occurrences of sep. The default separator is a<br> -single space.<br> - <br> -(joinfields and join are synonymous)</tt></small></dl> - <dl><dt><a name="-joinfields"><strong>joinfields</strong></a> = join(words, sep<small><font color="#909090">=' '</font></small>)<dd><small><tt><a href="#-join">join</a>(list [,sep]) -> string<br> - <br> -Return a string composed of the words in list, with<br> -intervening occurrences of sep. The default separator is a<br> -single space.<br> - <br> -(joinfields and join are synonymous)</tt></small></dl> - <dl><dt><a name="-ljust"><strong>ljust</strong></a>(s, width)<dd><small><tt><a href="#-ljust">ljust</a>(s, width) -> string<br> - <br> -Return a left-justified version of s, in a field of the<br> -specified width, padded with spaces as needed. The string is<br> -never truncated.</tt></small></dl> - <dl><dt><a name="-lower"><strong>lower</strong></a>(s)<dd><small><tt><a href="#-lower">lower</a>(s) -> string<br> - <br> -Return a copy of the string s converted to lowercase.</tt></small></dl> - <dl><dt><a name="-lstrip"><strong>lstrip</strong></a>(s)<dd><small><tt><a href="#-lstrip">lstrip</a>(s) -> string<br> - <br> -Return a copy of the string s with leading whitespace removed.</tt></small></dl> - <dl><dt><a name="-maketrans"><strong>maketrans</strong></a>(...)<dd><small><tt><a href="#-maketrans">maketrans</a>(frm, to) -> string<br> - <br> -Return a translation table (a string of 256 bytes long)<br> -suitable for use in string.translate. The strings frm and to<br> -must be of the same length.</tt></small></dl> - <dl><dt><a name="-replace"><strong>replace</strong></a>(s, old, new, maxsplit<small><font color="#909090">=-1</font></small>)<dd><small><tt>replace (str, old, new[, maxsplit]) -> string<br> - <br> -Return a copy of string str with all occurrences of substring<br> -old replaced by new. If the optional argument maxsplit is<br> -given, only the first maxsplit occurrences are replaced.</tt></small></dl> - <dl><dt><a name="-rfind"><strong>rfind</strong></a>(s, *args)<dd><small><tt><a href="#-rfind">rfind</a>(s, sub [,start [,end]]) -> int<br> - <br> -Return the highest index in s where substring sub is found,<br> -such that sub is contained within s[start,end]. Optional<br> -arguments start and end are interpreted as in slice notation.<br> - <br> -Return -1 on failure.</tt></small></dl> - <dl><dt><a name="-rindex"><strong>rindex</strong></a>(s, *args)<dd><small><tt><a href="#-rindex">rindex</a>(s, sub [,start [,end]]) -> int<br> - <br> -Like rfind but raises ValueError when the substring is not found.</tt></small></dl> - <dl><dt><a name="-rjust"><strong>rjust</strong></a>(s, width)<dd><small><tt><a href="#-rjust">rjust</a>(s, width) -> string<br> - <br> -Return a right-justified version of s, in a field of the<br> -specified width, padded with spaces as needed. The string is<br> -never truncated.</tt></small></dl> - <dl><dt><a name="-rstrip"><strong>rstrip</strong></a>(s)<dd><small><tt><a href="#-rstrip">rstrip</a>(s) -> string<br> - <br> -Return a copy of the string s with trailing whitespace<br> -removed.</tt></small></dl> - <dl><dt><a name="-split"><strong>split</strong></a>(s, sep<small><font color="#909090">=None</font></small>, maxsplit<small><font color="#909090">=-1</font></small>)<dd><small><tt><a href="#-split">split</a>(s [,sep [,maxsplit]]) -> list of strings<br> - <br> -Return a list of the words in the string s, using sep as the<br> -delimiter string. If maxsplit is given, splits into at most<br> -maxsplit words. If sep is not specified, any whitespace string<br> -is a separator.<br> - <br> -(split and splitfields are synonymous)</tt></small></dl> - <dl><dt><a name="-splitfields"><strong>splitfields</strong></a> = split(s, sep<small><font color="#909090">=None</font></small>, maxsplit<small><font color="#909090">=-1</font></small>)<dd><small><tt><a href="#-split">split</a>(s [,sep [,maxsplit]]) -> list of strings<br> - <br> -Return a list of the words in the string s, using sep as the<br> -delimiter string. If maxsplit is given, splits into at most<br> -maxsplit words. If sep is not specified, any whitespace string<br> -is a separator.<br> - <br> -(split and splitfields are synonymous)</tt></small></dl> - <dl><dt><a name="-strip"><strong>strip</strong></a>(s)<dd><small><tt><a href="#-strip">strip</a>(s) -> string<br> - <br> -Return a copy of the string s with leading and trailing<br> -whitespace removed.</tt></small></dl> - <dl><dt><a name="-swapcase"><strong>swapcase</strong></a>(s)<dd><small><tt><a href="#-swapcase">swapcase</a>(s) -> string<br> - <br> -Return a copy of the string s with upper case characters<br> -converted to lowercase and vice versa.</tt></small></dl> - <dl><dt><a name="-translate"><strong>translate</strong></a>(s, table, deletions<small><font color="#909090">=''</font></small>)<dd><small><tt><a href="#-translate">translate</a>(s,table [,deletions]) -> string<br> - <br> -Return a copy of the string s, where all characters occurring<br> -in the optional argument deletions are removed, and the<br> -remaining characters have been mapped through the given<br> -translation table, which must be a string of length 256. The<br> -deletions argument is not allowed for Unicode strings.</tt></small></dl> - <dl><dt><a name="-upper"><strong>upper</strong></a>(s)<dd><small><tt><a href="#-upper">upper</a>(s) -> string<br> - <br> -Return a copy of the string s converted to uppercase.</tt></small></dl> - <dl><dt><a name="-zfill"><strong>zfill</strong></a>(x, width)<dd><small><tt><a href="#-zfill">zfill</a>(x, width) -> string<br> - <br> -Pad a numeric string x with zeros on the left, to fill a field<br> -of the specified width. The string x is never truncated.</tt></small></dl> -</td></tr></table> -<p><table width="100%" cellspacing=0 cellpadding=2 border=0> -<tr bgcolor="#55aa55"> -<td colspan=3 valign=bottom><small><small> <br></small></small -><font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr> - -<tr><td bgcolor="#55aa55"><tt> </tt></td><td> </td> -<td width="100%"><strong>_StringType</strong> = <type 'string'><br> -<strong>__file__</strong> = r'c:\Python21\Lib\string.pyc'<br> -<strong>__name__</strong> = 'string'<br> -<strong>_idmap</strong> = '<font color="#c040c0">\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f</font> !"#$%&<font color="#c040c0">\'</font>()*+,-./...<font color="#c040c0">\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff</font>'<br> -<strong>_idmapL</strong> = None<br> -<strong>digits</strong> = '0123456789'<br> -<strong>hexdigits</strong> = '0123456789abcdefABCDEF'<br> -<strong>letters</strong> = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'<br> -<strong>lowercase</strong> = 'abcdefghijklmnopqrstuvwxyz'<br> -<strong>octdigits</strong> = '01234567'<br> -<strong>printable</strong> = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&<font color="#c040c0">\'</font>()*+,-./:;<=>?@[<font color="#c040c0">\\</font>]^_`{|}~ <font color="#c040c0">\t\n\r\x0b\x0c</font>'<br> -<strong>punctuation</strong> = '!"#$%&<font color="#c040c0">\'</font>()*+,-./:;<=>?@[<font color="#c040c0">\\</font>]^_`{|}~'<br> -<strong>uppercase</strong> = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'<br> -<strong>whitespace</strong> = '<font color="#c040c0">\t\n\x0b\x0c\r</font> '</td></tr></table> -</body></html>
\ No newline at end of file diff --git a/sandbox/tibs/pysource/notes/thoughts.txt b/sandbox/tibs/pysource/notes/thoughts.txt deleted file mode 100644 index 59691717b..000000000 --- a/sandbox/tibs/pysource/notes/thoughts.txt +++ /dev/null @@ -1,170 +0,0 @@ -Thoughts on pysource -==================== - -:Author: Tibs -:Date: 19 Sep 2002 - -.. - - [This is the contents of an email sent from Tibs to me, to tie up - -- or at least identify -- loose ends. When Tibs writes "you", - he's referring to me. It is published with Tibs' permission. I - mostly agree with what Tibs has written. I've added my own notes, - indented in square brackets, like this one. - - --David Goodger] - -Well, having looked back at the pysource sources, I'm confirmed -in my opinion that, if I were to start work on the project again, -I would probably start a new version of pysource from scratch, -using what I have learned (essentially, the content of visit.py), -and taking advantage of all the new technology that is in the -modern DOCUTILS. - -Thus I would (at least): - -1. produce a "frontend" using Optik -2. rewrite visit.py more neatly -3. construct DOCUTILS nodes to match pysource.dtd -4. *possibly* meld those into the classes in visit.py - (but possibly not - some of the stuff in visit.py - seems to me to be, of necessity, a bit messy as it - is constructing stuff, and thus it may be premature - to "DOCUTILS" it before it is necessary). -5. produce an example transform to amend the Python - specific DOCUTILS tree into a generic DOCUTILS - tree. - -That's a fair amount of work, and I'm not convinced that I can -find the sustained effort [1]_, especially if you might be -willing to take the task on (and if I would have been refactoring -anyway, a whole different head may be a significant benefit). - -.. [1] I believe that's called "British understatement". - -Some comments on pysource.txt and pysource.dtd ----------------------------------------------- - -I believe that a <docstring> node is a Good Thing. It's a natural -construct (if one believes that the other nodes are good things -as well!), and it allows the "transform" of the Python specific -tree to be done more sensibly. - - [I think I finally understand what Tibs is getting at here. I - thought he meant to have a <docstring> node in an otherwise - standard Docutils doctree, which just contained the raw docstring. - Instead, I believe he means that the Python-specific Docutils - doctree elements like <class_section> and - <module_section> (see pysource.dtd), should each have a <docstring> - element which contains ``%structure.model;``, instead of the - current ``%structure.model;`` directly inside the element. In - other words, a <docstring> element would be a simple container for - all the parsed elements of the docstring. On second thought, each - Python-specific section element would still have to have - ``%structure.model;``, in order to contain subsections. A - <package_section> would contain <module_section>s, which would - contain <class_section>s, and so on. - - So a <docstring> element may be useful as a convenience to - transforms. It would be a trivial change anyhow. - - The initial (internal) data structure resulting from the parsing - of Python modules & packages is a completely different beast. It - should contain <docstring> nodes, along with <package>, <module>, - <class>, etc., nodes. But these should *not* be subclasses of - docutils.nodes.Node, and this tree should not be a "Docutils - document tree". It should be a separate, parallel structure. - Document tree nodes (docutils.nodes.Node objects) are not suited - for this work. - - --DG] - -I recommend some initial "surgery" on the initial parse tree to -make finding the docstrings for nodes easier. - -I reckon that one produces a Python-specific doctree, and then -chooses one of several transforms to produce generic doctrees. - -By the way, I still vote for "static" introspection - that is, -without actually importing the module. It does limit what one can -do in some respects, but there are modules one might want to -document that one does not wish to import (and indeed, at work we -have some Python files that essentially cannot be introspected in -this manner with any ease - they are "designed" to be imported by -other modules after ``sys.path`` has been mangled suitably, and -just don't work stand-alone). - -I've tried to expand out the ideas you had on how the "pysource" -tool should work below. - - although it occurs to me that this is all terribly obvious, - really. Oh well... - -The pysource tool ------------------ - -The "input mode" should be the "Python Source Reader". - - You can see where I'm starting from in pysource.txt. - -This produces, as its output, a Python-specific doctree, -containing extra nodes as defined in pysource.dtd (etc.). - -One of the tasks undertaken by the Python Source Reader is to -decide what to do with docstrings. For each Python file, it -discovers (from ``__docformat__``) if the input parser is -"reStructuredText". If it is, then the contents of each docstring -in that file will be parsed as such, including sorting out -interpreted text (i.e., rendering it into links across the tree). -If it is not, then each docstring will be treated as a literal -block. - - This admits the posibility of adding other parsers *for - docstrings* at a later date - which I suspect is how HappyDoc - does it. It is just necessary to publicise the API for the - Docstring class, and then someone else can provide alternate - plugins. - -The output of the Python Source Reader is thus a Python-specific -doctree, with all docstrings fully processed (as appropriate), -and held inside <docstring> elements. - -The next stage is handled by the Layout Transformer. - - [What I call "stylist transforms". --DG] - -This determines the layout of the document to be produced - the -*kind* or *style* of output that the user wants (e.g., PyDoc -style, LibRefMan style, generic docutils style, etc.). - -Each layout is represented by a class that walks the -Python-specific doctree, replacing the Python-specific nodes with -appropriate generic nodes. The output of the Layout Transformer -is thus a generic doctree. - -The final stage is thus a normal DOCUTILS Writer - since it is -taking input that is a generic doctree, this makes perfect -sense. This also means that we get maximum leverage from existing -Writers, which is essential (not just a Good Thing). - -As you point out, some layouts will probably only be appropriate -for some output formats. Well, that's OK. On the other hand, if -the output of the Layout stage *is* a generic doctree, we're not -likely to get fatal errors by putting it through the wrong -Writer, so we needn't worry too much. - -Thus one might envisage a command line something like:: - - pysource --layout:book --format:html this_module - -Of course, other command line switches would be options for -particular phases (e.g., to use frames or not for HTML output). I -can see wanting a series of configuration files that one could -reference, to save specifying lots of switches. - -One specific thing to be decided, particularly for HTML, is -whether one is outputting a "cluster" of files (e.g., as javadoc -does). I reckon this can be left for later on, though (as can -such issues as saying "other interesting sources are *over -there*, so reference them if you can" - e.g., the Python -library). diff --git a/sandbox/tibs/pysource/pysource.py b/sandbox/tibs/pysource/pysource.py deleted file mode 100755 index a62c96ebb..000000000 --- a/sandbox/tibs/pysource/pysource.py +++ /dev/null @@ -1,262 +0,0 @@ -#! /usr/bin/env python -"""A simple command line interface for the pysource package - -This will become more interesting later on... -""" - -import os -import sys -import getopt - -import docutils.nodes -import docutils.utils - -# Local modules -import utils -import visit -import html - -import transform - -__docformat__ = "reST" - - -# ---------------------------------------------------------------------- -def rest_document(filename): - """Return an reST document. - """ - - from docutils.parsers.rst import Parser - - file = open(filename) - try: - text = file.read() - finally: - file.close() - - parser = Parser() - docroot = docutils.utils.new_document() - parser.parse(text,docroot) - return docroot - - -# ---------------------------------------------------------------------- -# I don't much like the Unix command line style (I always thought the -# VMS approach was much more flexible and sensible), but it *does* seem -# to be what people expect these days... - -options = [ - # long option, short, description - ("verbose", "v", "Report on progress in more detail"), - ("quiet", "q", "Suppress normal progress messages"), - ("text", "t", "The input file is a plain (text) reST file"), - ("show", "s", "Output basic information about the input"), - ("ast", "a", "Output a representation of the AST"), - ("xml", "x", "Output an XML representation of the input"), - ("html", "h", "Output an HTML representation of the input [default]"), - ("pretty", "p", "Output a 'pretty' representation of the input"), - ("doctest", "d", "Treat a reST file as doctest input."), - ("help", "h", "Show 'help' information"), - ("new", "n", "Use David Goodger's HTML Writer (sort of)"), - ("stdout", "", "Write output to stdout, instead of a file"), - ] - -def print_usage(): - docstring = visit.Docstring(main.__doc__) - docstring.show(sys.stdout) - print - print " <switches> are:" - print - for longopt, shortopt, description in options: - if shortopt: - print " -%s, --%-9s %s"%(shortopt,longopt,description) - else: - print " --%-9s %s"%(longopt,description) - -def main(): - """The command line interface to docutil's Python documentation extractor. - - Usage: ``pysource.py <switches> <inpath> [<outfile>]`` - - <inpath> is the path to a package or module. - - <outfile> is the path to the output file. If it's not given, then - output will be written to a file with the same name as the input - file, but defaulting to the current directory, and with extension - derived from the type of output: - - - show -> ``.show`` - - ast -> ``.ast`` - - xml -> ``.xml`` - - html -> ``.html`` - - pretty -> ``.pretty`` - - (unless --stdout is requested). The default is --html. - - Note that progress messages (and ``verb`` information) are written - to ``sys.stderr``. - """ - - if len(sys.argv) <= 1: - print "Not enough arguments" - print_usage() - return - - shortopts = ''.join([option[1] for option in options]) - longopts = [option[0] for option in options] - try: - opts, args = getopt.getopt(sys.argv[1:], shortopts, longopts) - except getopt.GetoptError,detail: - print "Problem with options:",detail - print_usage() - return - - verbose = 0 - quiet = 0 - input_format = "python" - output_format = "html" - want_docutilstree = 1 - new_writer = 0 - write_stdout = 0 - - for opt, arg in opts: - if opt in ["-?","--help"]: - print_usage() - return - elif opt in ["-n","--new"]: - new_writer = 1 - output_format = "html" - print >>sys.stderr, "Using new HTML Writer" - elif opt in ["-v","--verbose"]: - verbose = 1 - elif opt in ["-q","--quiet"]: - quiet = 1 - elif opt in ["-t","--text"]: - input_format = "text" - elif opt in ["-s","--show"]: - output_format = "show" - want_docutilstree = 0 - elif opt in ["-a","--ast"]: - output_format = "ast" - want_docutilstree = 0 - elif opt in ["-x","--xml"]: - output_format = "xml" - elif opt in ["-h","--html"]: - output_format = "html" - elif opt in ["-d","--doctest"]: - output_format = "doctest" - elif opt in ["-p","--pretty"]: - output_format = "pretty" - elif opt == "--stdout": - write_stdout = 1 - else: - raise getopt.GetoptError, "getopt should have saved us!" - - if len(args) == 0 or len(args) > 2: - print "Please specify an input and an (optional) output" - print_usage() - return - - if input_format == "text" and \ - output_format not in ["xml","html","pretty","doctest"]: - print "--text only supports --xml, --html, --pretty or --doctest" - print_usage() - return - - if input_format == "text" and output_format == "doctest" \ - and len(args) == 2: - print "--doctest does not accept an output file" - # Should it? - print_usage() - return - - if write_stdout and len(args) == 2: - print "It doesn't make sense to specify --stdout and an output file" - print_usage() - return - - filename = args[0] - if len(args) == 2: - if not quiet: - print >>sys.stderr, "... Output will go to",args[1] - outstream = open(args[1],"w") - elif write_stdout: - if not quiet: - print >>sys.stderr, "... Output will go to standard output" - outstream = sys.stdout - else: - head,tail = os.path.split(filename) - base,ext = os.path.splitext(tail) - outname = "%s.%s"%(base,output_format) - if not quiet: - print >>sys.stderr, "... Output will go to",outname - outstream = open(outname,"w") - - try: - if output_format == "doctest": - from doctest import Tester - if not quiet: print >>sys.stderr, "*** Doctesting the document" - t = Tester(globs={},verbose=verbose) - (fail,total) = t.runstring(open(filename).read(),filename) - if not quiet and not verbose: - print >>sys.stderr, "*** ", - if fail: - print "%d of"%fail, - if total == 1: - print "1 example", - else: - print "%d examples"%total, - if fail: - print "failed" - else: - print "passed" - elif input_format == "text": - if not quiet: print >>sys.stderr, "*** Making the document" - document = rest_document(filename) - else: - if os.path.isdir(filename): - print >>sys.stderr, "*** Parsing the Python package",filename - thing = visit.Package(None, filename) - else: - print >>sys.stderr, "*** Parsing the Python file",filename - thing = visit.Module(None, filename) - - if want_docutilstree: - if not quiet: - print >>sys.stderr, "*** Making the document" - with_groups = not new_writer - process = transform.Process(with_groups) - document = process(thing) - - if output_format == "show": - thing.show(outstream) - elif output_format == "ast": - thing.show_ast(outstream) - print - elif output_format == "pretty": - outstream.write(document.pformat(indent=" ")) - elif output_format == "xml": - if not quiet: print >>sys.stderr, "*** Producing a DOM tree" - domtree = document.asdom() - if not quiet: print >>sys.stderr, " Writing XML" - domtree.writexml(outstream,indent="", addindent=" ",newl="\n") - elif output_format == "html": - if not quiet: print >>sys.stderr, "*** Writing HTML" - if new_writer: - from docutils.writers.html4css1 import Writer - writer = Writer() - writer.write(document,outstream) - elif input_format == "text": - writer = html.Writer() - writer(document,outstream) - else: - writer = html.PythonWriter() - writer(document,outstream) - finally: - if outstream != sys.stdout: - outstream.close() - - -# ---------------------------------------------------------------------- -if __name__ == "__main__": - main() diff --git a/sandbox/tibs/pysource/rjhack.py b/sandbox/tibs/pysource/rjhack.py deleted file mode 100755 index 6046312ac..000000000 --- a/sandbox/tibs/pysource/rjhack.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python - -# Author: Richard Jones -# Contact: richard@users.sourceforge.net -# Revision: $Revision$ -# Date: $Date$ -# Copyright: This module has been placed in the public domain. - -""" -A front end to the Docutils Publisher, taking Python source and producing HTML. -""" - -import os, locale -try: - locale.setlocale(locale.LC_ALL, '') -except: - pass - -import visit, transform - -from docutils.parsers.rst import Parser -class SourceParser(Parser): - supported = ('pysource',) - settings_spec = ( - 'PySource Parser Options', - None, - (('Be verbose while parsing', ['--verbose-parse'], - {'action': 'store_true'}), - )) + Parser.settings_spec - def parse(self, filename, document): - if os.path.isdir(filename): - thing = visit.Package(document.settings, filename) - else: - thing = visit.Module(document.settings, filename) - process = transform.Process(with_groups=0, document=document) - process(thing) - -from docutils.readers import Reader -class SourceReader(Reader): - def read(self, source, parser, settings): - self.source = source - if not self.parser: - self.parser = parser - self.settings = settings - # we want the input as the filename, not the file content - self.source.source.close() - self.input = self.source.source_path - self.parse() - return self.document - -from docutils.core import publish_cmdline, default_description -description = ('Generates (X)HTML documents from Python sources. ' - + default_description) -parser = SourceParser() -reader = SourceReader(parser, 'pysource') -publish_cmdline(reader=reader, parser=parser, writer_name='html', - description=description) - diff --git a/sandbox/tibs/pysource/test/readme.txt b/sandbox/tibs/pysource/test/readme.txt deleted file mode 100644 index a6af4576d..000000000 --- a/sandbox/tibs/pysource/test/readme.txt +++ /dev/null @@ -1,27 +0,0 @@ -Files in the ``test`` directory -------------------------------- - -This directory is intended to contain various test file. They are (hopefully) -named in a consistent manner: - -README.txt - This file. - -``*.rtxt`` - Sample reST text files, suitable for checking odd things. - -``*.py`` - Sample Python test files, ditto. - -``*.doctest`` - Files that contain "semiliterate testing" [1]_ -- that is, - descriptive text (written with reST) surrounding doctest blocks. - The idea is that these files should be passed to (for instance) - pydps.py (or whatever it is called at the moment) to be tested by - Tim Peters' doctest - for instance:: - - pydps/pydps.py --text --doctest simple.doctest - - - -.. [1] a semi-lame reference to literate programming, I'm afraid. diff --git a/sandbox/tibs/pysource/test/test.doctest b/sandbox/tibs/pysource/test/test.doctest deleted file mode 100644 index bc91115ad..000000000 --- a/sandbox/tibs/pysource/test/test.doctest +++ /dev/null @@ -1,12 +0,0 @@ -The following is some Python code: - - >>> a = 1 - >>> a - 1 - -and here is some more: - - >>> a + 1 - 3 - -and that's all. diff --git a/sandbox/tibs/pysource/test/test.py b/sandbox/tibs/pysource/test/test.py deleted file mode 100755 index b3e380526..000000000 --- a/sandbox/tibs/pysource/test/test.py +++ /dev/null @@ -1,124 +0,0 @@ -"""A simple test file for input to ast-mining.py. - -But it isn't a "raw" string, so: - - \\*escape* \\`with` "\\\\" -""" - -__docformat__ = "reST" - -import string - -one = 1 - -"""Documentation after `one` (intervening blank line)""" - -two,three = 2,3 -"""Documenatation after ``two,three=2,3``""" - -four,five = two,three - -six = [four,five] -"""Documentation after ``six=[four,five]``""" - -global gmodule -gmodule = 1 -"""Global at the module level""" - -def func(a,b=1,c='jim',d=None,e=[],f={'a':1},g=(1,2,3)): - r"""Function at the module level - - This *is* a "raw" string, so: - - \*escape* \`with` "\\" - """ - - from string import lstrip,rstrip - - a = 3 - - global gfunc - gfunc = 1 - """Global referenced only within `func()`""" - - global one - one = 2 - - class InFunc: - pass - - def infunc(): - """Function defined inside function `func()`""" - global ginner - ginner = 1 - """Global referenced only within `infunc()`""" - - class Silly: - pass - - def silly_fn(): - pass - -def func2(a,*args,**kargs): - pass - -def func3((a,b,c),d): - pass - -class Fred: - """Class at the module level.""" - - a = 1 - """Documentation for class value `a`""" - - global gclass - gclass = 2 - """Global referenced only within `Fred`""" - - def __init__(self): - """Initialisation for `Fred`""" - self.b = 2 - """`self.b` within `Fred.__init__()`""" - - c = 3 - """Local variable within `Fred.__init__()`""" - - global gmeth - gmeth = 3 - """Global referenced only within a method""" - - global gagain - gagain = 3 - - class Insider: - """Class defined inside `Fred.__init__()`""" - - a = 'jim' - """Local name within class inside `Fred.__init__()`""" - - global gclass2 - gclass2 = 4 - """Global referenced only within a class within a method""" - - global gunused - global gagain - - def fred(self): - global gunused2,gfunc2 - gfunc2 = 5 - """Global referenced only within a method in a - class in a method - """ - - def fredinner(): - global ginner2 - global gagain - ginner2 = 6 - gagain = 6 - - infredinner = 7 - -class Jim(Fred): - pass - - diff --git a/sandbox/tibs/pysource/test/test.rtxt b/sandbox/tibs/pysource/test/test.rtxt deleted file mode 100644 index f28a3d4e2..000000000 --- a/sandbox/tibs/pysource/test/test.rtxt +++ /dev/null @@ -1,442 +0,0 @@ -pydps test document -=================== - -:Author: Tibs -:Version: 0.1 -:Eggs: Green -:Ham: Green - -This is intended to be a simple test document, with "something of everything". -It is *not* intended to trawl for *problems*, but just to check I've got the -writing out of just about everything supported in the HTML writer. - - :Author: Tibs - :Version: 0.1 - :Eggs: Green - :Ham: Green - :Parameter i: integer - -Lists ------ -1. A list -2. Some more -3. - - * What's this? - - and this? - + and this? - -i. And this is Roman - -a. And this is alpha - -A. And so is this - -I. What is this? - -.. Just a comment, honest. - That's all. - -A title - and some text. -And another - and more text. - -Short options: - --a option a - --b file option b - --cname option c - -Long options: - ---aaaa option aaaa ---bbbb=file option bbbb ---cccc name option cccc ---d-e-f-g option d-e-f-g ---h_i_j_k option h_i_j_k - -VMS/DOS-style options: - -/A option A -/B file option B -/Cstring option C - -Mixed short, long, and VMS/DOS options: - --a option a ---bbbb=file option bbbb -/C option C ---dddd name option dddd --e string option e -/F file option F - -Aliased options: - --a, --aaaa, /A option a, aaaa, A --b file, --bbbb=file, /B file option b, bbbb, B - -Multiple lines in descriptions, aligned: - --a option a, line 1 - line 2 --b file option b, line 1 - line 2 - -Multiple lines in descriptions, not aligned: - --a option a, line 1 - line 2 --b file option b, line 1 - line 2 - -Some edge cases: - ---option=arg arg too many arguments - ---option=arg=arg too many arguments - --aletter arg too many arguments (-a letter) - -/Aletter arg too many arguments (/A letter) - --a=b can't use = for short arguments - -/A=b can't use = for DOS/VMS arguments? - ---option= argument missing - ---=argument option missing - --- everything missing - -- this should be a bullet list item - -+ bullet - -.. Comments swallow up all indented text following. - - (Therefore this is not a) block quote. - -- bullet - - If we want a block quote after this bullet list item, - we need to use an empty comment: - -.. - - Block quote. - -Other blocks ------------- - -:: - - Anonymous literal block - -This is a literal block:: - - This here. - -And we don't need colons :: - - Not really. - -Of course, **I** like Python: - - >>> print " Indented block & output." - Indented block & output. - -Or maybe I don't:: - - >>> print " Indented block & output." - Indented block & output. - -Footnotes ---------- - -Autonumbered footnotes -...................... -[#]_ is the first auto-numbered footnote reference. -[#]_ is the second auto-numbered footnote reference. - -.. [#] Auto-numbered footnote 1. -.. [#] Auto-numbered footnote 2. -.. [#] Auto-numbered footnote 3. - -[#]_ is the third auto-numbered footnote reference. - -Autonumbered and named footnotes -................................ -[#six]_ is a reference to the sixth auto-numbered footnote. - -.. [#four] Fourth auto-numbered footnote. -.. [#five] Fifth auto-numbered footnote. -.. [#six] Sixth auto-numbered footnote. - -[#five]_ is a reference to the fifth auto-numbered footnote. -[#four]_ is a reference to the fourth auto-numbered footnote. -[#six]_ is another reference to the sixth auto-numbered footnote. - -Here are some internal cross-references to the implicit targets -generated by the footnotes: four_, five_, six_. - -Mixed anonymous and labelled auto-numbered footnotes -.................................................... -[#ten]_ should be 10, [#]_ should be 7, -[#]_ should be 9, [#]_ is one too many, -[#eight]_ should be 8, and [#twelve]_ doesn't exist, -nor does [#eleven]_. - -.. [#] Auto-numbered footnote 7. -.. [#eight] Auto-numbered footnote 8. -.. [#] Auto-numbered footnote 9. -.. [#ten] Auto-numbered footnote 10. -.. [#eleven] Auto-numbered footnote 11. -.. [#eleven] Auto-numbered footnote 11 again (duplicate). - -Inline stuff ------------- -(**strong**) but not (**) or '(** ' or x**2 or \\**kwargs or ** - -(however, '**kwargs' will trigger a warning and may be problematic) - -Strong asterisk: ***** - -Strong double asterisk: ****** - -``literal`` -``lite\\ral`` - -``literal ``TeX quotes'' & \\backslash`` but not "``" or `` - -(however, ``standalone TeX quotes'' will trigger a warning -and may be problematic) - -Find the ```interpreted text``` in this paragraph! - -`interpreted` -:role:`interpreted` - -`interpreted` but not \\`interpreted` [`] or ({[`] or [`]}) or ` - -`interpreted`-text `interpreted`: text `interpreted`:text `text`'s interpreted - -http://www.standalone.hyperlink.com - -one-slash-only:/absolute.path - -mailto:someone@somewhere.com - -news:comp.lang.python - -An email address in a sentence: someone@somewhere.com. - -ftp://ends.with.a.period. - -(a.question.mark@end?) - -Tables ------- - -+-------------------------------------+ -| A table with one cell and one line. | -+-------------------------------------+ - -+-----------------------+ -| A malformed table. | -+-----------------------+ - -+------------------------+ -| A well-formed | table. | -+------------------------+ - -+------------------------+ -| This +----------+ too! | -+------------------------+ - -+--------------------------+ -| A table with three rows, | -+------------+-------------+ -| and two | columns. | -+------------+-------------+ -| First and last rows | -| contain column spans. | -+--------------------------+ - -+------------+-------------+---------------+ -| A table | two rows in | and row spans | -| with three +-------------+ to left and | -| columns, | the middle, | right. | -+------------+-------------+---------------+ - -+------------+-------------+---------------+ -| A table | | two rows in | and funny | -| with 3 +--+-------------+-+ stuff. | -| columns, | the middle, | | | -+------------+-------------+---------------+ - -+-----------+-------------------------+ -| W/NW cell | N/NE cell | -| +-------------+-----------+ -| | Middle cell | E/SE cell | -+-----------+-------------+ | -| S/SE cell | | -+-------------------------+-----------+ - -+--------------+-------------+ -| A bad table. | | -+--------------+ | -| Cells must be rectangles. | -+----------------------------+ - -+-------------------------------+ -| A table with two header rows, | -+------------+------------------+ -| the first | with a span. | -+============+==================+ -| Two body | rows, | -+------------+------------------+ -| the second with a span. | -+-------------------------------+ - -+-------------------------------+ -| A table with two head/body | -+=============+=================+ -| row | separators. | -+=============+=================+ -| That's bad. | | -+-------------+-----------------+ - -The traditional: - - +------------------------+------------+----------+----------+ - | Header row, column 1 | Header 2 | Header 3 | Header 4 | - +========================+============+==========+==========+ - | body row 1, column 1 | column 2 | column 3 | column 4 | - +------------------------+------------+----------+----------+ - | body row 2 | Cells may span columns. | - +------------------------+------------+---------------------+ - | body row 3 | Cells may | - Table cells | - +------------------------+ span rows. | - contain | - | body row 4 | | - body elements. | - +------------------------+------------+---------------------+ - -+-----------------+--------+ -| A simple table | cell 2 | -+-----------------+--------+ -| cell 3 | cell 4 | -+-----------------+--------+ -No blank line after table. - -+-----------------+--------+ -| A simple table | cell 2 | -+-----------------+--------+ -| cell 3 | cell 4 | -+-----------------+--------+ - Unexpected indent and no blank line after table. - -+------------------------------+ -| This table contains another. | -| | -| +-------------------------+ | -| | A table within a table. | | -| +-------------------------+ | -+------------------------------+ - -+------------------+--------+ -| A simple table | | -+------------------+--------+ -| with empty cells | | -+------------------+--------+ - -Links and so on ---------------- -.. _target: - -(internal hyperlink) - -.. _one-liner: http://structuredtext.sourceforge.net - -.. _starts-on-this-line: http:// - structuredtext. - sourceforge.net - -.. _entirely-below: - http://structuredtext. - sourceforge.net - -.. _target1: Not a proper hyperlink target - -.. _a long target name: - -.. _`a target name: including a colon (quoted)`: - -.. _a target name\: including a colon (escaped): - -.. _target2: http://www.python.org/ - -(indirect external hyperlink) - -Duplicate indirect links (different URIs): - -.. _target3: first - -.. _target3: second - -Duplicate indirect links (same URIs): - -.. _target4: first - -.. _target4: first - -Directives ----------- -.. Attention:: Directives at large. - -.. Note:: This is a note. - -.. Tip:: 15% if the - service is good. - -- .. WARNING:: Strong prose may provoke extreme mental exertion. - Reader discretion is strongly advised. -- .. Error:: Does not compute. - -.. Caution:: - - Don't take any wooden nickels. - -.. DANGER:: Mad scientist at work! - -.. Important:: - - Wash behind your ears. - - Clean up your room. - - Call your mother. - - Back up your data. - -.. image:: images/ball.gif - -.. image:: - picture.png - [height=100 width=200 scale=50] - -.. figure:: picture.png - - A picture with a caption. - -.. figure:: picture.png - - A picture with a caption and a legend. - - +-----------------------+-----------------------+ - | Symbol | Meaning | - +=======================+=======================+ - | .. image:: tent.png | Campground | - +-----------------------+-----------------------+ - | .. image:: waves.png | Lake | - +-----------------------+-----------------------+ - | .. image:: peak.png | Mountain | - +-----------------------+-----------------------+ diff --git a/sandbox/tibs/pysource/test/testassign.py b/sandbox/tibs/pysource/test/testassign.py deleted file mode 100755 index e4d70a356..000000000 --- a/sandbox/tibs/pysource/test/testassign.py +++ /dev/null @@ -1,12 +0,0 @@ -class Jim: - - def __init__(self): - a = 1 - a = [2,3] - a,b = 4,5 - a,b = [6,7] - a,self.a = 8,9 - a = b = self.a = 10 - a,b = c,d = 11,12 - self.a,self.b = 13,14 - self.a = self.b = 15 diff --git a/sandbox/tibs/pysource/test/testfunc.py b/sandbox/tibs/pysource/test/testfunc.py deleted file mode 100755 index 4a887b098..000000000 --- a/sandbox/tibs/pysource/test/testfunc.py +++ /dev/null @@ -1,34 +0,0 @@ -# Stuff needed to make the examples compile... -class Fred: - def __getitem__(self,index): - pass -a = Fred() -b = [1,2,3,4] -d = Fred() - -def func1(a,b=1,c='jim',d=None,e=[],f={'a':1,a:1},g=(1,2,3)): - pass - -def func2(a,*args,**kargs): - pass - -def func2a(a,*args): - pass - -def func2b(a,**kargs): - pass - -def func3((a,b,c),d): - pass - -def func4(a=a,b=Fred,c=Fred()): - pass - -def func5(a=[x for x in [1,2,3] if x > 2], - b=[(x,y) for x in [1,2,3] for y in [3,4,5]]): - pass - -def func6(a=b[1:2],c=d[1,4:6,...,8:9:10], - e=lambda x: x*2, - f=lambda x,a=a: func5(x,a)): - pass diff --git a/sandbox/tibs/pysource/test/testinterpreted.py b/sandbox/tibs/pysource/test/testinterpreted.py deleted file mode 100755 index 00eb59311..000000000 --- a/sandbox/tibs/pysource/test/testinterpreted.py +++ /dev/null @@ -1,44 +0,0 @@ -"""Test interpreted text - -This module (`testinterpreted`) contains one :class:`Fred` -(and one hopes `Fred` will also work) and a function *and* -a method called `fred`. - -NB: the above sentence *should*, when transformed into HTML -or some other output format, hopefully, contain text something -like:: - - contains one class Fred - -i.e., the ``:class:`Fred``` should probably cause the "insertion" -of the word "class". Or so I think. -""" #' - -__docformat__ = "reST" - -class Fred: - """This class (`Fred`), in :module:`testinterpreted`, contains - a method `fred`. - """ - - jim = None - """A class value, a default. - """ - - def fred(self): - """This method (`fred`) is in class `Fred` - """ - fred = 3 - """This name (`fred`) is in :function:`fred` in class `Fred`. - """ - -def fred(fred): - """This function (:function:`fred`) is *not* in class `Fred`. - - It has one argument, :parameter:`fred`. - - If I just say `fred`, what *should* that resolve to? - """ - fred.jim = 3 - """We assume that `fred` must be something with a `jim` attribute... - """ diff --git a/sandbox/tibs/pysource/test/testinterpreted2.py b/sandbox/tibs/pysource/test/testinterpreted2.py deleted file mode 100755 index 59dac777a..000000000 --- a/sandbox/tibs/pysource/test/testinterpreted2.py +++ /dev/null @@ -1,37 +0,0 @@ -""" -This is the example from David Goodger's DPS document pysource-reader.txt. -""" - -__docformat__ = "restructuredtext" - -class Keeper(Storer): - - """ - Extend `Storer`. Class attribute `instances` keeps track of - the number of `Keeper` objects instantiated. - """ - - instances = 0 - """How many `Keeper` objects are there?""" - - def __init__(self): - """ - Extend `Storer.__init__()` to keep track of instances. - - Keep count in `self.instances` and data in `self.data`. - """ - Storer.__init__(self) - self.instances += 1 - - self.data = [] - """Store data in a list, most recent last.""" - - def storedata(self, data): - """ - Extend `Storer.storedata()`; append new `data` to a list - (in `self.data`). - - Use :method:`Keeper.storedata` to store the object's data in - `Keeper.data`:instance_attribute:. - """ - self.data = data diff --git a/sandbox/tibs/pysource/test/testsimp.py b/sandbox/tibs/pysource/test/testsimp.py deleted file mode 100755 index 7dfeb4202..000000000 --- a/sandbox/tibs/pysource/test/testsimp.py +++ /dev/null @@ -1,12 +0,0 @@ -a = 'b' -class Fred: - """A *silly* demonstration.""" - - def __init__(self, b=1, c='jim', d=None, f={'a':1,a:1}, - g=[x for x in [1,2,3] if x > 2]): - """Initialise ourselves.""" - self.list = g - - def __call__(self,a): - """Call ourselves.""" - pass diff --git a/sandbox/tibs/pysource/test/testyield.py b/sandbox/tibs/pysource/test/testyield.py deleted file mode 100755 index e3fd1d2d0..000000000 --- a/sandbox/tibs/pysource/test/testyield.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import generators -from string import lower as lll -from string import lower as lll, upper as uuu, lower, lower as xxx - -def fred(): - a = 1 - yield a - diff --git a/sandbox/tibs/pysource/transform.py b/sandbox/tibs/pysource/transform.py deleted file mode 100755 index f93f17ed4..000000000 --- a/sandbox/tibs/pysource/transform.py +++ /dev/null @@ -1,358 +0,0 @@ -"""Transfer the structure from visit.py into a DOCUTILS node tree. - -TODO: ensure that the encoding of individual items makes sense. - -This should probably be using David's new mechanisms from the docutils module -to build the DOCUTILS tree - but they were written after this was coded, and -I've still to investigate them. -""" - -import string - -import docutils.nodes -import docutils.utils - -import visit -import utils -import buildtree - -__docformat__ = "reST" - - -# ---------------------------------------------------------------------- -class Process: - """The infrastucture that knows how to produce our DOCUTILS node tree. - """ - - def __init__(self, with_groups=1, document=None): - """Instantiate our transformation to a DOCUTILS tree. - - At the moment, whilst we are considering both my own old HTML - output and also moving towards David Goodger's DOCUTILS HTML output, - we have the ``with_groups`` argument - if this is false then - buildtree will not output "group" elements... - """ - self.current_module = None - self.dps = buildtree.BuildTree(with_groups=with_groups, root=document) - - def __call__(self,thing): - """Produce a DOCUTILS 'document' node and attach our information to it - """ - self.dps.start("document") - - if thing.__class__ == visit.Package: - self.add_package(thing) - elif thing.__class__ == visit.Module: - self.add_module(thing) - else: - raise ValueError,"Argument must be a Package or Module,"\ - " not a %s"%thing.__class__.__name__ - - self.dps.end("document") - document = self.dps.finish() - - # David's HTML writer requires the document to have a "source" - # attribute - so add a temporary one (something better should - # be done later on - see what David does!) - document["source"] = "pysource" - return document - - def add_package(self,thing): - """Process a package and produce DOCUTILS nodes for it. - """ - - self.dps.start("section",style="package") - self.dps.add("target",name=thing.label()) - self.dps.add("title",self.title(thing)) - - self.dps.start("group",style="details") - self.dps.add("paragraph","Path: "+thing.directory) - self.dps.end("group") - - for mod in thing.getModules(): - self.add_module(mod) - - self.dps.end("section") - - def add_module(self,thing): - """Process a module and produce DOCUTILS nodes for it. - """ - - self.current_module = thing - - self.dps.add("target",name=thing.label()) - self.dps.start("section",style="module") - self.dps.add("title",self.title(thing)) - - self.dps.start("group",style="details") - self.dps.start("paragraph","Full name: ") - self.dps.add("emphasis",thing.fullname) - self.dps.end("paragraph") - self.dps.add("paragraph","Path: "+thing.filename) - self.dps.end("group") - - self.add_generic(thing) - - obscure = thing.getObscureGlobals() - if obscure: - self.dps.start("group",style="obscure_globals") - self.dps.start("paragraph") - self.dps.add("strong","Globals in obscure places") - self.dps.start("bullet_list") - - for name,places in thing.getObscureGlobals(): - self.dps.start("list_item") - self.dps.start("paragraph") - self.dps.add("literal",name) - self.dps.add("text"," defined and used in") - if len(places) == 1: - self.dps.add("text"," ") - self.dps.add("literal",places[0]) - else: - self.dps.add("text",":") - self.dps.start("bullet_list") - for place in places: - self.dps.add("list_item", - self.dps.make("literal",place)) - self.dps.end("bullet_list") - self.dps.end("list_item") - self.dps.end("bullet_list") - self.dps.end("group") - - self.dps.end("section") - - def add_generic(self,thing): - """Basic stuff, common to just about all things - """ - - # Side effects, all is side effects... - self.add_docstring(thing) - self.add_imports(thing) - self.add_attributes(thing) - self.add_classes(thing) - self.add_functions(thing) - - def add_docstring(self,thing): - is_function = (thing.__class__ in [visit.Method,visit.Function]) - - if not thing.docstring and not is_function: - return - - self.dps.start("group",style="docstring") - - if is_function: - args = thing.getArgs() - self.dps.start("paragraph") - # Make the function/method name easily findable... - self.dps.start("literal",style="funcall") - self.dps.add("text",thing.name) - self.dps.end("literal") - # And then we have the arguments... - self.dps.add("target",name=thing.args_label()) - self.dps.start("literal",style="funargs") - self.dps.add("text","(%s)"%string.join(args,", ")) - self.dps.end("literal") - self.dps.end("paragraph") - - if thing.docstring: - # Insert the docstring into our tree structure - thing.docstring.add_to_DPS(self.dps) - - self.dps.end("group") - - # ############################################################# - # Should we sort things alphabetically within each list, below? - # - maybe yes for some, but no for others... - # ############################################################# - - def add_imports(self,thing): - """Work out a description of any imports. - - `thing` is the visitor node that we are to use to construct - DOCUTILS nodes. - """ - - imports = thing.getImports() - froms = thing.getFromImports() - - if not imports and not froms: - return - - self.dps.add("target",name=thing.label()) - self.dps.start("group",style="imports") - self.dps.start("paragraph") - self.dps.add("strong","Imports:") - self.dps.start("bullet_list") - - if imports: - names = imports.keys() - names.sort() - for name in names: - self.dps.start("list_item") - self.dps.start("paragraph","import ") - self._add_import(name,imports) - self.dps.end("list_item") - - if froms: - #print froms - modnames = froms.keys() - modnames.sort() - for modname in modnames: - self.dps.start("list_item") - self.dps.start("paragraph","from ") - self.dps.add("literal",modname) - self.dps.add("text"," import ") - dict = froms[modname] - names = dict.keys() - names.sort() - uselist = len(names) > 1 - if uselist: - self.dps.start("bullet_list") - for name in names: - if uselist: - self.dps.start("list_item") - self._add_import(name,dict) - self.dps.end("list_item") - else: - self._add_import(name,dict) - if uselist: - self.dps.end("bullet_list") - self.dps.end("list_item") - - self.dps.end("bullet_list") - self.dps.end("group") - - def _add_import(self,name,import_dict): - self.dps.add("literal",name) - aslist = import_dict[name] - if len(aslist) == 1 and aslist[0] == None: - self.dps.add("text"," (as "); - self.dps.add("emphasis","itself"); - self.dps.add("text",")"); - else: - self._add_aslist(aslist) - - def _add_aslist(self,aslist): - self.dps.add("text"," as ") - another = 0 - for name in aslist: - if another: - self.dps.add("text",", ") - else: - another = 1 - if name is None: - self.dps.add("emphasis","itself") - else: - self.dps.add("literal",name) - - def add_attributes(self,thing): - attributes = thing.getAttributeNames() - if not attributes: - return - - # We don't do attributes for functions, or for methods that are - # not called "__init__" or "__new__" (the DOCUTILS spec doesn't require - # retention of attributes in "__new__" methods, but it seems sensible - # to me, and I'm not sure they were around when the DOCUTILS spec was - # first written...) - if thing.__class__ == visit.Function: - return - if thing.__class__ == visit.Method and \ - thing.name not in ("__init__","__new__"): - return - - self.dps.start("group",style="attributes") - self.dps.start("paragraph") - self.dps.add("strong","Attributes:") - self.dps.start("bullet_list") - - for name in attributes: - self.dps.start("list_item") - nameinst = thing.getAttribute(name) - self.dps.start("paragraph") - self.dps.add("target",name=nameinst.label()) - self.dps.add("literal",nameinst.getAsString()) - if nameinst.isGlobal(): - self.dps.add("text"," ") - self.dps.add("emphasis","(global)") - if nameinst.__class__ == visit.InstanceValue and \ - nameinst.see_also: - self.dps.add("text"," - see also class instance ") - self.dps.add("a",nameinst.our_class.name, - href="#"+nameinst.our_class.label()) - self.add_docstring(nameinst) - self.dps.end("list_item") - - self.dps.end("bullet_list") - self.dps.end("group") - - def add_classes(self,thing): - class_names = thing.getClassNames() - for name in class_names: - class_instance = thing.getClass(name) - self.add_class(class_instance) - - def add_class(self,thing): - """Process a class and produce DOCUTILS nodes for it. - """ - - self.dps.add("target",name=thing.label()) - self.dps.start("section",style="class") - self.dps.add("title",self.title(thing)) - - self.dps.start("group",style="details") - self.dps.start("paragraph","Full name: ") - self.dps.add("emphasis",thing.fullname) - - names = thing.getBaseNames() - if names: - self.dps.start("paragraph","Inherits from: ") - done_one = 0 - for name in names: - if done_one: - self.dps.add("text",", ") - else: - done_one = 1 - self.dps.add("literal",name) - self.dps.end("group") - - self.add_generic(thing) - self.dps.end("section") - - def add_functions(self,thing): - """Add information about any functions or methods. - """ - - func_names = thing.getFunctionNames() - for name in func_names: - fun_instance = thing.getFunction(name) - self.add_function(fun_instance) - - def add_function(self,thing): - """Process a function or method and produce DOCUTILS nodes for it. - """ - - if thing.__class__ == visit.Method: - style = "method" - else: - style = "function" - self.dps.add("target",name=thing.label()) - self.dps.start("section",style=style) - self.dps.add("title",self.title(thing)) - - self.dps.start("group",style="details") - self.dps.start("paragraph","Full name: ") - self.dps.add("emphasis",thing.fullname) - self.dps.end("group") - - if thing.generator: - self.dps.start("group",style="generator") - self.dps.add("paragraph","%s %s is actually a generator"%\ - (thing.__class__.__name__,thing.name)) - self.dps.end("group") - - self.add_generic(thing) - self.dps.end("section") - - def title(self,thing): - return "%s %s"%(thing.__class__.__name__,thing.name) diff --git a/sandbox/tibs/pysource/utils.py b/sandbox/tibs/pysource/utils.py deleted file mode 100755 index 8823c9265..000000000 --- a/sandbox/tibs/pysource/utils.py +++ /dev/null @@ -1,474 +0,0 @@ -"""Utilities for pysource -""" - -import types -import string -import compiler - -# We'd better have at least *one* module in this package that demonstrates -# *not* using reST for our docstrings... -__docformat__ = "none" - - -# ---------------------------------------------------------------------- -PLAINTEXT = "plaintext" -RESTRUCTUREDTEXT = "restructuredtext" - -canonical_format = { "plaintext" : PLAINTEXT, - "plain" : PLAINTEXT, - "none" : PLAINTEXT, - "rst" : RESTRUCTUREDTEXT, - "rest" : RESTRUCTUREDTEXT, - "rtxt" : RESTRUCTUREDTEXT, - "restructuredtext" : RESTRUCTUREDTEXT, - } - -def docformat(text): - """Interpret a module's __docformat__ string. - - Returns a tuple of (format,language) - """ - if text == None: - return PLAINTEXT,"en" - - words = string.split(string.lower(text)) - - #print words - - if len(words) == 0: - return PLAINTEXT,"en" - elif len(words) > 2: - raise ValueError,"__docformat__ may be at most two 'words'" - - if len(words) == 2: - language = string.lower(words[1]) - else: - language = "en" - - try: - format = canonical_format[string.lower(words[0])] - except KeyError: - legal = canonical_format.keys() - legal.sort() - raise ValueError,"__docformat__ should be one of %s"%legal - - return format,language - - -# ---------------------------------------------------------------------- -def flatten(item): - """Retrieve some simpler representation of our AST. - - (and it's not meant to be 'theoretically' wonderful, just something - I can look at to work out how an AST works...) - """ - if isinstance(item,compiler.ast.Node): - things = [item.__class__.__name__] - children = item.getChildren() - for child in children: - things.append(flatten(child)) - return things - else: - return [item] - - -# ---------------------------------------------------------------------- -def treeprint(stream,item,indent=0): - """Simple pretty printer for the AST.""" - if isinstance(item,compiler.ast.Node): - stream.write("\n%s<%s>"%(" "*indent,item.__class__.__name__)) - - children = item.getChildren() - for child in children: - treeprint(stream,child,indent+2) - - # Fake our docstring as a sub-node (it's *really* more an attribute) - if hasattr(item,"docstring"): - stream.write("\n%s <docstring> %s"%(" "*indent,item.docstring)) - - # And ditto for a remembered assignment expression - if hasattr(item,"assign_expr"): - stream.write("\n%s <assign_expr>"%(" "*indent)) - treeprint(stream,item.assign_expr,indent+4) - else: - stream.write(" ") - stream.write(`item`) - - -# ---------------------------------------------------------------------- -def find_attr_docs(tree,verbose=0): - """Find candidates for documented attributes - - Note that after this, it may be that the AST will not garbage collect - its own nodes properly anymore, as we are adding in cross-linkages. - """ - - if not isinstance(tree,compiler.ast.Node): - return - - children = tree.getChildren() - - # Might as well get our recursion done with first... - for child in children: - find_attr_docs(child,verbose) - - # I believe that only Stmt nodes can have Assign and Discard - # nodes as children - if not isinstance(tree,compiler.ast.Stmt): - return - - if len(children) == 0: - return - - pairs = [] - last = children[0] - for item in children[1:]: - pairs.append((last,item)) - last = item - - for this,next in pairs: - if isinstance(this,compiler.ast.Assign) and \ - isinstance(next,compiler.ast.Discard): - if verbose: - print - print - print "*** Attribute docstring candidate" - treeprint(this,4) - treeprint(next,4) - print - - nextexpr = next.expr - if isinstance(nextexpr,compiler.ast.Const): - if type(nextexpr.value) == types.StringType: - docstring = nextexpr.value - else: - if verbose: - print - print "... Discarded constant is not a string" - continue - else: - if verbose: - print - print "... Discarded expression is not a constant" - continue - - # If there is more than one assignment attached to - # the <Assign> node, we are not interested - if len(this.nodes) > 1: - if verbose: - print - print "... (but there are too many assignments in the <Assign>)" - continue - - target = this.nodes[0] - if isinstance(target,compiler.ast.AssName): - # Let's be cheeky and glue the docstring on... - target.docstring = docstring - elif isinstance(target,compiler.ast.AssAttr): - # Let's be cheeky and glue the docstring on... - target.docstring = docstring - else: - if verbose: - print - print "... (but the assignment is to a tuple/list/etc.)" - continue - - if verbose: - print - print "Becomes:" - treeprint(this,4) - - -# ---------------------------------------------------------------------- -def find_attr_vals(tree,verbose=0): - """Find attributes whose values we're interested in. - - Clearly, when this is working, it could do with being "folded" into - `find_attr_docs()`. - - Note that after this, it may be that the AST will not garbage collect - its own nodes properly anymore, as we are adding in cross-linkages. - """ - - if not isinstance(tree,compiler.ast.Node): - return - - children = tree.getChildren() - - # Might as well get our recursion done with first... - for child in children: - find_attr_vals(child,verbose) - - # I believe that only Stmt nodes can have Assign and Discard - # nodes as children - if not isinstance(tree,compiler.ast.Stmt): - return - - for this in children: - if isinstance(this,compiler.ast.Assign): - if verbose: - print - print - print "*** Assignment - name/value candidate" - treeprint(this,4) - print - - # If there is more than one assignment attached to - # the <Assign> node, we are not interested - if len(this.nodes) > 1: - if verbose: - print - print "... (but there are too many assignments in the <Assign>)" - continue - - target = this.nodes[0] - if isinstance(target,compiler.ast.AssName) or \ - isinstance(target,compiler.ast.AssAttr): - # Let's be cheeky and glue the associated expression on... - target.assign_expr = this.expr - else: - if verbose: - print - print "... (but the assignment is to a tuple/list/etc.)" - continue - - if verbose: - print - print "Becomes:" - treeprint(this,4) - - -# ---------------------------------------------------------------------- -def stringify_arg(thing): - """Return a string representation of a function argument. - - This just works for tuples of (strings or tuples (of strings ...) ...) - """ - if type(thing) == types.StringType: - return thing - elif type(thing) == types.TupleType: - innards = [] - for item in thing: - innards.append(stringify_arg(item)) - return "(" + string.join(innards,",") + ")" - else: - raise ValueError,"Tried to stringify type %s"%type(thing) - - -# ---------------------------------------------------------------------- -def merge_args(args,defaults): - """Merge together arguments and defaults from an argument list. - - Returns a list of argument strings. - """ - if args == None: - return [] - - if defaults == None: - defaults = [] - - # This is horrible - do it nicely later on! - argstrs = [] - for item in args: - argstrs.append(stringify_arg(item)) - - pos = len(args) - len(defaults) - next = 0 - for index in range(pos,len(args)): - thing = defaults[next] - thing = stringify_expr(thing) - argstrs[index] = "%s=%s"%(argstrs[index],thing) - next = next + 1 - return argstrs - - -# ---------------------------------------------------------------------- -def stringify_expr(thing): - """Return a very simple string representation of an expression node - - Specifically, this function aims to support stringifying things - which can be on the RHS of an assignment - is that *actually* the - same as stringifying expression nodes? - """ - - # Humph - saving typing may be a good thing... - strify = stringify_expr - - #print thing.__class__.__name__ - - if thing == None: - return 'None' - elif isinstance(thing,compiler.ast.Add): - return strify(thing.left) + " + " + strify(thing.right) - elif isinstance(thing,compiler.ast.And): - exprs = [] - for node in thing.nodes: - exprs.append(strify(node)) - return string.join(exprs," && ") - elif isinstance(thing,compiler.ast.AssAttr): - # Attribute as target of assignment - if thing.flags == compiler.consts.OP_ASSIGN: - return strify(thing.expr) + "." + thing.attrname - else: - raise ValueError,"Unexpected flag %d in %s"%(thing.flags,`thing`) - elif isinstance(thing,compiler.ast.AssName): - # Name as target of assignment, but this also means name - # as target of "in" assignment (e.g., "x in [1,2,3]"), - # which is why *we're* interested in it (since this can - # occur in list comprehensions, which can occur as the - # RHS of assignments) - if thing.flags == compiler.consts.OP_ASSIGN: - return thing.name - else: - raise ValueError,"Unexpected flag %d in %s"%(thing.flags,`thing`) - elif isinstance(thing,compiler.ast.Backquote): - return "`" + strify(thing.expr) + "`" - elif isinstance(thing,compiler.ast.Bitand): - exprs = [] - for node in thing.nodes: - exprs.append(strify(node)) - return string.join(exprs," & ") - elif isinstance(thing,compiler.ast.Bitor): - exprs = [] - for node in thing.nodes: - exprs.append(strify(node)) - return string.join(exprs," | ") - elif isinstance(thing,compiler.ast.Bitxor): - exprs = [] - for node in thing.nodes: - exprs.append(strify(node)) - return string.join(exprs," ^ ") - elif isinstance(thing,compiler.ast.CallFunc): - # Yuck - this is getting complicated! - # (for an example, see method `hyperlink_target` in - # restructuredtext/states.py) - str = strify(thing.node) + "(" - arglist = [] - if thing.args: - for arg in thing.args: - arglist.append(strify(arg)) - if thing.star_args: - arglist.append("*"+strify(thing.star_args)) - if thing.dstar_args: - arglist.append("**"+strify(thing.dstar_args)) - if arglist: - str += string.join(arglist,", ") - return str+")" - elif isinstance(thing,compiler.ast.Compare): - str = strify(thing.expr) + " " - for op,val in thing.ops: - str += op + " " + strify(val) - return str - elif isinstance(thing,compiler.ast.Const): - # Try not to let long strings take up too much room... - value = thing.value - if type(value) == type("") and len(value) > 50: - value = value[:47] + "..." - # Make Python decide for us if it needs quotes round it - # (and, if so, what sort) - return `value` - elif isinstance(thing,compiler.ast.Dict): - innards = [] - for key,val in thing.items: - key = strify(key) - val = strify(val) - innards.append(key+":"+val) - return "{" + string.join(innards,", ") + "}" - elif isinstance(thing,compiler.ast.Div): - return strify(thing.left) + " / " + strify(thing.right) - elif isinstance(thing,compiler.ast.Ellipsis): - return "..." - elif isinstance(thing,compiler.ast.Getattr): - return strify(thing.expr) + "." + thing.attrname - elif isinstance(thing,compiler.ast.Invert): - # Bitwise negation - return "~" + strify(thing.expr) - elif isinstance(thing,compiler.ast.Keyword): - # An 'arg=value' within a function call - return thing.name + "=" + strify(thing.expr) - elif isinstance(thing,compiler.ast.Lambda): - str = "lambda " - if thing.flags != 0: - str += " <flag %d> "%thing.flags - str += string.join(merge_args(thing.argnames,thing.defaults),", ") - str += ": " - str += strify(thing.code) - return str - elif isinstance(thing,compiler.ast.LeftShift): - return strify(thing.left) + " << " + strify(thing.right) - elif isinstance(thing,compiler.ast.List): - innards = [] - for item in thing.nodes: - innards.append(strify(item)) - return "[" + string.join(innards,", ") + "]" - elif isinstance(thing,compiler.ast.ListComp): - str = "["+strify(thing.expr) - for node in thing.quals: - str += " "+strify(node) - return str+"]" - elif isinstance(thing,compiler.ast.ListCompFor): - str = "for "+strify(thing.assign) - str += " in "+strify(thing.list) - if thing.ifs: - for node in thing.ifs: - str += " "+strify(node) - return str - elif isinstance(thing,compiler.ast.ListCompIf): - return "if "+strify(thing.test) - elif isinstance(thing,compiler.ast.Mod): - return strify(thing.left) + "%" + strify(thing.right) - elif isinstance(thing,compiler.ast.Mul): - return strify(thing.left) + " * " + strify(thing.right) - elif isinstance(thing,compiler.ast.Name): - return thing.name - elif isinstance(thing,compiler.ast.Not): - return "not " + strify(thing.expr) - elif isinstance(thing,compiler.ast.Or): - exprs = [] - for node in thing.nodes: - exprs.append(strify(node)) - return string.join(exprs," || ") - elif isinstance(thing,compiler.ast.Power): - return strify(thing.left) + " ** " + strify(thing.right) - elif isinstance(thing,compiler.ast.RightShift): - return strify(thing.left) + " >> " + strify(thing.right) - elif isinstance(thing,compiler.ast.Slice): - if thing.flags != compiler.consts.OP_APPLY: - raise ValueError,"Unexpected flag %d in %s"%(thing.flags,`thing`) - return strify(thing.expr) + "[" + \ - strify(thing.lower) + ":" + strify(thing.upper) + "]" - elif isinstance(thing,compiler.ast.Sliceobj): - slicelist = [] - for idx in thing.nodes: - slicelist.append(strify(idx)) - return string.join(slicelist,":") - elif isinstance(thing,compiler.ast.Sub): - return strify(thing.left) + " - " + strify(thing.right) - elif isinstance(thing,compiler.ast.Subscript): - if thing.flags != compiler.consts.OP_APPLY: - raise ValueError,"Unexpected flag %d in %s"%(thing.flags,`thing`) - str = strify(thing.expr) + "[" - sublist = [] - for sub in thing.subs: - sublist.append(strify(sub)) - return str + string.join(sublist,", ") + "]" - elif isinstance(thing,compiler.ast.Tuple): - innards = [] - for item in thing.nodes: - innards.append(strify(item)) - return "(" + string.join(innards,", ") + ")" - elif isinstance(thing,compiler.ast.UnaryAdd): - return "+" + strify(thing.expr) - elif isinstance(thing,compiler.ast.UnarySub): - return "-" + strify(thing.expr) - else: - return _whatsthis(thing) - -def _whatsthis(thing): - # Wrong, but what else can we do? - import sys - print >>sys.stderr,"stringify_expr - don't recognise %s %s"%\ - (thing.__class__.__name__,thing) - return `thing` - - diff --git a/sandbox/tibs/pysource/visit.py b/sandbox/tibs/pysource/visit.py deleted file mode 100755 index ec5a2992f..000000000 --- a/sandbox/tibs/pysource/visit.py +++ /dev/null @@ -1,2074 +0,0 @@ -#! /usr/bin/env python -"""Extract information from a Python file (module) for the DOCUTILS - -An example of using Jeremy Hylton's compiler module. - -Originally based on an example *by* Jeremy Hylton. In the introduction -to that example, he said: - - Here's a trivial example program that extracts some information - about methods and attributes from a class and its methods. - I wasn't exhaustive here. I'll get attributes assigned to by - `self.x` in a method body and I'll get method definitions in - the class body. I don't deal with obvious things like attributes - defined at the class level. - -I'm expanding it, initially as a bigger example, and also so I get to -understand how things work! - -Note: there should be an example Python file called test.py in the -same directory as this module - it's meant to be a suitable input -for testing/learning purposes. - -:Author: Tibs <tibs@tibsnjoan.co.uk> -:Version: Whatever. Oh, 0.4, then -:Date: 2002-January-18 - -Aims for DOCUTILS ------------------ -1. Locate all modules, classes, functions, methods and their docstrings. -2. "Filter" these to leave the ones that DOCUTILS is interested in. -3. Annotate functions and methods with their arguments. -4. Locate all assignments which have docstrings. -5. Similarly "filter" these. -6. Locate all uses of `global` assignments. -7. Filter out those which are not obvious from the module level, - so that the user can be warned about them. -8. Produce a tree structure suitable for DOCUTILS processing, containing - the suitable results of the above. - -(At least some of) 1, 3 and 4 have already been done. - -Still to do ------------ -* *all* sorts of assignment should be visited, so that - we can tie down the use of globals properly. -* the option of ignoring _name and __name things should be - available (should this be the default?) -* Some consideration should be given to freeing up this mess of things - when they are all finished with - there are so many interconnections - that automatic deletion is unlikely to work well -* other things as they occur to me -* Finish off InstanceValue (its potential link to the class that defines it) - -Names and fullnames -------------------- -A "name" is taken to be the name as presented in Python code. Note that -for class instance attribute references (e.g., "self.fred"), the name is -includes the class reference, dot and attribute name (e.g., "self.fred"). - -A "fullname" includes the full path to the name as well - that is, -including all of the elements required to "reach" this item. Thus, for -instance, a method Fred in class Jim in module Bill would be:: - - Bill.Jim.Fred - -whilst a name A defined within Fred would be:: - - Bill.Jim.Fred:A - -Note that if Fred contains a reference to self.B, then that would (for the -moment at least) be written as:: - - Bill.Jim.Fred:self.B - -Within an eventual HTML document, such fullnames become anchor and link -names for nodes. For that purpose, they will be transformed so that they -do not contain "." or ":", but are still not accidentally confusable with -legitimate Python names. Thus the last example would be transformed to:: - - Bill-Jim-Fred--self-B - -Note that we also have a "special" label, artificially "generated" - that -is, there is a single label for all arguments to a function or method, -which is formed of the label for the function or method, followed by -*three* hyphens, and then the text "args" - so, for example: - - Bill-Jim---args - -Special cases -------------- -Notes on special cases in name resolution: - -- obviously I should be taking account of explicit roles. - -- if it can't find the name directly via the "scope", it should be looking - up in the module - so the ScopeMixin needs to provide support for that... - -- the module name should be recognised as, in some sense, within the - module's "parent" - even if there is no such beast. - -- that generalises - in general, an object's own name should be visible - to its docstring, I think - so, for instance:: - - digit = 3 - '''We store the current digit of pi in `digit`''' - - (yes, I know that's poor documentation, but even so) - -- <self>.name in a method (where <self> is the first argument to said - method) should be regarded as being a name in the class containing - the method, in some sense (but what sense?) -""" #" - -import os -import sys -import string -import compiler - -from docutils.parsers.rst import Parser - -# Local modules -import utils -import visit - -__docformat__ = "reST" - - -# ---------------------------------------------------------------------- -def make_Docstring(text, settings): - """A factory function to handle non-existant docstrings. - """ - if text: - return Docstring(text, settings) - else: - return None - -class Docstring: - """We use this to represent a docstring, parsed and unparsed. - """ - - def __init__(self, text, settings, debug=0): - """Instantiate a new Docstring from its text. - """ - self.text = self._trim(text) - """The text of the docstring. - """ - self.document = None - """The result of parsing the docstring, if requested. - (thus, only non-None if we *are* parsing docstrings in - the containing module). - """ - self.settings = settings - """The docutils settings to apply to new document parsers. - """ - - self.debug = debug - - def _trim(self,docstring): - """Trim leading space appropriately from the docstring text. - - When the docstring is read from the source file, it is likely - to have all but the first line indented with respect to the - first - for example, consider this docstring. - - Before it is *used* for anything (e.g., typing it out to be - read by the user), it is necessary to adjust for this. The - way to do this is well-defined (???where???). - - Returns the adjusted text. - """ - - # Remove any confusing tabs - follow the normal Python rules - # - and split into convenient lines - lines = string.split(string.expandtabs(docstring),"\n") - - # Find significant indentation - firstline = lines[0] - if firstline and firstline[0] == " ": - oldlen = len(firstline) - firstline = string.lstrip(firstline) - indent = oldlen - len(firstline) - else: - indent = 0 - for line in lines[1:]: - oldlen = len(line) - lin = string.lstrip(line) - if lin: - indent = oldlen - len(lin) - break - - # And remove it... - newlines = [firstline] - for line in lines[1:]: - oldlen = len(line) - lin = string.lstrip(line) - if lin: - extra = oldlen - len(lin) - indent - if extra > 0: - lin = " "*extra + lin - newlines.append(lin) - - # Don't introduce a trailing blank line if they did some - # variant of the following layout:: - # """Some text - # """ - if newlines and newlines[-1] == "": - del newlines[-1] - - # And lastly, reassemble it back into text again - return string.join(newlines,"\n") - - def show(self,stream,spaces=""): - # Not optimised for efficiency... - lines = string.split(self.text,"\n") - - # For the moment, don't try to format it very nicely at all... - stream.write('%s"""%s\n'%(spaces,lines[0])) - for line in lines[1:]: - stream.write(spaces+line+"\n") - stream.write('%s"""\n'%spaces) - - def parse(self,parser): - """Parse our text with the given parser. - """ - import docutils - self.document = docutils.utils.new_document(self.text, self.settings) - parser.parse(self.text,self.document) - - def add_to_DPS(self,dps): - """Add this docstring content to a DOCUTILS subtree. - - Calls the appropriate things from `dps`, which is a - builtree.BuildTree instance. - """ - if self.document: - for child in self.document.children: - dps.addsubtree(child) - else: - dps.start("literal_block") - dps.add("text",self.text) - dps.end("literal_block") - - # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: - # Experimental <interpreted> node code... - # Copied from transform.py, and not adjusted yet (except superficially - # and stupidly) for its position in this class. - # - despite the comments in the docstring, it doesn't *do* anything yet... - - def transform_interpreted(self,thing): - """Deal with <interpreted> nodes in this docstring. - - The intent is to find any <interpreted> nodes, try to locate the - relevant Python value, and insert an appropriate cross-reference. - - `thing` is the `Module`, `Class`, `Function`, `Method` or - `Name` instance to which this docstring is "attached". It - is used to resolve the <interpreted> names. - """ - if self.debug: - print "\n*** Locating <interpreted> items for %s"%thing.fullname - self.show(sys.stdout," ") - self.locate_interpreted(self.document,thing) - - def locate_interpreted(self,element,thing): - """Deal with finding "interpreted" nodes in a document subtree. - """ - for node in element: - if node.tagname == "#text": - continue - elif node.tagname == "interpreted": - self.treat_interpreted(node,thing) - else: - self.locate_interpreted(node,thing) - - def treat_interpreted(self,element,thing): - """Do something interesting with an "interpreted" node. - """ - if element.has_key("role"): - role = element["role"] - else: - role = None - - name = element.astext() - - #if role: - # print "...<interpreted> :%s: %s"%(role,name) - #else: - # print "...<interpreted> %s"%name - - # Attempt to work out what this <interpreted> item refers to - target = thing.scope_find_name(name,role=role,indent="...") - if target: - element["refname"] = target - - -# ---------------------------------------------------------------------- -class ScopeMixin: - """Learning about how to represent scope. - - The idea of this mixin class is to work out the best way of representing - scope, so that I can use the results when working out exactly what an - <interpreted> item in a docstring refers to. - - Note that I do not intend (at least, for the moment) to worry about Python - before 2.2 - i.e., before embedded scope became non-optional. This is - purely laziness on my part... - """ - - scope_debug = 0 - - def scope_init(self): - """Call at the end of `__init__` to initilise our experiments - """ - - self.scope_module = None - """Which Module we are in. - """ - - self.scope_parent = None - """The *immediate* parent entity. - Note that for a method, this will be the class defining it. - - (Ultimately, it might be better if this were a weak reference.) - """ - - self.scope_encloser = None - """The innermost enclosing block. Note that for a method, - this should *not* be the class, but the innermost enclosing - function/method/module/whatever (whatever?). - """ - - self.scope_defines = [] - """A list of the names *defined* by this entity - (by assignment, definition or whatever). - - The list is ordered by last occurrence of the name. - """ - - self.scope_fullname = {} - """The key is the name from `self.scope_defines`, the value is the - "full" name for the defined value. - """ - - self.scope_children = {} - """A dictionary of the entities defined by this entity. - - The keys are the entity name (so correspond to those in - `self.scope_defines`), and the values are NULL if the entity - is defined by simple assignment, otherwise the relevant - "scope" instance (e.g., a Method or Class). - """ - - self.scope_globals = [] - """A list of names which are declared global in this scope. - """ - - def scope_define_parent(self,parent=None,module=None): - """Define our parent and encloser. - """ - self.scope_module = module - self.scope_parent = parent - self.scope_encloser = parent - if isinstance(self,Method): - while isinstance(self.scope_encloser,Class): - self.scope_encloser = self.scope_encloser.scope_parent - - def scope_define_name(self,name,fullname,child=None): - """Add a name that is defined in this scope. - - - `name` is the obvious name of the name being defined. - - `fullname` is the "path" to it, starting at the module. - - `child` is the class representing that name - e.g., a Class - or Name instance. - - Note - remember that functions/methods "define" their arguments... - - HOWEVER we are not trying for a general solution to the problem of - "find the item that is being referred to" - we are just trying to - provide useful links between <interpreted> items in docstrings and - those things that they might reasonably be expected to be referring to. - - Furthermore, we know that we will not show attributes, docstring or - not, if they are not - - a. At module level (so a ModuleValue) - b. Within a class (so a ClassValue) - c. Within a method called __init__ or __new__. - d. An argument (we always want to know about arguments). - - So we discard any attributes that do not match these criteria... - """ - if self.scope_debug: - if name in self.scope_defines: - redef = "(re)" - else: - redef = " " - print "%-15s %-30s %sdefined in %s"%(child.__class__.__name__, - name,redef,self.name) - - # Should we ignore this name? - if isinstance(child,Name): - if self.__class__ not in [Package,Module,Class] and \ - not (self.__class__ == Method and - self.name in ["__init__","__new__"]) and \ - child.__class__ != Argument: - return - # Remember to ensure the name is always at the END of the list - # (even if it was already there) - if name in self.scope_defines: - self.scope_defines.remove(name) - self.scope_defines.append(name) - self.scope_fullname[name] = fullname - self.scope_children[name] = child - - def scope_define_global(self,name): - """Indicate that a name is actually global. - - Note that it is entirely possible that the name is also present - in `self.scope_defines` - being global should be remembered as taking - precedence. - """ - if self.scope_debug: - if name in self.scope_defines: - redef = "(re)" - else: - redef = " " - print "%-10s %-30s %sdefined in %s"%("[Global]", - name,redef,self.name) - if name not in self.scope_globals: - self.scope_globals.append(name) - - def scope_get_object(self,name): - """Return the defined object corresponding to `name`, or None. - """ - try: - return self.scope_children[name] - except: - return None - - def scope_get_type(self,name): - """Return the "type" of the defined object corresponding to `name`. - """ - try: - return self.scope_children[name].__class__.__name__ - except: - return None - - def scope_name_in_self(self,name): - """Return true if the `name` is defined by ourselves. - """ - return name in self.scope_defines - - def scope_name_in_encloser(self,name): - """Return true if the `name` is defined by our encloser. - - (If we don't *have* an encloser - e.g., we're a module - then - it obviously returns false.) - """ - if self.scope_encloser: - return name in self.scope_encloser.scope_defines - else: - return 0 - - def scope_name_in_module(self,name): - """Return true if the `name` is defined by our module. - """ - if self.scope_module: - return name in self.scope_module.scope_defines - else: - return 0 - - def scope_up_is_module(self): - """Is our encloser the module? - """ - return self.scope_module == self.scope_encloser - - # We need a dictionary to relate role names to the classes we use - # A value of None against a key means that we are not attempting - # to handle this yet. - roles = {"package" : "Package", - "module" : "Module", - "class" : "Class", - "method" : "Method", - "function" : "Function", - "module_attribute" : "ModuleValue", - "class_attribute" : "ClassValue", - "instance_attribute": "InstanceValue", - "variable" : "Name", - "parameter" : "Argument", - "type" : None, - "exception_class" : None, - "exception" : None, - "warning_class" : None, - "warning" : None} - - def scope_obj_role_match(self,what,role): - """Decide if the role (if any) makes sense with respect to `what`. - - `what` is the object we thing might be our item of interest, - and `role` is the role we're looking for, or None if we're not - particular. - """ - if role: - if what.__class__.__name__ == self.roles[role]: - return 1 - else: - return 0 - else: - return 1 - - def scope_class_role_match(self,class_name,role): - """Decide if the role (if any) makes sense with respect to `what`. - - `what` is the class name of the object we thing might be our item - of interest, and `role` is the role we're looking for, or None if - we're not particular. - """ - if role: - if class_name == self.roles[role]: - return 1 - else: - return 0 - else: - return 1 - - def scope_callable_match(self,what,need_callable): - """Does the callable state of the `what` match our needs? Do we care? - """ - if need_callable: - return what.is_callable() - else: - return 1 - - def scope_find_name(self,name,role=None,indent=" "): - """A first pass at looking up names in scope. - - - `name` is the text that the user has enclosed in backquotes, - to make it interpreted - - `role`, if given, specifies exactly what sort of name the - user determines it to be. - - This makes no attempt to do anything beyond looking up a - simple name - it doesn't try to deal with (for instance) - a "()" at the end of the name, nor does it try to cope - with dotted names in any clever way. - - Returns either the label for the referenced entity (which is - a translation of its `fullname` into something legal as an - XML name), or None. - """ - if name.endswith("()"): - name = name[:-2] - need_callable = 1 - else: - need_callable = 0 - - if self.scope_debug: - print indent, - if role: - print ":%s:"%role, - - # Is it a reference to ourself? - if name == self.name and \ - self.scope_obj_role_match(self,role) and \ - self.scope_callable_match(self,need_callable): - if self.scope_debug: - print "%s '%s' IS %s %s"%\ - (self.__class__.__name__,name, - self.__class__.__name__,self.name) - return self.label() - - # Is it a reference to one of our children? - if self.scope_name_in_self(name): - what = self.scope_get_type(name) - whom = self.scope_children[name] - if self.scope_class_role_match(what,role) and \ - self.scope_callable_match(whom,need_callable): - if self.scope_debug: - print "%s '%s' found HERE in %s %s"%\ - (what,name,self.__class__.__name__,self.name) - if whom.__class__ == Argument: - return self.args_label() - else: - return whom.label() - - # Is it a reference to one of our encloser's children? - if self.scope_name_in_encloser(name): - what = self.scope_encloser.scope_get_type(name) - whom = self.scope_encloser.scope_children[name] - if self.scope_class_role_match(what,role) and \ - self.scope_callable_match(whom,need_callable): - if self.scope_debug: - print "%s '%s' found UP in %s %s"%\ - (what,name,self.scope_encloser.__class__.__name__, - self.scope_encloser.name) - if whom.__class__ == Argument: - return self.scope_encloser.args_label() - else: - return whom.label() - - # Is it a reference to our encloser? - if self.scope_encloser and name == self.scope_encloser.name: - # I'm not sure about this one - *should* we be able to "see" - # the name of our encloser? Or is what I'm *really* after - # the name of the *parent*? - # - # Specific instance - consider a method M in class C - # - should `C` in `M`s docstring refer to class C, - # even if class C is *not* defined at module level? - if self.scope_obj_role_match(self.scope_encloser,role) and \ - self.scope_callable_match(self.scope_encloser,need_callable): - if self.scope_debug: - print "%s '%s' IS %s %s"%\ - (self.scope_encloser.__class__.__name__,name, - self.scope_encloser.__class__.__name__, - self.scope_encloser.name) - return self.scope_encloser.label() - - # Is it a reference to a child of our module? - if not self.scope_up_is_module() and self.scope_name_in_module(name): - what = self.scope_module.scope_get_type(name) - whom = self.scope_module.scope_children[name] - if self.scope_class_role_match(what,role) and \ - self.scope_callable_match(whom,need_callable): - if self.scope_debug: - print "%s '%s' found GLOBAL in %s %s"%\ - (what,name,self.scope_module.__class__.__name__, - self.scope_module.name) - # Modules don't have arguments... - return whom.label() - - # Is it a reference to our module itself? - if name == self.scope_module.name: - if self.scope_obj_role_match(self.scope_module,role) and \ - not need_callable: - if self.scope_debug: - print "%s '%s' IS %s %s"%\ - (self.scope_module.__class__.__name__,name, - self.scope_module.__class__.__name__, - self.scope_module.name) - return self.scope_module.label() - - if self.scope_debug: - print "'%s' not in %s %s"%(name,self.__class__.__name__,self.name), - if self.scope_encloser: - print "or %s %s"%(self.scope_encloser.__class__.__name__, - self.scope_encloser.name), - if self.scope_up_is_module(): - print - else: - print "or %s %s"%(self.scope_module.__class__.__name__, - self.scope_module.name) - return None - - def scope_show(self,indent=""): - print "%s%s %s"%(indent,self.__class__.__name__,self.name), - if self.scope_encloser and self.scope_encloser != self.scope_parent: - print "<<upscope %s %s>>"%(self.scope_encloser.__class__.__name__, - self.scope_encloser.name) - else: - print - - extra_indent = indent+" " - - if self.scope_globals: - print "%sglobals %s"%(extra_indent,self.scope_globals) - - for name in self.scope_defines: - child = self.scope_children[name] - if child: - child.scope_show(extra_indent) - - -# ---------------------------------------------------------------------- -class Base(ScopeMixin): - """A simple base class for common functionality. - """ - - verbose = 0 - - def __init__(self, settings, name, fullname, docstring=None): - self.settings = settings - """ The parser settings - """ - - self.name = name - """The name of this scope entity. - """ - - self.fullname = fullname - """The fully qualified name of this entity - - for instance, for a method `fred` in a class `Jim` - in a module `bob`, this would be "bob.Jim.fred" - """ - - self.docstring = make_Docstring(docstring, settings) - """The docstring for this scope, or None. - Note that, at least for the moment, we're only supporting - one docstring for an entity. This may need to change for - proper DOCUTILS support (but I [1]_, personally, hope not). - - .. [1] "I" being Tibs, at this point, and at this time [2]_. - .. [2] "This time" being April 2002. - """ - - self.imports = {} - """Import statements within this scope. The key is the - name of each module being imported, the value is a list - of the names "as" which the module is imported, with - `None` meaning "as itself". - """ - - self.from_imports = {} - """From <module> import <names> statements within this - scope. The key is the <module>, the value a dictionary - like that used for `self.imports`. - """ - - self.classes = {} - """Classes defined within this scope. The key is the - class name, the value is the Class instance for this ``class``. - """ - - self.functions = {} - """Functions or methods defined within this scope. The key is the - function name, the value is the Function or Method instance - for this ``def``. - """ - - self.attributes = {} - """Attributes assigned to within a scope. The key is the - attribute name, the value is the Name instance for this ``name``. - """ - - self.children = [] - """A list of all of the items contained herein which might - have, or have children which have, docstrings - i.e., packages, - modules, classes, functions, methods. - """ - - self.class_list = [] - """A list of the class names defined in this scope, in order. - """ - - self.function_list = [] - """A list of the function/method names defined in this scope, in order. - """ - - self.attribute_list = [] - """A list of the attribute names defined in this scope, in order. - """ - - self.globals = {} - """The names mentioned in global statements within a scope. - The key is the name, the value doesn't matter. - """ - - self.parser = None - """We may need a reStructuredText parser to handle our docstrings - - if so, we'll generate one "on demand".""" - - def getParser(self): - """Retrieve a reStructuredText parser, for docstring usage. - """ - if not self.parser: - self.parser = Parser() - return self.parser - - def parse_docstrings(self,parser): - """If we are meant to, parse our docstrings. - """ - if self.docstring: - self.docstring.parse(parser) - for child in self.children: - child.parse_docstrings(parser) - - def is_callable(self): - """Does it make sense to call an object of this type? - """ - return 0 - - def label(self): - """Return an XML-legal name for this object. - - Note that we implicitly assume that all objects have their - output XML/HTML representation written to the same file - - i.e., that we do not have to worry about references that - are anything beyond simple names. - """ - str = string.replace(self.fullname,".","-") - str = string.replace(str,":","--") - return str - - def addGlobal(self,name): - self.globals[name] = name - - def addClass(self,klass): - self.classes[klass.name] = klass - self.class_list.append(klass.name) - self.children.append(klass) - - def addFunction(self,func): - self.functions[func.name] = func - self.function_list.append(func.name) - self.children.append(func) - - def addAttribute(self,name,instance): - self.attributes[name] = instance - self.attribute_list.append(name) - self.children.append(instance) - - def addImport(self,names): - """Remember an import statement. - - `names` is a list of ``(name,as)`` tuples, where ``name`` - is the name of the item being imported, and ``as`` is either - None of else the name "as" which this item is being imported - - Note we aren't even *trying* to remember its exact locality. - """ - for name,as in names: - if not self.imports.has_key(name): - self.imports[name] = [as] - else: - if as not in self.imports[name]: - self.imports[name].append(as) - - def addFromImport(self,modname,names): - """Remember a from import statement. - - `modname` is the name of the module "from" which things are - being imported. - - `names` is a list of ``(name,as)`` tuples, where ``name`` - is the name of the item being imported, and ``as`` is either - None of else the name "as" which this item is being imported - - Note we aren't even *trying* to remember its exact locality. - """ - if not self.from_imports.has_key(modname): - self.from_imports[modname] = {} - - dict = self.from_imports[modname] - for name,as in names: - if not dict.has_key(name): - dict[name] = [as] - else: - if as not in dict[name]: - dict[name].append(as) - - def isGlobal(self,name): - return self.globals.has_key(name) - - def getClassNames(self): - return self.class_list - #return self.classes.keys() - - def getClass(self,name): - return self.classes[name] - - def getFunctionNames(self): - return self.function_list - #return self.functions.keys() - - def getFunction(self,name): - return self.functions[name] - - def getAttributeNames(self): - return self.attribute_list - #return self.attributes.keys() - - def getAttribute(self,name): - return self.attributes[name] - - def getAttributeDocstring(self,name): - """Return an attribute docstring, or None - """ - return self.attributes[name] - - def getImports(self): - """Return our "import"s, but not in any particular order. - """ - return self.imports - - def getFromImports(self): - """Return our "from ... import"s, but not in any particular order. - """ - return self.from_imports - - def getFromImportNames(self,name): - return self.from_imports[name] - - def getSelf(self): - """Return the name used for "self". Doesn't do much elsewhere. - - (Actually, for a method *or* a function, it returns the name - of the first argument.) - """ - return None - - def _show_body(self,stream,indent=0): - spaces = indent*" " - - if self.docstring: - self.docstring.show(stream,spaces+" ") - - if self.imports: - names = self.imports.keys() - stream.write("%s Import %s\n"%(spaces,string.join(names,", "))) - - if self.from_imports: - modnames = self.from_imports.keys() - for mod in modnames: - names = self.from_imports[mod] - stream.write("%s From %s import %s\n"%\ - (spaces,mod,string.join(names,", "))) - - # Should we sort things alphabetically within each list? - - if self.verbose: - globals = self.globals.keys() - if globals: - stream.write("%s Global statements\n"%spaces) - for name in globals: - stream.write("%s %s\n"%(spaces,name)) - - attributes = self.attributes.keys() - if attributes: - stream.write("%s Attributes\n"%spaces) - for name in attributes: - self.attributes[name].show(stream,indent+4) - - class_names = self.classes.keys() - if class_names: - for name in class_names: - self.classes[name].show(stream,indent+2) - - func_names = self.functions.keys() - if func_names: - for name in func_names: - self.functions[name].show(stream,indent+2) - - def show(self,stream,indent=0): - """Override as necessary.""" - stream.write("%s%s %s\n"%(" "*indent,self.__class__.__name__, - self.name)) - self._show_body(stream,indent) - - # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: - # Experimental <interpreted> node code... - def find_docstrings(self): - """Find each docstring in our subtree. - """ - if self.docstring: - self.docstring.transform_interpreted(self) - for child in self.children: - child.find_docstrings() - # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: - - -# ---------------------------------------------------------------------- -class Package(Base): - """The representation of a Python package. - - Unlike the other children of `Base`, this class actually visits - its members itself - that is, it is self-constructing. - """ - - def __init__(self,settings,directory,parent=None,debug=0): - """Instantiate a new package. - - If this is a "sub" package, then `parent` should be a Package - instance, within which we may be found... - - `settings` will give the parsing settings -- `verbose_parse` will - cause the generation of a message for each stage of the production - of our data, and `debug` will give a message for each "entity" - in the AST that we visit. - """ - - self.settings = settings - self.verbose = settings.verbose_parse - self.debug = debug - - path = os.path.expanduser(directory) # expand "~" - path = os.path.expandvars(directory) # expand "$fred" or "%fred%" - path = os.path.abspath(path) # expand to a 'full' path - path = os.path.normpath(path) # sort out "/fred" versus "/fred/" - self.directory = path - """The `directory` is our best guess as to the actual - path to the package.""" - - # Take the package name to be the final component - base,name = os.path.split(path) - if parent: - fullname = parent.fullname + "." + name - Base.__init__(self,settings,name,fullname) - else: - # With no parent, our name and fullname are the same. - Base.__init__(self,settings,name,name) - - self.modules = {} - """Remember the modules (or Python files, really) - in this package. The key is the module name, the - value the Module instance for this module. - """ - - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # Experimental scope setup - self.scope_init() - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - self.parse_modules() - - def parse_modules(self): - """Locate our constituent modules and parse them. - """ - files = os.listdir(self.directory) - - if "__init__.py" not in files: - raise ValueError,\ - "Directory %s does not contain an __init__.py file"%\ - self.directory - - # Parsing the __init__.py file may give us further information, - # such as an __all__ value, which we may later want to use to - # determine which files to read and parse, and which to ignore - path = os.path.join(self.directory,"__init__.py") - self.addModule(Module(self.settings,path,package=self, debug=self.debug)) - - # But, for the moment, just parse all the Python files... - for file in files: - name,ext = os.path.splitext(file) - if name == "__init__": - continue - elif ext in [".py",".pyw"]: - path = os.path.join(self.directory,file) - self.addModule(Module(self.settings,path,package=self, - debug=self.debug)) - - def addModule(self,mod): - self.modules[mod.name] = mod - self.children.append(mod) - - def getModuleNames(self): - return self.modules.keys() - - def getModule(self,name): - return self.modules[name] - - def getModules(self): - """Return a list of Module instances. - """ - return self.modules.values() - - def show(self,stream,indent=0): - stream.write("%sPackage %s"%(" "*indent,self.name)) - if self.directory: - stream.write("(in %s)\n"%self.directory) - else: - stream.write("\n") - - names = self.modules.keys() - names.sort() - for name in names: - self.modules[name].show(stream) - - def show_ast(self,stream): - """Print out a representation of our ASTs. - """ - names = self.modules.keys() - names.sort() - for name in names: - self.modules[name].show_ast(stream) - - -# ---------------------------------------------------------------------- -class Module(Base): - """The representation of a Python module. - - This class also knows how to *parse* a Python module. - - Still known to be missing: - - - visitLambda (I shudder) - - should we also do visitExec? - - - visitAssign - - visitAugAssign - - These last two are needed to allow us to link a Name with its value(s). - - If attribute docstrings are to be found, then the AST on which we - act must first have been processed with `find_attr_docs()`. - - Note we also need to visit the *other* sorts of assignment, so - we can update our Module with any globals used therein... - """ - - def __init__(self,settings,filename,docstring=None,package=None,debug=0): - """Instantiate a new module. - - If this is a module within a package, then `package` should be - a Package instance. - - `settings` will give the parsing settings -- `verbose_parse` will - cause the generation of a message for each stage of the production - of our data, and `debug` will give a message for each "entity" - in the AST that we visit. - """ - self.settings = settings - self.package = package - self.verbose = settings.verbose_parse - self.debug = debug - - self.docformat,self.language = utils.docformat("plaintext") - - filename = os.path.expanduser(filename) # expand "~" - filename = os.path.expandvars(filename) # expand "$fred" or "%fred%" - filename = os.path.abspath(filename) # expand to a 'full' path - filename = os.path.normpath(filename) # and tidy up - self.filename = filename - """The `filename` is our best guess as to the actual - path to the module.""" - - dir,file = os.path.split(filename) - name,ext = os.path.splitext(file) - - if package: - fullname = package.fullname + "." + name - Base.__init__(self,settings, name,fullname,docstring) - else: - # Without a package, our name and fullname are the same - Base.__init__(self,settings, name,name,docstring) - - self.ast = None - """The parse tree, as produced by compiler. - """ - - self.globals_used = {} - """Remember the globals promulgated on this module - (that is, occurrences of a global statement, associated - with assignment *to* the named global, within the same - scope). - - The key is the global name, the value a list of - (scope,superscope) pairs. - """ - - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # Experimental scope setup - self.scope_init() - self.scope_define_parent(parent=package,module=self) - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - # Now, parse ourselves! - self.parse_module() - - # And then perform our "finishing off" operations - self.determine_docformat() - - if self.verbose: - print >>sys.stderr, \ - " >> docformat: %s, language %s"%(self.docformat, - self.language) - - if self.docformat == utils.RESTRUCTUREDTEXT: - if self.verbose: - print >>sys.stderr," .. parsing docstrings in module",\ - self.name - parser = self.getParser() - self.parse_docstrings(parser) - if self.verbose: - print >>sys.stderr," .. finding <interpreted> nodes" - self.find_docstrings() - - def parse_module(self): - """Parse ourselves (!) - """ - if self.verbose: print >>sys.stderr, "*** Parsing file",self.filename - self.ast = compiler.parseFile(self.filename) - - # "Comb" the tree to sort out attribute docstrings - if self.verbose: print >>sys.stderr, " Locating attribute docstrings" - utils.find_attr_docs(self.ast) - - # And to link names and their values in assignments - if self.verbose: print >>sys.stderr, " Locating attribute values" - utils.find_attr_vals(self.ast) - - if self.verbose: print >>sys.stderr, " Walking the tree" - compiler.walk(self.ast,self) - - def determine_docformat(self): - """Call this after reading in the module, before "using" it. - - This detects whether there is a module value called __docformat__, and - if so, inspect its value. - """ - if self.attributes.has_key("__docformat__"): - name = self.attributes["__docformat__"] - try: - value = name.actualLastValue() - except ValueError, detail: - print >>sys.stderr, "Ignoring __docformat__" - print >>sys.stderr, detail - return - self.docformat,self.language = utils.docformat(value) - - def addGlobalUsed(self,name,where): - if self.globals_used.has_key(name): - self.globals_used[name].append(where) - else: - self.globals_used[name] = [where] - - def getObscureGlobals(self): - """Return a list of (name,[fullname]) tuples - """ - globals = self.globals_used.keys() - globals.sort() - attributes = self.attributes.keys() - obscure = [] - for name in globals: - if name not in attributes: - wherelist = self.globals_used[name] - obscure.append( (name,wherelist) ) - return obscure - - # ---------------------------------------------------------------------- - # Extraction code - - def _scopes(self,scope,superscope): - str = "" - if scope: - str = str + " in %-8s %-12s"%(scope.__class__.__name__, - `scope.name`) - if superscope: - str = str + " in %-8s %-12s"%(superscope.__class__.__name__, - `superscope.name`) - return str - - def _report(self,what,name,scope,superscope): - print >>sys.stderr,\ - "%-8s %-12s"%(what,name) + self._scopes(scope,superscope) - - def visitModule(self,node,scope=None,superscope=None): - """Visit a Module node - heh, this must be us! - - We expect `scope` and `superscope` both to be None - """ - if self.debug: - self._report("Module",self.filename,scope,superscope) - - self.docstring = make_Docstring(node.doc, self.settings) - - # Visit our children with ourselves as their scope - self.visit(node.node,self) - - def visitClass(self,node,scope=None,superscope=None): - """Visit a Class node - - `scope` is the scope of this Class, and `superscope` is - that scope's scope (if any). - - We don't yet cope well with things like:: - - class Fred(docutils.nodes) - - - this will need fixing. - """ - if self.debug: - self._report("Class",node.name,scope,superscope) - - if scope: - fullname = "%s.%s"%(scope.fullname,node.name) - else: - fullname = node.name - - # If we were nice, we'd resolve the - # Getattr(Getattr(Name('docutils'), 'nodes'), '_TextElement') - # that we get for something like docutils.nodes into something - # easier to deal with... - cls = Class(self.settings,node.name,fullname,node.bases,node.doc) - if scope: - scope.addClass(cls) - - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # Experimental scope work - if scope: - cls.scope_define_parent(scope,self) - scope.scope_define_name(node.name,fullname,cls) - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - # Visit our children with ourselves as their scope, - # and our scope as their superscope - self.visit(node.code,cls,scope) - - def visitFunction(self,node,scope=None,superscope=None): - """Visit a Function node (nb: this means "function or method") - - `scope` is the scope of this Function, and `superscope` is - that scope's scope (if any). - """ - if self.debug: - if isinstance(scope,Class): - self._report("Method",node.name,scope,superscope) - else: - self._report("Function",node.name,scope,superscope) - - fullname = "%s.%s"%(scope.fullname,node.name) - - if isinstance(scope,Class): - fun = Method(self.settings,node.name,fullname, - node.argnames,node.defaults,node.flags,node.doc) - else: - fun = Function(self.settings,node.name,fullname, - node.argnames,node.defaults,node.flags,node.doc) - scope.addFunction(fun) - - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # Experimental scope work - fun.scope_define_parent(scope,self) - scope.scope_define_name(node.name,fullname,fun) - for argname in node.argnames: - fullname = "%s.%s:%s"%(scope.fullname,node.name,argname) - name = Argument(self.settings,argname,fullname) - name.scope_define_parent(fun,self) - fun.scope_define_name(argname,fullname,name) - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - # Visit our children with this function as the scope, and - # our scope as the superscope - self.visit(node.code,fun,scope) - - def visitAssAttr(self,node,scope,superscope=None): - """Visit an attribute assignment node. - - For example:: - - self.a = 1 - - Note that this is assignment to a *single* attribute only. - - `scope` is the scope of this assignment, and `superscope` is - that scope's scope (if any). - """ - if self.debug: - self._report("attr",node.attrname,scope,superscope) - - # An AssAttr contains: - # .expr - the expression on the LH of the dot - # .attrname - the attribute name on the RH of the dot - # .flags - # We're (to a first approximation) only interested in - # assignments of the form <self>.<attr>, where "<self>" - # is whatever name was passed down as the first argument - # to a containing method. - if isinstance(node.expr,compiler.ast.Name): - fullname = "%s:%s.%s"%(scope.fullname,node.expr.name,node.attrname) - selfname = "%s.%s"%(node.expr.name,node.attrname) - if node.expr.name == scope.getSelf(): - what = InstanceValue - else: - what = Name # hmm, this feels wrong, but I do want to distinguish - - if hasattr(node,"docstring"): - #name = what(node.attrname,fullname,node.expr.name,node.docstring) - name = what(selfname,fullname,node.expr.name,node.docstring) - else: - #name = what(node.attrname,fullname,node.expr.name) - name = what(selfname,fullname,node.expr.name) - if hasattr(node,"assign_expr"): - name.setValue(node.assign_expr) - scope.addAttribute(selfname,name) - - if node.expr.name == scope.getSelf() and scope.__class__ == Class: - name.set_class(scope) - - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # We should do *something* for our experimental scope - # work at this point - but I guess it would be to add - # this name to the class that contains the method in - # which we are - and if we're actually in a *function* - # which is to be assigned to a class at run-time (for - # instance), that is a bit difficult (!). So ignore - # this case for now... - # OR maybe we should be adding in "fullname" and not - # "name" each time we add something to the scope. - name.scope_define_parent(scope,self) - scope.scope_define_name(selfname,fullname,name) - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - else: - # What to do here??? - # - it's presumably something like fred[3].attr, so I guess - # we don't want to do anything very clever with it, at least - # for the moment. - - print - print ">>> Unhandled AST node <<<" - print " visitAssAttr: %s"%(node) - print " i.e.: %s"%(utils.stringify_expr(node)) - print " location: Module %s, %s %s, %s %s"%\ - (self.scope_module.name, - scope.__class__.__name__,scope.name, - superscope.__class__.__name__,superscope.name) - print - - # Hmm - what scope and superscope does the - # subexpression *really* want... - # - is there any point (from a DPV point of view) - # in following down the innards of the expression? - self.visit(node.expr,scope,superscope) - - def visitAssName(self,node,scope=None,superscope=None): - """Visit a name assignment node. - - For example:: - - a = 1 - - Note that this is assignment to a *single* name only. - - `scope` is the scope of this assignment, and `superscope` is - that scope's scope (if any). - """ - if self.debug: - self._report("name",node.name,scope,superscope) - - # For the moment, allow any assignments - later we may restrict - # this (e.g., to Module, Class, __init__ method) - if scope: - fullname = "%s:%s"%(scope.fullname,node.name) - else: - fullname = node.name - - if scope.__class__ == Module: - what = ModuleValue - elif scope.__class__ == Class: - what = ClassValue - else: - what = Name - - if hasattr(node,"docstring"): - name = what(self.settings,node.name,fullname, - docstring=node.docstring) - else: - name = what(self.settings,node.name,fullname) - if hasattr(node,"assign_expr"): - name.setValue(node.assign_expr) - if scope.isGlobal(node.name): - name.setGlobal() - scope.addAttribute(node.name,name) - - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # Experimental scope work - if scope: - name.scope_define_parent(scope,self) - scope.scope_define_name(node.name,fullname,name) - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - # Hmm - check whether the name is a global, and if so, - # tell our Module about it... - if scope.isGlobal(node.name): - self.addGlobalUsed(node.name,scope.fullname) - - def visitAssTuple(self,node,scope=None,superscope=None): - """Visit a tuple assignment statement. - - For example: - - a, b, c = 1, 2, 3 - - Although we're not directly interested in these for docstring - purposes, we *may* want to know that a variable has participated - in one (why?). - """ - for item in node.nodes: - self.visit(item,scope,superscope) - - def visitAssList(self,node,scope=None,superscope=None): - """Visit a list assignment statement. - - For example: - - a, b, c = [1, 2, 3] - - Although we're not directly interested in these for docstring - purposes, we *may* want to know that a variable has participated - in one (why?). - """ - for item in node.nodes: - self.visit(item,scope,superscope) - - def visitGlobal(self,node,scope=None,superscope=None): - """Visit a global statement node - - `scope` is the scope of this global, and `superscope` is - that scope's scope (if any). - """ - if self.debug: - for name in node.names: - self._report("global",name,scope,superscope) - - for name in node.names: - scope.addGlobal(name) - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # Experimental scope work - scope.scope_define_global(name) - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - def visitYield(self,node,scope=None,superscope=None): - """Visit a yield statement. - - The presence of a yield statement within a function indicates - that it is a generator (well, unless that statement is - unreachable - but we shall not worry about that, at least - for the moment!). - - `scope` is the scope of this statement, and `superscope` is - that scope's scope (if any). - """ - if self.debug: - self._report("yield",node.value,scope,superscope) - - # So tell our scope that it is (probably) a generator - scope.generator = 1 - - # There seems little point in visiting our value, but on - # the other hand, why not - self.visit(node.value,scope,superscope) - - - def visitImport(self,node,scope=None,superscope=None): - """Visit an import statement node - - `scope` is the scope of this import statement, and - `superscope` is that scope's scope (if any). - """ - if self.debug: - self._report("import","",scope,superscope) - print >>sys.stderr, "%8s %12s %s"%(" "," ",node.names) - - if scope: - scope.addImport(node.names) - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # Experimental scope work - for item in node.names: - fullname = scope.fullname + "." + item[0] - name = ImportName(self.settings,item[0],fullname) - name.scope_define_parent(scope,self) - scope.scope_define_name(item[0],fullname,name) - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - def visitFrom(self,node,scope=None,superscope=None): - """Visit a from .. import statement node - - The rules for `scope` and `superscope` should be identical - to those for `visitImport()`. - """ - if self.debug: - self._report("from",node.modname,scope,superscope) - print >>sys.stderr, "%8s %12s %s"%(" "," ",node.names) - - if scope is not None: - scope.addFromImport(node.modname,node.names) - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # Experimental scope work - for item in node.names: - fullname = scope.fullname + "." + item[0] - name = ImportName(self.settings,item[0],fullname) - name.scope_define_parent(scope,self) - scope.scope_define_name(item[0],fullname,name) - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - # ---------------------------------------------------------------------- - - def _prt(self,stream,indent,name,place): - stream.write("%sGlobal %-10s in %s\n"%(" "*indent,name,place)) - - def _and(self,stream,indent,name,place): - stream.write("%s %-10s and in %s\n"%(" "*indent," "*len(name), - place)) - - def _show_odd_globals(self,stream,indent=0): - """Show which global names are defined and used below module level - - Obviously (!) we are only interested in things which are not - already known to be module level attributes - we want to warn - the user about things that are defined in non-obvious places. - """ - obscure = self.getObscureGlobals() - done_header = 0 - for name,wherelist in obscure: - if not done_header: - stream.write("Globals defined *and used* below" - " the top level:\n") - done_header = 1 - self._prt(stream,indent+2,name,wherelist[0]) - for place in wherelist[1:]: - self._and(stream,indent+2,name,place) - - def show(self,stream,indent=0): - stream.write("%sModule %s"%(" "*indent,self.name)) - if self.filename: - stream.write("(in file %s)\n"%self.filename) - else: - stream.write("\n") - self._show_body(stream,indent) - self._show_odd_globals(stream,indent) - - def show_ast(self,stream): - """Print out a representation of our AST. - """ - stream.write("AST for module %s\n"%self.name) - utils.treeprint(stream,self.ast) - -# ---------------------------------------------------------------------- -class Class(Base): - """The representation of a Python class.""" - - def __init__(self,settings,name,fullname,bases,docstring): - Base.__init__(self,settings,name,fullname,docstring) - - self.bases = bases or [] - """A list of the base classes for this class.""" - - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # Experimental scope setup - self.scope_init() - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - def is_callable(self): - """Does it make sense to call an object of this type? - - NB: it only makes sense to call this *after* the class has - been parsed! - """ - return self.functions.has_key("__call__") - - def show(self,stream,indent=0): - basestr = "" - if self.bases: - names = self.getBaseNames() - basestr = "(" + string.join(names,",") + ")" - stream.write("%sClass %s%s\n"%(" "*indent,self.name,basestr)) - self._show_body(stream,indent) - - def getBaseNames(self): - names = [] - for base in self.bases: - # Unfortunately, we don't yet cope with base classes - # like "docutils.nodes" in our visitor... - # Make the best of it we can, for now - try: - names.append(base.name) - except: - names.append("???") - return names - - # Is it worth doing this? - addMethod = Base.addFunction - getMethodNames = Base.getFunctionNames - - -# ---------------------------------------------------------------------- -class Function(Base): - """The representation of a Python function (and thus also of a method). - - But see also `Method`. - """ - - def __init__(self,settings,name,fullname,args,defaults,flags,docstring): - Base.__init__(self,settings,name,fullname,docstring) - - self.args = args - """The arguments for this function or method.""" - - self.defaults = defaults - """The defaults (if any) for the latter arguments. - - Note that if there are 4 arguments, and only the last - 2 have defaults, then this list will be 2 items long. - """ - - self.flags = flags - """Used to indicate \*args and \**kwargs. - """ - - self.generator = 0 - """Set to true if this function appears to be a generator - (i.e., it contains a "yield" statement). Obscure code that - doesn't actually *reach* the yield statement - for instance:: - - def fred(): - print "3" - if 1: return - yield "2" - - will also, incorrectly, be recognised as a generator - tough. - """ - - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # Experimental scope setup - self.scope_init() - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - def is_callable(self): - """Does it make sense to call an object of this type? - """ - return 1 - - def args_label(self): - """Return the specialised label we use for all of our arguments. - """ - return self.label() + "---args" - - def getArgs(self): - """Return a representation of our arguments as a list (of strings). - - This is then suitable for joining with commas and displaying. - """ - args = utils.merge_args(self.args,self.defaults) - if not args: - return args - - # The file compiler/consts.py defines CO_VARARGS=1 and - # CO_VARKEYWORDS=2. - - # The method Transformer.com_arglist() in file compiler/transformer.py - # handles a function's argument list. - - # According to that, if there's a "*args" item, then "flags" gets the - # CO_VARARGS bit set, and if there's a "**args" item, then "flags" gets - # the CO_VARKEYWORDS bit set. In either case, we *know* they've got to - # be the last arguments, so we can count backwards (as we do for - # default arguments). - - if self.flags & compiler.consts.CO_VARKEYWORDS: - # If it's there, this must always be the last one... - args[-1] = "**" + args[-1] - - if self.flags & compiler.consts.CO_VARARGS: - # But this one might be last OR last but one - if self.flags & compiler.consts.CO_VARKEYWORDS: - args[-2] = "*" + args[-2] - else: - args[-1] = "*" + args[-1] - - return args - - def show(self,stream,indent=0): - stream.write("%s%s %s(%s)\n"%(" "*indent,self.__class__.__name__, - self.name, - string.join(self.getArgs(),", "))) - self._show_body(stream,indent) - - def getSelf(self): - """For a method, return the name used for "self". - - (Actually, for a method *or* a function, return the name of - the first argument - we need it on functions as well in case - someone is defining a function later to be assigned to a class - and used as a method.) - """ - if len(self.args) > 0: - return self.args[0] - else: - return None - - -# ---------------------------------------------------------------------- -class Method(Function): - """Just to get the class name right, I'm afraid... - """ - pass - - -# ---------------------------------------------------------------------- -class Name(ScopeMixin): - """Information about use of a name in assignment. - - STILL UNDER CONSTRUCTION - - Test using pysource.py's "--show" option... - - - We are not interested in *all* names, nor in all information - about them... - - (We *could* inherit from Base, but that really brings too much other - stuff that we don't need with it - and it doesn't *quite* seem worth - having a mixin class (although the more I work with this, the more - that seems a wrong decision - maybe next refactor time will change - things.) - """ - - def __init__(self,settings,name,fullname,selfname=None,docstring=None): - """Instantiate a new Name. - - * `settings` -- the settings for the parser - * `name` -- the name of this, erm, name - * `fullname` -- the "fully qualified" name - this is the "path" from - our top-level entity down to this name (e.g., module.class.name) - * `selfname` -- if this is an instance attribute, the string used - as the "self" equivalent - * `docstring` -- the docstring for this name, if any. - """ - - self.name = name - """The name of this, erm, name. - """ - - self.fullname = fullname - """The fully qualified name of this name - - for instance, for a name `fred` in a class `Jim` - in a module `bob`, this would be "bob.Jim.fred" - """ - - self.docstring = make_Docstring(docstring, settings) - """The docstring for this name, or None. - Note that, at least for the moment, we're only supporting - one docstring for an entity. See the equivalent comment - in `Base`. - """ - - self.firstuse = None - """Remember the first use of this name - i.e., what is - assigned to it. The value stored is the AST expression - assigned to our name. - """ - - self.lastuse = None - """Also remember the last use of this name. The value - stored is the AST expression, as for firstuse. - """ - - self.reused = 0 - """Assume it has not been assigned to more than once. - If it has, then we can't *really* report on its 'content', - and `self.firstuse` should be ignored. - """ - - self.selfname = selfname - """If this name is referred to as something like 'self.name' - then we want to remember the string used as 'self' (since the - user *might* be using something else). This will be ``None`` - if there is no 'prefix'. - """ - - self.isglobal = 0 - """True if this name is global to its module. - """ - - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # Experimental scope setup - self.scope_init() - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - def parse_docstrings(self,parser): - """If we are meant to, parse our docstrings. - - (The oddly plural name is for compatibility with those things - that inherit from `Base` - another reason for thinking that a - mixin class may be about due...) - """ - if self.docstring: - self.docstring.parse(parser) - - # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: - # Experimental <interpreted> node code... - def find_docstrings(self): - """Find each docstring in our subtree. - - (The oddly plural name is for compatibility with those things - that inherit from `Base` - another reason for thinking that a - mixin class may be about due...) - """ - if self.docstring: - self.docstring.transform_interpreted(self) - # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: - - def is_callable(self): - """Does it make sense to call an object of this type? - """ - return 0 - - def label(self): - """Return an XML-legal name for this object. - - Note that we implicitly assume that all objects have their - output XML/HTML representation written to the same file - - i.e., that we do not have to worry about references that - are anything beyond simple names. - - (another duplicate of a method in Base) - """ - str = string.replace(self.fullname,".","-") - str = string.replace(str,":","--") - return str - - def setValue(self,expr): - self.lastuse = expr - if not self.firstuse: - self.firstuse = expr - else: - self.reused = 1 - - def setGlobal(self): - self.isglobal = 1 - - def isGlobal(self): - return self.isglobal - - def getDocstring(self): - return self.docstring - - def getSelfName(self): - return self.selfname - - def getValueString(self): - if self.reused or not self.firstuse: - return None - else: - return utils.stringify_expr(self.firstuse) - - def getLastValueString(self): - """Return the stringified value of this name, or None. - """ - return utils.stringify_expr(self.lastuse) - - def actualLastValue(self): - """Return the actual value assigned to this name. - - If the value assigned is a constant, then return it, otherwise - raise a ValueError (we can't just return NULL because that *is* - a sensible constant!). - """ - thing = self.lastuse - # See utils.stringify_expr() for information on what we think - # we're doing here... - if isinstance(thing,compiler.ast.Const): - return thing.value - else: - raise ValueError,"Value assigned to %s is not a constant:\n" \ - "(%s)"%(self.getSelfName(),self.getLastValueString()) - - def getAsString(self): - str = self.name - val = self.getValueString() - if val: - str += " = " + val - return str - - def show(self,stream,indent=0): - """Override as necessary.""" - stream.write("%s%s %s"%(" "*indent,self.__class__.__name__, - self.getAsString())) - if self.isglobal: - stream.write(" (global)\n") - else: - stream.write("\n") - if self.docstring: - self.docstring.show(stream," "*(indent+2)) - - -# ---------------------------------------------------------------------- -class Argument(Name): - """An argument to a method or function. - - This is a separate class just to get the class name right, I'm afraid... - """ - pass - - -# ---------------------------------------------------------------------- -class ModuleValue(Name): - """A name defined by assignment at the top level of a module. - - This is a separate class just to get the class name right, I'm afraid... - """ - pass - - -# ---------------------------------------------------------------------- -class ClassValue(Name): - """A name defined by assignment within a class definition. - - This is a separate class just to get the class name right, I'm afraid... - """ - pass - - -# ---------------------------------------------------------------------- -class InstanceValue(Name): - """An instance defined by assignment within a method - e.g., self.thing - - TO DO: Hmm - I need to actually *implement* the use of `self.our_class` - and `self.see_also`. - """ - - def __init__(self,*args,**kws): - Name.__init__(self,*args,**kws) - - self.our_class = None - """If we are self.thing in a method, then "self" refers to a class - (the one enclosing the method!), and this will be a reference to it. - """ - - self.see_also = None - """If the class already defined a `ClassInstance` of this name, - then we'll ultimately want a reference to that class here, so - that our output can say "see also class instance XXX". - """ - - def set_class(self,klass): - """Indicate which Class we really belong to. - """ - self.our_class = klass - - -# ---------------------------------------------------------------------- -class ImportName(Name): - """A name assigned by "import" or "from ... import" - - Should we distinguish these? ((yes)) - - This is a separate class just to get the class name right, I'm afraid... - - NB: we don't yet deal with "import ... as ..." - """ - - def is_callable(self): - """Does it make sense to call an object of this type? - - Unfortunately, if it's something imported with "from ... import" - there is no simple way of knowing - so maybe err on the side of - assuming so. - - ((Note that this is actually a good argument for separating out - the two sorts of import, since we know *modules* are not callable, - and "import" just makes modules visible.)) - """ - return 1 - - -# ---------------------------------------------------------------------- -def test_parse_module(filename): - print "Reading file %s"%filename - return Module(filename,debug=0) - -def test_show_ast(thing): - print - print "AST" - print "===" - thing.show(sys.stdout) - -def test_show_scopes(thing): - print - print "Scopes" - print "======" - thing.scope_show() - -def test(): - print "Testing pysource/visit.py" - if len(sys.argv) <= 1: - print "Usage: pysource/visit.py <python-file>" - return - filename = sys.argv[1] - thing = test_parse_module(filename) - test_show_ast(thing) - test_show_scopes(thing) - -if __name__ == "__main__": - test() diff --git a/sandbox/tibs/pysource2/__init__.py b/sandbox/tibs/pysource2/__init__.py deleted file mode 100644 index d7e9d8a32..000000000 --- a/sandbox/tibs/pysource2/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -"""Package pysource2 - Python source to Python source documentation - -:Author: Tibs -:Contact: tibs@tibsnjoan.co.uk -:Revision: $Revision$ -:Date: $Date$ -:Copyright: This module has been placed in the public domain. -""" - -__docformat__ = 'reStructuredText' diff --git a/sandbox/tibs/pysource2/log.txt b/sandbox/tibs/pysource2/log.txt deleted file mode 100644 index 9bae3e9e0..000000000 --- a/sandbox/tibs/pysource2/log.txt +++ /dev/null @@ -1,113 +0,0 @@ -============================= -Writing and testing pysource2 -============================= - -:Author: Tibs -:Contact: tibs@tibsnjoan.co.uk -:Revision: $Revision$ -:Date: $Date$ -:Copyright: This document has been placed in the public domain. - -pysource2 is my attempt to rewrite the original pysource. pysource -itself was a proof-of-concept module to find docstrings withing Python -source files and present them as (by default) HTML documentation, as -described by the Docutils project. Since it was written before the -Docutils codebase became stabilised around its current Reader/Writer -patterns, it doesn't really mesh well with the current approaches. Also, -lots of the code is fairly grotty anyway, and could do with a rewrite on -principle - not least because it is not well tested. - -So, pysource2 is both that rewrite, and also an attempt on my part to -learn how to do test driven development. - -Setting the path -================ - -I want to take my docutils stuff directly from the source directories, -so that I work with the latest CVS code, and don't have to keep installing -things. Thus I need to set the Python path to point to the source -directories:: - - export PYTHONPATH=${PYTHONPATH}:${HOME}/docutils - -Since I'm using Python 2.2.3, I also need the modules in the "extras" -directory:: - - export PYTHONPATH=${PYTHONPATH}:${HOME}/docutils/extras - -If I want access to the testing stuff, I also need the "test" -directory:: - - export PYTHONPATH=${PYTHONPATH}:${HOME}/docutils/test - - -NB: Relies upon the code in docutils/docutils/readers/python/moduleparser.py - -Log -=== -The "obvious" place to start is with packages - the previous pysource -never did quite get them right (for a start, it wouldn't cope with -sub-packages). Also, having a utility to report on packages, then on -modules, and gradually on to finer levels of detail, seems like giving -something useful as soon as possible. - -It looked over-complex to adopt the docutils test framework itself, -initially, especially since I am new both to unit testing *and* to test -driven development. So I am being less ambitious, and working with -"pure" unit tests - I reckon I'll learn more that way. - -So, the first pass gives me package.py and test_package.py. - -My first impressions of (such a little bit of) development is that TDD -does indeed give one the feeling of reassurance I'd expected from my -half-TDD efforts in Java at LSL. - -Initially, I was looking to detect a request for a package that didn't -exist, or wasn't a directory file, explicitly, with dedicated -exceptions. This felt rather over-complex, and indeed refactoring those -tests out and just catching a (non-explicit) OSError in the tests works -well enough - in reality, a user is not going to ask to parse a package -that is not already known to be an existant directory (heck, the "user" -is probably a program that's just figured out if the thing whose -documentation is wanted is a file or a directory), and if they do then -OSError makes sense since it is what one would normally get. - - -Questions -========= - -* Should we attempt to parse files that don't end in ".py"? - - What about ".pyw"? - - What about Python files on Unix which have had their extension removed and - been made executable? - -* Should there be an option to produce a document for a directory of Python - files that is not a package - e.g., a directory of useful scripts put - together just to be on the UNIX path, or Python's own library. - - -TODO -==== - - * Add a method to Module to indicate if it has an Attribute called - __docformat__, and if so, what its value is. - - * That requires understanding how the testing for the moduleparser is - organised and works, so I can add an appropriate test. - - * At which stage, should I incorporate Package (and NotPython) therein? - - * Write a simple transform (first learn how!) to parse any Docstring - contents in a module with __docformat__ equal to one of the reStructuredText - indicators. - - * Write another transform to turn the Pythonic doctree into a standard one. - - * At which point, we'll have something useful, albeit not very powerful, - so provide an appropriate command line interface for (at least) HTML output. - - * Work out how to do attribute references, etc., in *this* context (I have - no idea if the mechanisms from the original pysource will be any use). - diff --git a/sandbox/tibs/pysource2/not_a_directory b/sandbox/tibs/pysource2/not_a_directory deleted file mode 100644 index e69de29bb..000000000 --- a/sandbox/tibs/pysource2/not_a_directory +++ /dev/null diff --git a/sandbox/tibs/pysource2/package.py b/sandbox/tibs/pysource2/package.py deleted file mode 100644 index 96524693f..000000000 --- a/sandbox/tibs/pysource2/package.py +++ /dev/null @@ -1,185 +0,0 @@ -"""package.py - support for calculating package documentation. - -:Author: Tibs -:Contact: tibs@tibsnjoan.co.uk -:Revision: $Revision$ -:Date: $Date$ -:Copyright: This module has been placed in the public domain. -""" - -__docformat__ = 'reStructuredText' - -import os -from docutils.readers.python.moduleparser import Node, parse_module - -DEBUG = 0 - -class NotAPackageException(Exception): - pass - - -# ---------------------------------------------------------------------- -class Package(Node): - """This class represents a Python package. - - `filename` is the name of the package - i.e., the package name. - This may be extended/altered/expanded to include/disambiguate the - name of the package, the "full" name of the package (e.g., if it is - a sub-package) and the full path of the package, as needs indicate. - - Note that a package must, by definition, include at least one module, - i.e., __init__.py (otherwise, it isn't a package). - """ - - def __init__(self, filename): - """Initialise a Package. - - Note that this does *not* take a "node" argument, since a Package - is not actually quite like the Module and other sub-nodes. - - @@@ (Actually, there's a case to say that Node should be able to take - a "node" value of None and cope, in which case our life would be - easier - I may work on that later on...) - """ - # Hackery - the following two lines copied from Node itself. - self.children = [] - self.lineno = None - self.filename = filename - - def attlist(self): - return Node.attlist(self, filename=self.filename) - - - -# ---------------------------------------------------------------------- -class NotPython(Node): - """This class is used to represent a non-Python file. - - @@@ If the file isn't Python, should we try for reStructuredText? - """ - - def __init__(self, filename): - """Initialise a NotPython instance. - - @@@ Same caveats as Package. - """ - # Hackery - the following two lines copied from Node itself. - self.children = [] - self.lineno = None - self.filename = filename - - def attlist(self): - return Node.attlist(self, filename=self.filename) - - -# ---------------------------------------------------------------------- -def parse_package_or_module(path): - """Parse a package or module for documentation purposes. - - `path` should either be a directory representing a Python package, or - a single Python file. - """ - path = os.path.normpath(path) - if os.path.isdir(path): - return parse_package(path) - else: - return parse_file(path,path) - -def parse_package(package_path): - """Parse a package for documentation purposes. - - `package_path` should be the system path of the package directory, which is - not necessarily the same as the Python path... - """ - - if DEBUG: print "Parsing package",package_path - - package_path = os.path.normpath(package_path) - dir,file = os.path.split(package_path) - if dir == "": - dir = "." - return parse_subpackage(dir,file) - -def parse_subpackage(package_path,subpackage): - """Parse a subpackage for documentation purposes. - - `package_path` should be the system path of the package directory, - and `subpackage` is the (file) name of the subpackage therein. It - is assumed that this is already known to be a directory. - """ - - sub_path = os.path.join(package_path,subpackage) - - if DEBUG: print "Parsing sub-package",sub_path - - files = os.listdir(sub_path) - if "__init__.py" not in files: - raise NotAPackageException,\ - "Directory '%s' is not a Python package"%sub_path - - node = Package(subpackage) - - # Should we sort the files? Well, if we don't have them in a predictable - # order, it is harder to test the result(!), and also I believe that it - # is easier to use the output if there is some obvious ordering. Of course, - # the question then becomes whether packages and modules should be in the - # same sequence, or separated. - files.sort() - - for filename in files: - fullpath = os.path.join(sub_path,filename) - if os.path.isdir(fullpath): - try: - node.append(parse_subpackage(sub_path,filename)) - except NotAPackageException: - pass - else: - # We do *not* want to try .pyc or .pyo files - we can guarantee - # that they won't parse (the Python compiler code gets unhappy - # about NULL bytes therein), and we definitely don't want an - # entry for such files in our documentation. - # Similarly, I work on Linux, and don't want to consider files - # that end with "~" (this last is a bit nasty...) - if os.path.splitext(filename)[1] not in (".pyc",".pyo") and \ - filename[-1] != "~": - node.append(parse_file(fullpath,filename)) - return node - -def parse_file(fullpath,filename): - """Parse a single file (which we hope is a Python file). - - * `fullpath` is the full path of the file - * `filename` is the name we want to use for it in the docutils tree - - Returns a docutils parse tree for said file. - """ - - if DEBUG: print "Parsing file",fullpath - - # @@@ Should we worry about the extension of the file? - # Trying to use that to predict the contents can be a problem - # - we already know that we have to worry about ".pyw" as well - # as ".py", not to mention the possibility (e.g., on Unix) of - # having removed the extension in order to make an executable - # file "look" more like a Unix executable. On the whole, it's - # probably better to try to parse a file, and worry about it - # not parsing if/when that occurs. - module = open(fullpath) - try: - module_body = module.read() - try: - module_node = parse_module(module_body,filename) - except SyntaxError: - # OK - it wasn't Python - so what *should* we do with it? - module_node = NotPython(filename) - if DEBUG: print " (not Python)" - return module_node - finally: - module.close() - - - -# ---------------------------------------------------------------------- -if __name__ == "__main__": - result = parse_package("trivial_package") - print result diff --git a/sandbox/tibs/pysource2/pysrc2html.py b/sandbox/tibs/pysource2/pysrc2html.py deleted file mode 100755 index 9aaf26782..000000000 --- a/sandbox/tibs/pysource2/pysrc2html.py +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env python -"""pysrc2html - Read Python package/modules and output HTML documentation - -@@@ I'm not terribly happy with the name of this module, but it will do for -now (pydoc2html *might* be better?) - -:Author: Tibs -:Contact: tibs@tibsnjoan.co.uk -:Revision: $Revision$ -:Date: $Date$ -:Copyright: This module has been placed in the public domain. -""" - -__docformat__ = 'reStructuredText' - -import sys -from package import parse_package_or_module -import transform -from docutils.writers.html4css1 import Writer -from docutils.frontend import OptionParser - -usage = '%prog [options] [<package-directory> | <python-file> [html-file]]' -description = ('Generates .html documentation for the given Python package' - ' or module.') - -writer = Writer() - -option_parser = OptionParser(components=[writer], - usage=usage,description=description) - -settings = option_parser.parse_args(sys.argv[1:]) - -source_path = settings._source -target_path = settings._destination - -nodes = parse_package_or_module(source_path) - -# That then needs converting to a docutils tree -document = transform.make_document(nodes,settings) - -# And *that* wants converting to the appropriate output format -try: - target = open(target_path,"w") - writer.write(document,target) -finally: - target.close() diff --git a/sandbox/tibs/pysource2/reader.py b/sandbox/tibs/pysource2/reader.py deleted file mode 100644 index 6e4c7dfd2..000000000 --- a/sandbox/tibs/pysource2/reader.py +++ /dev/null @@ -1,31 +0,0 @@ -"""reader.py - docutils Reader for Python source code - -:Author: Tibs -:Contact: tibs@tibsnjoan.co.uk -:Revision: $Revision$ -:Date: $Date$ -:Copyright: This module has been placed in the public domain. -""" - -__docformat__ = 'reStructuredText' - -import docutils.readers -from docutils.readers.python.moduleparser import Node, parse_module -#from package import parse_package -from transform import make_document - -class Reader(docutils.readers.Reader): - """A Python source code specific Reader. - """ - - config_section = 'python reader' - config_section_dependencies = ('readers',) - - def parse(self): - """Parse `self.input` into a document tree.""" - - tree = parse_module(self.input,self.source.source_path) - self.document = document = make_document(tree) - #self.document = document = self.new_document() - #self.parser.parse(self.input, document) - document.current_source = document.current_line = None diff --git a/sandbox/tibs/pysource2/temp.rst b/sandbox/tibs/pysource2/temp.rst deleted file mode 100644 index fa2836232..000000000 --- a/sandbox/tibs/pysource2/temp.rst +++ /dev/null @@ -1,81 +0,0 @@ -<document source="temp.txt"> - <comment xml:space="preserve"> - This is a simple reStructuredText file that represents what I would - <comment xml:space="preserve"> - like the output of transforming my test Python code to be - <section class="package" id="package-trivial-package" name="package trivial_package"> - <title> - Package trivial_package - <section class="module" id="module-trivial-package-init" name="module trivial_package.__init__"> - <title> - Module trivial_package.__init__ - <block_quote class="docstring"> - <paragraph> - A simple docstring. - <section class="module" id="module-trivial-package-file1" name="module trivial_package.file1"> - <title> - Module trivial_package.file1 - <block_quote class="docstring"> ## Hmm - not quite right - <paragraph> - This is the first example file. It - <emphasis> - does - use reStructuredText. - <paragraph> - Attributes: - <bullet_list bullet="*"> - <list_item> - <paragraph> - __docformat__ = "reST" (line 5) - <paragraph> - Import: os (line 7) - <section class="class" id="class-trivial-package-file1-fred" name="class trivial_package.file1.fred"> - <title> - Class trivial_package.file1.Fred - <field_list> - <field> - <field_name> - line - <field_body> - <paragraph> - 9 - <paragraph class="docstring"> ## Hmm - An example class - it announces each instance as it is created. - <section class="method" id="method-trivial-package-file1-fred-init" name="method trivial_package.file1.fred.__init__"> - <title> - Method trivial_package.file1.Fred.__init__ - <field_list> - <field> - <field_name> - line - <field_body> - <paragraph> - 13 - <field> - <field_name> - parameters - <field_body> - <paragraph> - self - <section class="module" id="module-trivial-package-file2" name="module trivial_package.file2"> - <title> - Module trivial_package.file2 - <block_quote class="docstring"> - <paragraph> - This module is - <emphasis> - not - using reStructuredText for its docstrings. - <section class="file" id="file-trivial-package-not-python" name="file trivial_package.not_python"> - <title> - File trivial_package.not_python - <paragraph> - (Not a Python module) - <section class="package" id="package-trivial-package-sub-package" name="package trivial_package.sub_package"> - <title> - Package trivial_package.sub_package - <section class="module" id="module-trivial-package-sub-package-init" name="module trivial_package.sub_package.__init__"> - <title> - Module trivial_package.sub_package.__init__ - <paragraph> - (No documentation) diff --git a/sandbox/tibs/pysource2/temp.txt b/sandbox/tibs/pysource2/temp.txt deleted file mode 100644 index dc1e9640f..000000000 --- a/sandbox/tibs/pysource2/temp.txt +++ /dev/null @@ -1,79 +0,0 @@ -.. This is a simple reStructuredText file that represents what I would -.. like the output of transforming my test Python code to be - -.. class:: package - -======================= -Package trivial_package -======================= - -.. class:: module - -Module trivial_package.__init__ -=============================== - - .. class:: docstring - - A simple docstring. - -.. class:: module - -Module trivial_package.file1 -============================ - - .. class:: docstring - - This is the first example file. It *does* use reStructuredText. - - Attributes: - - * __docformat__ = "reST" (line 5) - - Import: os (line 7) - -.. class:: class - -Class trivial_package.file1.Fred --------------------------------- - -:line: 9 - - .. class:: docstring - - An example class - it announces each instance as it is created. - -.. class:: method - -Method trivial_package.file1.Fred.__init__ -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:line: 13 -:parameters: self - -.. class:: module - -Module trivial_package.file2 -============================ - - .. class:: docstring - - This module is *not* using reStructuredText for its docstrings. - -.. class:: file - -File trivial_package.not_python -=============================== - -(Not a Python module) - -.. class:: package - -Package trivial_package.sub_package -=================================== - -.. class:: module - -Module trivial_package.sub_package.__init__ -------------------------------------------- - -(No documentation) diff --git a/sandbox/tibs/pysource2/test_package.py b/sandbox/tibs/pysource2/test_package.py deleted file mode 100644 index d267d3937..000000000 --- a/sandbox/tibs/pysource2/test_package.py +++ /dev/null @@ -1,179 +0,0 @@ -#! /usr/bin/env python -"""test_package.py - -Unit tests for parsing packages for pysource. - -Initially, this is a standalone test, but ultimately it may be merged into the -mechanisms used for the Docutils self-tests. - -:Author: Tibs -:Contact: tibs@tibsnjoan.co.uk -:Revision: $Revision$ -:Date: $Date$ -:Copyright: This module has been placed in the public domain. -""" - -__docformat__ = 'reStructuredText' - -import unittest - -from package import parse_package, NotAPackageException -from transform import make_document - -# The following is to ensure that there are .pyc files in the package -# - this is important for testing, since the Python compiler gets quite -# unhappy if given a non-text file to play with (it doesn't like null bytes), -# so we need to do something about that... -import trivial_package - -class PackageTest(unittest.TestCase): - - def testNoSuchDirectory(self): - """Not a package - no such directory. - """ - - self.assertRaises(OSError, - parse_package, - "no_such_directory") - - def testNotADirectory(self): - """Not a package - file is not a directory. - """ - - self.assertRaises(OSError, - parse_package, - "not_a_directory") - - def testNotAPackage(self): - """Not a package - directory is empty. - """ - - self.assertRaises(NotAPackageException, - parse_package, - "not_a_package") - - def testPackage(self): - """A package containing subpackage(s) - - The directory is called "trivial_package" for historical reasons. - """ - - wanted_result = """\ -<Package filename="trivial_package"> - <Module filename="__init__.py"> - <Docstring> - A simple docstring. - <Module filename="file1.py"> - <Docstring> - This is the first example file. It *does* use reStructuredText. - <Attribute lineno="5" name="__docformat__"> - <Expression lineno="5"> - "reST" - <Import lineno="7"> - os - <Class lineno="9" name="Fred"> - <Docstring lineno="9"> - An example class - it announces each instance as it is created. - <Method lineno="13" name="__init__"> - <ParameterList lineno="13"> - <Parameter lineno="13" name="self"> - <Module filename="file2.py"> - <Docstring> - This module is *not* using reStructuredText for its docstrings. - <NotPython filename="not_python"> - <Package filename="sub_package"> - <Module filename="__init__.py">\n""" - - actual_result = str(parse_package("trivial_package")) - - if wanted_result != actual_result: - print "+++++++++++++++++++++++++ WANT" - print wanted_result - print "+++++++++++++++++++++++++ GOT" - print actual_result - print "+++++++++++++++++++++++++" - - self.assertEqual(actual_result,wanted_result) - - def testMakeDocument(self): - """ - Turn our Package tree into a docutils Document. - """ - - # I've split the wanted result string up into substrings so I can - # amend it more easily (or so I hope). - trivial_package = """\ -<document source="Package trivial_package"> - <section class="package" id="package-trivial-package" name="package trivial_package"> - <title> - Package trivial_package\n""" - - # The "xml:space" attribute is by observation, not prediction - module_init = """\ - <section class="module" id="module-trivial-package-init" name="module trivial_package.__init__"> - <title> - Module trivial_package.__init__ - <literal_block class="docstring" xml:space="preserve"> - A simple docstring.\n""" - - module_file1 = """\ - <section class="module" id="module-trivial-package-file1" name="module trivial_package.file1"> - <title> - Module trivial_package.file1 - <literal_block class="docstring" xml:space="preserve"> - This is the first example file. It *does* use reStructuredText. - <section class="class" id="class-trivial-package-file1-fred" name="class trivial_package.file1.fred"> - <title> - Class trivial_package.file1.Fred - <literal_block class="docstring" xml:space="preserve"> - An example class - it announces each instance as it is created.\n""" - - module_file2 = """\ - <section class="module" id="module-trivial-package-file2" name="module trivial_package.file2"> - <title> - Module trivial_package.file2 - <literal_block class="docstring" xml:space="preserve"> - This module is *not* using reStructuredText for its docstrings.\n""" - - non_python_file = """\ - <section class="file" id="file-trivial-package-not-python" name="file trivial_package.not_python"> - <title> - File trivial_package.not_python - <paragraph> - File - <literal> - not_python - is not a Python module.\n""" - - sub_package = """\ - <section class="package" id="package-trivial-package-sub-package" name="package trivial_package.sub_package"> - <title> - Package trivial_package.sub_package\n""" - - sub_module_init = """\ - <section class="module" id="module-trivial-package-sub-package-init" name="module trivial_package.sub_package.__init__"> - <title> - Module trivial_package.sub_package.__init__\n""" - - wanted_result = (trivial_package + module_init + module_file1 + - module_file2 + non_python_file + sub_package + - sub_module_init) - - tree = parse_package("trivial_package") - - document = make_document(tree) - - actual_result = document.pformat() - - if wanted_result != actual_result: - print "+++++++++++++++++++++++++ WANT" - print wanted_result - print "+++++++++++++++++++++++++ GOT" - print actual_result - print "+++++++++++++++++++++++++" - - self.assertEqual(actual_result,wanted_result) - - -if __name__ == "__main__": - unittest.main() diff --git a/sandbox/tibs/pysource2/test_reader.py b/sandbox/tibs/pysource2/test_reader.py deleted file mode 100644 index e48f20749..000000000 --- a/sandbox/tibs/pysource2/test_reader.py +++ /dev/null @@ -1,127 +0,0 @@ -#! /usr/bin/env python -"""test_reader.py - -Unit tests for the Python source Reader - -Initially, this is a standalone test, but ultimately it may be merged into the -mechanisms used for the Docutils self-tests. - -:Author: Tibs -:Contact: tibs@tibsnjoan.co.uk -:Revision: $Revision$ -:Date: $Date$ -:Copyright: This module has been placed in the public domain. -""" - -__docformat__ = 'reStructuredText' - -import unittest - -from package import parse_package -from transform import make_document -from reader import Reader -from docutils.core import publish_string -from docutils.readers.python.moduleparser import parse_module - -class PackageTest(unittest.TestCase): - - def testReader(self): - """Test the reader works as expected - """ - reader = Reader() - - source="# A Python comment" - source_path="test.py" - - # Hmm - extra debugging info... - publish_string = publish_string_with_traceback - - actual_result = publish_string(reader=reader,reader_name="python", - parser_name="restructuredtext", - writer_name="pseudoxml", - source=source, source_path=source_path) - - wanted_result = """\ -<document source="Module test"> - <section class="module" id="module-test" name="module test"> - <title> - Module test\n""" - - - if wanted_result != actual_result: - print "+++++++++++++++++++++++++ WANT" - print wanted_result - print "+++++++++++++++++++++++++ GOT" - print actual_result - print "+++++++++++++++++++++++++" - - self.assertEqual(actual_result,wanted_result) - - def testTool(self): - """Trying to think what to do for packages""" - # The Reader interface is designed to work with single test entities, - # either a string or the content of a text file (i.e., a single thing - # that can be accessed via some sort of "read" method). - # This doesn't work for packages, where one has multiple files. - # Thus I suspect that the Reader interface is not appropriate for - # what I want to do (at least, not without doing it unnecessary - # violence and making it a lot more complicated). - # So I need to do things "by hand"... - - source="# A Python comment" - source_path="test.py" - - # Since a body of text is a Module, not a Package, we'll go straight - # to it - nodes = parse_module(source,source_path) - - # That then needs converting to a docutils tree - document = make_document(nodes) - - # And *that* wants converting to the appropriate output format - - from docutils.writers.pseudoxml import Writer - writer = Writer() - writer.document = document - writer.translate() - actual_result = writer.output - - wanted_result = """\ -<document source="Module test"> - <section class="module" id="module-test" name="module test"> - <title> - Module test\n""" - - - if wanted_result != actual_result: - print "+++++++++++++++++++++++++ WANT" - print wanted_result - print "+++++++++++++++++++++++++ GOT" - print actual_result - print "+++++++++++++++++++++++++" - - self.assertEqual(actual_result,wanted_result) - - -def publish_string_with_traceback(reader=None,reader_name=None, - parser_name=None,writer_name=None, - source=None,source_path=None): - """A modified version of publish_string, so I can request traceback. - """ - from docutils.core import Publisher - from docutils import io - pub = Publisher(reader=reader, - source_class=io.StringInput, - destination_class=io.StringOutput) - pub.set_components(reader_name="python", - parser_name="restructuredtext", - writer_name="pseudoxml") - - pub.process_command_line(argv=["--traceback"]) - - pub.set_source(source=source, source_path=source_path) - return pub.publish(enable_exit=False) - - -if __name__ == "__main__": - unittest.main() diff --git a/sandbox/tibs/pysource2/transform.py b/sandbox/tibs/pysource2/transform.py deleted file mode 100644 index 1be37bd02..000000000 --- a/sandbox/tibs/pysource2/transform.py +++ /dev/null @@ -1,157 +0,0 @@ -"""transform.py - create a docutils Document tree from a Package or Module tree - -:Author: Tibs -:Contact: tibs@tibsnjoan.co.uk -:Revision: $Revision$ -:Date: $Date$ -:Copyright: This module has been placed in the public domain. -""" - -__docformat__ = 'reStructuredText' - -import os -from docutils.utils import new_document -import docutils.nodes as nodes -from package import Package, NotPython -from docutils.readers.python.moduleparser import Module, Class, Docstring - -def make_document(tree,settings=None): - """Return a docutils Document tree constructed from this Python tree. - - The tree given must be either a Package or Module tree. - """ - - # @@@ Can it ever be anything other than a package or module? - # I'd assert not - the module is the basic "smallest unit". - # Should we test that? - if isinstance(tree,Package): - document = new_document("Package %s"%tree.filename,settings) - section = make_package_section(tree) - else: - document = new_document("Module %s"%os.path.splitext(tree.filename)[0], - settings) - section = make_module_section(tree) - document.append(section) - return document - -def make_package_section(tree,parent_name=None): - """Return a docutils tree constructed from this Package tree - """ - if parent_name: - tree_name = "%s.%s"%(parent_name,tree.filename) - else: - tree_name = tree.filename - title = "Package %s"%(tree_name) - - # @@@ Do I really want to normalise (case fold, in particular) - # the id/name for this section? Python names can legitimately - # distinguish case, and whilst that's not terribly useful at - # the file level (since not all OS/filesystems keep such a - # distinction), it certainly is a valid possibility *within* - # a file... - # - # make_id() produces a name that starts with [a-z] and continues - # with a-z, 0-9 and hyphen (or something like that). - # - # fully_normalize_name() reduces spaces to single spaces (OK), - # but also lowercases. - # - # @@@ Think more on usage here, I guess - section = nodes.section(CLASS="package",id=nodes.make_id(title), - name=nodes.fully_normalize_name(title)) - title = nodes.title(text=title) - section.append(title) - - # @@@ I'm enforcing an order of modules before non-python files before - # subpackages here - # - do I really care? - # - do I want some other way order? - # - is this the best way to do it (e.g., I could sort the children - # into order first instead) - for child in tree.children: - if isinstance(child,Module): - subsection = make_module_section(child,tree_name) - section.append(subsection) - for child in tree.children: - if isinstance(child,NotPython): - subsection = make_not_python_section(child,tree_name) - section.append(subsection) - for child in tree.children: - if isinstance(child,Package): - subsection = make_package_section(child,tree_name) - section.append(subsection) - return section - -def make_module_section(tree,parent_name=None): - """Return a docutils tree constructed from this Module sub-tree - """ - module_name = os.path.splitext(tree.filename)[0] - if parent_name: - tree_name = "%s.%s"%(parent_name,module_name) - else: - tree_name = module_name - title = "Module %s"%(tree_name) - - # @@@ Same considerations on id/name as above - section = nodes.section(CLASS="module",id=nodes.make_id(title), - name=nodes.fully_normalize_name(title)) - title = nodes.title(text=title) - section.append(title) - - # Assume that the docstring must be the first child - if len(tree.children) > 0 and \ - isinstance(tree.children[0],Docstring): - section.append(make_docstring(tree.children[0])) - - # @@@ Again, I'm looking for classes before anything else - for child in tree.children: - if isinstance(child,Class): - subsection = make_class_section(child,tree_name) - section.append(subsection) - - return section - -def make_not_python_section(tree,parent_name=None): - """Return a docutils tree constructed from this NotPython (file) sub-tree - """ - if parent_name: - tree_name = "%s.%s"%(parent_name,tree.filename) - else: - tree_name = tree.filename - title = "File %s"%(tree_name) - - # @@@ Same considerations on id/name as above - section = nodes.section(CLASS="file",id=nodes.make_id(title), - name=nodes.fully_normalize_name(title)) - title = nodes.title(text=title) - section.append(title) - paragraph = nodes.paragraph(text="File ") - paragraph.append(nodes.literal(text=tree.filename)) - paragraph.append(nodes.Text(" is not a Python module.")) - section.append(paragraph) - return section - -def make_class_section(tree,parent_name): - """Return a docutils tree constructed from this Class sub-tree - """ - tree_name = "%s.%s"%(parent_name,tree.name) - title = "Class %s"%(tree_name) - - # @@@ Same considerations on id/name as above - section = nodes.section(CLASS="class",id=nodes.make_id(title), - name=nodes.fully_normalize_name(title)) - title = nodes.title(text=title) - section.append(title) - - # Assume that the docstring must be the first child - if len(tree.children) > 0 and \ - isinstance(tree.children[0],Docstring): - section.append(make_docstring(tree.children[0])) - - # @@@ Don't forget that we want base classes to be named at - # some point - - return section - -def make_docstring(docstring): - return nodes.literal_block(text=docstring.text,CLASS="docstring") diff --git a/sandbox/tibs/pysource2/trivial_package/__init__.py b/sandbox/tibs/pysource2/trivial_package/__init__.py deleted file mode 100644 index 3e664b315..000000000 --- a/sandbox/tibs/pysource2/trivial_package/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -"""A simple docstring. -""" diff --git a/sandbox/tibs/pysource2/trivial_package/file1.py b/sandbox/tibs/pysource2/trivial_package/file1.py deleted file mode 100644 index 9636cb676..000000000 --- a/sandbox/tibs/pysource2/trivial_package/file1.py +++ /dev/null @@ -1,14 +0,0 @@ -#! /usr/bin/env python -"""This is the first example file. It *does* use reStructuredText. -""" - -__docformat__ = "reST" - -import os - -class Fred: - """An example class - it announces each instance as it is created. - """ - - def __init__(self): - print "Aha" diff --git a/sandbox/tibs/pysource2/trivial_package/file2.py b/sandbox/tibs/pysource2/trivial_package/file2.py deleted file mode 100644 index d0e203574..000000000 --- a/sandbox/tibs/pysource2/trivial_package/file2.py +++ /dev/null @@ -1,2 +0,0 @@ -"""This module is *not* using reStructuredText for its docstrings. -""" diff --git a/sandbox/tibs/pysource2/trivial_package/not_python b/sandbox/tibs/pysource2/trivial_package/not_python deleted file mode 100644 index b447bba55..000000000 --- a/sandbox/tibs/pysource2/trivial_package/not_python +++ /dev/null @@ -1,3 +0,0 @@ -The content of this file is not Python. - -(Although it *is*, in fact, reStructuredText.) diff --git a/sandbox/tibs/pysource2/trivial_package/sub_package/__init__.py b/sandbox/tibs/pysource2/trivial_package/sub_package/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/sandbox/tibs/pysource2/trivial_package/sub_package/__init__.py +++ /dev/null |