a pastebin project

Unnamed

  1. """ An experiment using breve xml library, and metaclass.
  2.  
  3. Author: Ben West
  4. Email: bewest at gmail
  5. blog: http://bewest.wordpress.com/
  6.  
  7. I hang out on freenode.
  8.  
  9. Breve is available at <URL:http://breve.twisty-industries.com/>.
  10. From the website:
  11. -----------------
  12.   Breve is a Python template engine that is designed to be clean and elegant
  13.   with minimal syntax.
  14.   Like Stan (and unlike most Python template engines), Breve is neither an XML
  15.   parser nor PSP-style regex engine. Rather, Breve templates are actual Python
  16.   expressions. In popular parlance, Breve is an internal DSL.
  17.  
  18. This seemed like a really elegant idea to me.  I began to wonder about
  19. generating classes that would allow easy manipulation of XML documents, given
  20. a schema.  The simplest way to create a new xml tag is to call Proto(),
  21. with the name of the tag as the argument.  I don't know if there is a way to
  22. also control the attributes as you are generating the XML.  If there's not,
  23. there should be.
  24.  
  25. Once, I got some simple hello world tests going with breve, I started fooling
  26. around with ways to create new sets of tags.  I kept fooling around, trying to
  27. reduce the number of steps necessary, until I came up with the current
  28. contents of this experiment.
  29.  
  30. The basic idea is to reduce the amount of work required for an author to start
  31. creating valid templates in breve for an known XML vocabulary that has a
  32. schema.  This experiment goes far enough to show that this feature is
  33. definitely possible.
  34.  
  35. The customtags class implements a callable.  The reason it is a class is so
  36. that other authors might override certain aspects of it, such as findTags,
  37. getDocString.  When an instance is called, it will construct a type (actually
  38. a metaclass) in which the __dict__ contains all the breve tag's found by
  39. findTags.  As a bonus, the docstring is also populated, providing the author
  40. with rich documentation on that class, potentially coming directly from the
  41. schema.
  42.  
  43. Using this technique, it would be possible to design a package, in which the
  44. __init__.py inspects a directory for the presence of schema files.  At
  45. runtime, this package could create all the classes necessary to start creating
  46. templates for those schemas.  Any time the schema changes, the class also
  47. changes, on the next import.  I believe this makes it more desirable over
  48. code generators that create python code containing the tags on disk.
  49. """
  50.  
  51. import sys
  52. import breve
  53. from breve import tags
  54. from breve.tags.html import tags as T
  55. import doctest
  56.  
  57. def hello_world():
  58.   """Hello world example.
  59.  
  60.   >>> str( hello_world() )
  61.   '<div>hello world</div>'
  62.   """
  63.   # breve takes care of html automatically.
  64.   return T.div[ 'hello world' ]
  65.  
  66.  
  67. def simple_customtag():
  68.   """Simple custom tag hello world example.
  69.  
  70.   >>> str( simple_customtag() )
  71.   '<foo>hello world</foo>'
  72.   """
  73.   newtag = tags.Proto('foo')
  74.   return newtag['hello world']
  75.  
  76.  
  77. # first define the callable class I discussed above.  It's instances will be
  78. # responsible for return a type representing an XML schema when called.
  79.  
  80. class customtags(object):
  81.   """This is an experiment to dynamically provision some tags.
  82.  
  83.  
  84.   Anyway, this is neat because you can load this up in the python shell and
  85.   get detailed help.  This is useful for having a config file that specifies
  86.   the location of some XML schema.  You can change the schema, and your code
  87.   will update automatically.
  88.  
  89.   XXX: This is probably bad practice because if something goes weird, it would
  90.   be nearly impossible to debug ;-)
  91.  
  92.   However, this would faciliate importing schema definitions as a module, and
  93.   getting rich help from the schema's documentation using the normal python
  94.   methods. (Eg, imaging putting this in __init__.py, and creating a class for
  95.   each schema in that directory.)
  96.  
  97.   For example:
  98.   >>> class MyTags(customtags()()): pass
  99.   >>> newtags = MyTags() # instantiate it
  100.   >>> str( newtags.feed['hello world'] )
  101.   '<feed>hello world</feed>'
  102.  
  103.   This example consisted of just plain calls with no parameters, so it may
  104.   seem a bit over the top, without a full implementation of all the features
  105.   previously described.
  106.   """
  107.   def __call__(self, *args, **kwds):
  108.     """Construct and then return a type representing a schema.
  109.     """
  110.     doc = self.getDocString()
  111.     class metaTagSet(type):
  112.       def __new__(cls, classname, bases, classdict):
  113.         classdict['__doc__'] = doc
  114.         # add each element as a breve tag to the new class's __dict__
  115.         for e in self.tags.keys():
  116.           classdict[e] = tags.Proto(e)
  117.         # TODO: is this preferred, or should I use super()?
  118.         return type.__new__(cls, classname, bases, classdict)
  119.  
  120.     # create a quick wrapper, so we can use normal inheritance syntax.
  121.     class TagSet(object):
  122.       __metaclass__ = metaTagSet
  123.  
  124.     return TagSet
  125.    
  126.   def __init__(self):
  127.     self.tags = self.findTags()
  128.  
  129.   def getDocString(self):
  130.     """Returns the doc string for the class.
  131.     """
  132.     doc = """A bunch of tags for use in breve, an s-expression xml generator.
  133.     \n"""
  134.     for e in self.tags.keys():
  135.       doc += "%s: %s\n" % (e, self.tags[e])
  136.     return doc
  137.  
  138.    
  139.   def findTags(self):
  140.     """In the future, this could simply parse a schema.
  141.     """
  142.     return {
  143.      'feed'      : """A feed elements contains comments, service, and
  144. collection.  It is the root element.""",
  145.     'comments'   : """comments is for containing a comment from one source.""",
  146.     'service'    : """A service descripts a set of capabilities.""",
  147.     'collection' : """Useful for containing members, and related things."""}
  148.  
  149.   # for now, just a dumb dictionary of element names and a doc string.
  150.  
  151. # wow... dynamically bind properties to a /class/ at runtime!
  152. # imagine an __init__.py that inspected the contents of a directory for schema
  153. # files, and then exported one of these for each schema. :-).
  154. # You get a nice literate programming environment, because your declaritive
  155. # documentation gets completely re-used in your procedural code, without you
  156. # repeating yourself at all.
  157. class CustomTags(customtags()()):
  158.   pass
  159.  
  160.  
  161. def main(args):
  162.   """A slightly more involved test...
  163.  
  164.   erm. basically if we get back xml the test works. (and it does)
  165.   """
  166.   template = hello_world()
  167.   #t = breve.Template ( tags.html.tags, doctype = '', xmlns = '' )
  168.   print template
  169.  
  170.  
  171.   newtags = CustomTags()
  172.   print newtags.feed[
  173.     [newtags.comments[ newtags.service, newtags.service ] for i in \
  174.       xrange(3)],
  175.     newtags.collection,
  176.     newtags.collection,
  177.     "hello world"
  178.   ]
  179.  
  180.  
  181. if __name__ == '__main__':
  182.   main(sys.argv)
  183.   doctest.testmod()

advertising

Create a Paste

Please enter your new post below (or upload a file instead):





Please note that information posted here will not expire by default. If you want it to expire, please set the expiry time above. If it is set to expire, web search engines will not be allowed to index it prior to it expiring. Items that are not marked to expire will be indexable by search engines. Be careful with your passwords.

worth-right
fantasy-obligation