diff options
Diffstat (limited to 'sandbox/tibs/pysource/buildtree.py')
-rwxr-xr-x | sandbox/tibs/pysource/buildtree.py | 403 |
1 files changed, 0 insertions, 403 deletions
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() |