[Checkins] SVN: Sandbox/malthe/chameleon.html/ Initial checkin of a dynamic HTML template language.

Malthe Borch mborch at gmail.com
Thu Sep 25 17:16:37 EDT 2008


Log message for revision 91489:
  Initial checkin of a dynamic HTML template language.

Changed:
  A   Sandbox/malthe/chameleon.html/
  A   Sandbox/malthe/chameleon.html/CHANGES.txt
  A   Sandbox/malthe/chameleon.html/README.txt
  A   Sandbox/malthe/chameleon.html/bootstrap.py
  A   Sandbox/malthe/chameleon.html/buildout.cfg
  A   Sandbox/malthe/chameleon.html/setup.py
  A   Sandbox/malthe/chameleon.html/src/
  A   Sandbox/malthe/chameleon.html/src/chameleon/
  A   Sandbox/malthe/chameleon.html/src/chameleon/__init__.py
  A   Sandbox/malthe/chameleon.html/src/chameleon/html/
  A   Sandbox/malthe/chameleon.html/src/chameleon/html/__init__.py
  A   Sandbox/malthe/chameleon.html/src/chameleon/html/language.py
  A   Sandbox/malthe/chameleon.html/src/chameleon/html/language.txt
  A   Sandbox/malthe/chameleon.html/src/chameleon/html/template.py
  A   Sandbox/malthe/chameleon.html/src/chameleon/html/template.txt
  A   Sandbox/malthe/chameleon.html/src/chameleon/html/tests/
  A   Sandbox/malthe/chameleon.html/src/chameleon/html/tests/__init__.py
  A   Sandbox/malthe/chameleon.html/src/chameleon/html/tests/layouts/
  A   Sandbox/malthe/chameleon.html/src/chameleon/html/tests/layouts/example.html
  A   Sandbox/malthe/chameleon.html/src/chameleon/html/tests/layouts/example.xss
  A   Sandbox/malthe/chameleon.html/src/chameleon/html/tests/test_doctests.py
  A   Sandbox/malthe/chameleon.html/src/chameleon/html/xss.py

-=-
Added: Sandbox/malthe/chameleon.html/CHANGES.txt
===================================================================
--- Sandbox/malthe/chameleon.html/CHANGES.txt	                        (rev 0)
+++ Sandbox/malthe/chameleon.html/CHANGES.txt	2008-09-25 21:16:36 UTC (rev 91489)
@@ -0,0 +1,8 @@
+Changelog
+=========
+
+Head
+~~~~
+
+
+

Added: Sandbox/malthe/chameleon.html/README.txt
===================================================================
--- Sandbox/malthe/chameleon.html/README.txt	                        (rev 0)
+++ Sandbox/malthe/chameleon.html/README.txt	2008-09-25 21:16:36 UTC (rev 91489)
@@ -0,0 +1,23 @@
+Overview
+========
+
+This package implements a template compiler for dynamic HTML
+documents. In particular, it supports the dynamic element language XSS
+which is used to set up dynamic content.
+
+XSS language
+------------
+
+The XSS language uses a CSS-compliant syntax to let you match HTML
+elements using CSS selectors and set up dynamic content
+definitions.
+
+For example:
+
+  html > head > title {
+    name: document-heading;
+    structure: true;
+    attributes: document-attributes;
+  }
+
+

Added: Sandbox/malthe/chameleon.html/bootstrap.py
===================================================================
--- Sandbox/malthe/chameleon.html/bootstrap.py	                        (rev 0)
+++ Sandbox/malthe/chameleon.html/bootstrap.py	2008-09-25 21:16:36 UTC (rev 91489)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id: bootstrap.py 71627 2006-12-20 16:46:11Z jim $
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+ez = {}
+exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+                     ).read() in ez
+ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+    cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+    os.P_WAIT, sys.executable, sys.executable,
+    '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+    dict(os.environ,
+         PYTHONPATH=
+         ws.find(pkg_resources.Requirement.parse('setuptools')).location
+         ),
+    ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)

Added: Sandbox/malthe/chameleon.html/buildout.cfg
===================================================================
--- Sandbox/malthe/chameleon.html/buildout.cfg	                        (rev 0)
+++ Sandbox/malthe/chameleon.html/buildout.cfg	2008-09-25 21:16:36 UTC (rev 91489)
@@ -0,0 +1,14 @@
+[buildout]
+develop = . ../chameleon.core
+parts = test
+
+[test]
+recipe = zc.recipe.testrunner
+environment = test-environment
+eggs =
+   chameleon.core [lxml]
+   chameleon.html
+
+[test-environment]
+CHAMELEON_DEBUG = False
+CHAMELEON_CACHE = False
\ No newline at end of file

Added: Sandbox/malthe/chameleon.html/setup.py
===================================================================
--- Sandbox/malthe/chameleon.html/setup.py	                        (rev 0)
+++ Sandbox/malthe/chameleon.html/setup.py	2008-09-25 21:16:36 UTC (rev 91489)
@@ -0,0 +1,37 @@
+from setuptools import setup, find_packages
+import sys
+
+version = '0.1'
+
+install_requires = [
+    'setuptools',
+    'zope.interface',
+    'zope.component',
+    'zope.i18n >= 3.5',
+    'chameleon.core',
+    'cssutils',
+    'lxml >= 2.1.1',
+    ]
+
+setup(name='chameleon.html',
+      version=version,
+      description="Dynamic HTML template compiler with XSS language support.",
+      long_description=open("README.txt").read(),
+      classifiers=[
+        "Programming Language :: Python",
+        "Topic :: Text Processing :: Markup :: HTML",
+        "Topic :: Text Processing :: Markup :: XML",
+        "Topic :: Software Development :: Libraries :: Python Modules",
+        ],
+      keywords='',
+      author='Malthe Borch and the Zope Community',
+      author_email='zope-dev at zope.org',
+      url='',
+      license='BSD',
+      namespace_packages=['chameleon'],
+      packages = find_packages('src'),
+      package_dir = {'':'src'},
+      include_package_data=True,
+      zip_safe=False,
+      install_requires=install_requires,
+      )

Added: Sandbox/malthe/chameleon.html/src/chameleon/__init__.py
===================================================================
--- Sandbox/malthe/chameleon.html/src/chameleon/__init__.py	                        (rev 0)
+++ Sandbox/malthe/chameleon.html/src/chameleon/__init__.py	2008-09-25 21:16:36 UTC (rev 91489)
@@ -0,0 +1,6 @@
+# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+    from pkgutil import extend_path
+    __path__ = extend_path(__path__, __name__)

Added: Sandbox/malthe/chameleon.html/src/chameleon/html/__init__.py
===================================================================
--- Sandbox/malthe/chameleon.html/src/chameleon/html/__init__.py	                        (rev 0)
+++ Sandbox/malthe/chameleon.html/src/chameleon/html/__init__.py	2008-09-25 21:16:36 UTC (rev 91489)
@@ -0,0 +1 @@
+#

Added: Sandbox/malthe/chameleon.html/src/chameleon/html/language.py
===================================================================
--- Sandbox/malthe/chameleon.html/src/chameleon/html/language.py	                        (rev 0)
+++ Sandbox/malthe/chameleon.html/src/chameleon/html/language.py	2008-09-25 21:16:36 UTC (rev 91489)
@@ -0,0 +1,159 @@
+import cgi
+import os
+
+from chameleon.core import translation
+from chameleon.core import config
+from chameleon.core import etree
+from chameleon.core import types
+from chameleon.core import utils
+
+true_values = 'true', '1', 'yes'
+
+from xss import parse_xss
+
+import lxml.cssselect
+
+def merge_dicts(dict1, dict2):
+    if dict2 is None:
+        return dict1
+    
+    keys = set(dict1).union(set(dict2))
+
+    for key in keys:
+        if key == 'class':
+            value1 = dict1.get(key)
+            value2 = dict2.get(key)
+
+            if value1 is not None:
+                if value2 is not None:
+                    dict1[key] += " " + value2
+            elif value2 is not None:
+                dict1[key] = value2
+        else:
+            value2 = dict2.get(key)
+            if value2 is not None:
+                dict1[key] = value2
+    return dict1
+    
+def composite_attr_dict(attrib, *dicts):
+    return reduce(merge_dicts, (dict(attrib),) + dicts)
+
+class Element(translation.Element):
+    """The XSS template language base element."""
+    
+    class node(translation.Node):
+        define_symbol = '_define'
+        composite_attr_symbol = '_composite_attr'
+        
+        @property
+        def omit(self):
+            return self.element.xss_omit.lower() in true_values
+
+        @property
+        def dict_attributes(self):
+            if self.element.xss_attributes is not None:
+                xhtml_attributes = utils.get_attributes_from_namespace(
+                    self.element, config.XHTML_NS)
+                attrib = repr(tuple(xhtml_attributes.items()))
+
+                names = self.element.xss_attributes.split(',')
+                attributes = ", ".join(
+                    ["attributes.get('%s')" % name.strip() for name in names])
+                
+                value = types.value(
+                    "%s(%s, %s)" % \
+                    (self.composite_attr_symbol, attrib, attributes))
+                value.symbol_mapping[self.composite_attr_symbol] = composite_attr_dict
+                return value
+            
+        @property
+        def define(self):
+            content = self.element.xss_content
+            if content is not None:
+                expression = types.value(
+                    "content.get('%s')" % content)
+                return types.definitions((
+                    (types.declaration((self.define_symbol,)), types.parts((expression,))),))
+            
+        @property
+        def content(self):
+            content = self.element.xss_content
+            if content is not None:
+                expression = types.value(
+                    "%s or %s" % (self.define_symbol, repr(self.element.text)))
+
+                if self.element.xss_structure.lower() not in true_values:
+                    expression = types.escape((expression,))
+
+                return expression
+
+        @property
+        def skip(self):
+            if self.element.xss_content is not None:
+                return types.value(self.define_symbol)
+                
+    node = property(node)
+
+    xss_omit = utils.attribute(
+        '{http://namespaces.repoze.org/xss}omit', default="")
+    
+    xss_content = utils.attribute(
+        '{http://namespaces.repoze.org/xss}content')
+
+    xss_structure = utils.attribute(
+        '{http://namespaces.repoze.org/xss}structure', default="")
+
+    xss_attributes = utils.attribute(
+        '{http://namespaces.repoze.org/xss}attributes')
+
+class XSSTemplateParser(etree.Parser):
+    """XSS template parser."""
+    
+    element_mapping = {
+        config.XHTML_NS: {None: Element},
+        config.META_NS: {None: translation.MetaElement}}
+
+class DynamicHTMLParser(XSSTemplateParser):    
+    def __init__(self, filename):
+        self.path = os.path.dirname(filename)
+        
+    def parse(self, body):
+        root, doctype = super(DynamicHTMLParser, self).parse(body)
+
+        # locate XSS links
+        links = root.xpath(
+            './/xmlns:link[@rel="xss"]', namespaces={'xmlns': config.XHTML_NS})
+        for link in links:
+            try:
+                href = link.attrib['href']
+            except KeyError:
+                raise AttributeError(
+                    "Attribute missing from tag: 'href' (line %d)." % link.sourceline)
+
+            filename = os.path.join(self.path, href)
+            if not os.path.exists(filename):
+                raise ValueError(
+                    "File not found: %s" % repr(href))
+
+            rules = parse_xss(filename)
+
+            for rule in rules:
+                selector = lxml.cssselect.CSSSelector(rule.selector)
+                for element in root.xpath(
+                    selector.path, namespaces=rule.namespaces):
+                    if rule.name:
+                        element.attrib[
+                            '{http://namespaces.repoze.org/xss}content'] = \
+                            rule.name
+                    if rule.structure:
+                        element.attrib[
+                            '{http://namespaces.repoze.org/xss}structure'] = \
+                            rule.structure
+                    if rule.attributes:
+                        element.attrib[
+                            '{http://namespaces.repoze.org/xss}attributes'] = \
+                            rule.attributes
+                        
+            link.getparent().remove(link)
+            
+        return root, doctype

Added: Sandbox/malthe/chameleon.html/src/chameleon/html/language.txt
===================================================================
--- Sandbox/malthe/chameleon.html/src/chameleon/html/language.txt	                        (rev 0)
+++ Sandbox/malthe/chameleon.html/src/chameleon/html/language.txt	2008-09-25 21:16:36 UTC (rev 91489)
@@ -0,0 +1,114 @@
+XSS template language
+=====================
+
+This language is used internally by the template compiler.
+
+  >>> print render_xss("""\
+  ... <html xmlns="http://www.w3.org/1999/xhtml"
+  ...       xmlns:xss="http://namespaces.repoze.org/xss">
+  ...   <div xss:content="document-content">
+  ...      <span>Default content</span>
+  ...   </div>
+  ... </html>""", content={'document-content': u"Dynamic content"})
+  <html>
+    <div>Dynamic content</div>
+  </html>
+
+If we omit the slot from the passed content argument, the element is
+left untouched.
+  
+  >>> print render_xss("""\
+  ... <html xmlns="http://www.w3.org/1999/xhtml"
+  ...       xmlns:xss="http://namespaces.repoze.org/xss">
+  ...   <div xss:content="document-content">
+  ...      <span>Default content</span>
+  ...   </div>
+  ... </html>""", content={})
+  <html>
+    <div>
+       <span>Default content</span>
+    </div>
+  </html>
+
+If the xss:omit attribute is set, the tag will be replaced by the dynamic content.
+  
+  >>> print render_xss("""\
+  ... <html xmlns="http://www.w3.org/1999/xhtml"
+  ...       xmlns:xss="http://namespaces.repoze.org/xss">
+  ...   <div xss:content="document-content" xss:omit="true">
+  ...      <span>Default content</span>
+  ...   </div>
+  ... </html>""", content={'document-content': u"Dynamic content"})
+  <html>
+    Dynamic content
+  </html>
+
+When the xss:structure attribute is set, the dynamic content is not
+escaped.
+  
+  >>> print render_xss("""\
+  ... <html xmlns="http://www.w3.org/1999/xhtml"
+  ...       xmlns:xss="http://namespaces.repoze.org/xss">
+  ...   <div xss:content="document-content" xss:structure="true">
+  ...      <span>Default content</span>
+  ...   </div>
+  ... </html>""", content={'document-content': u"<span>Dynamic content</span>"})
+  <html>
+    <div><span>Dynamic content</span></div>
+  </html>
+
+Attributes may be dynamically applied to elements using the
+xss:attributes.
+
+  >>> print render_xss("""\
+  ... <html xmlns="http://www.w3.org/1999/xhtml"
+  ...       xmlns:xss="http://namespaces.repoze.org/xss">
+  ...   <div xss:attributes="example">
+  ...      Content
+  ...   </div>
+  ... </html>""", attributes={'example': {'class': 'example-class'}})
+  <html>
+    <div class="example-class">
+       Content
+    </div>
+  </html>
+
+As a special case, if "class" is a dynamic attribute, it will not
+override an existing "class" attribute on the element, but extend it
+(concatenate with a space character).
+  
+  >>> print render_xss("""\
+  ... <html xmlns="http://www.w3.org/1999/xhtml"
+  ...       xmlns:xss="http://namespaces.repoze.org/xss">
+  ...   <div id="existing" class="existing-class" xss:attributes="example">
+  ...      Content
+  ...   </div>
+  ...   <span xss:attributes="example">
+  ...      Content
+  ...   </span>
+  ... </html>""", attributes={'example': {'id': 'new', 'class': 'additional-class'}})
+  <html>
+    <div id="new" class="existing-class additional-class">
+       Content
+    </div>
+    <span id="new" class="additional-class">
+       Content
+    </span>
+  </html>
+
+We can supply multiple dynamic attribute identifiers, separated by a
+comma.
+  
+  >>> print render_xss("""\
+  ... <html xmlns="http://www.w3.org/1999/xhtml"
+  ...       xmlns:xss="http://namespaces.repoze.org/xss">
+  ...   <div id="existing" class="existing-class" xss:attributes="example1, example2">
+  ...      Content
+  ...   </div>
+  ... </html>""", attributes={'example1': {'class': 'foo', 'style': 'bar'},
+  ...                         'example2': {'class': 'bar', 'style': 'foo'}})
+  <html>
+    <div style="foo" id="existing" class="existing-class foo bar">
+       Content
+    </div>
+  </html>

Added: Sandbox/malthe/chameleon.html/src/chameleon/html/template.py
===================================================================
--- Sandbox/malthe/chameleon.html/src/chameleon/html/template.py	                        (rev 0)
+++ Sandbox/malthe/chameleon.html/src/chameleon/html/template.py	2008-09-25 21:16:36 UTC (rev 91489)
@@ -0,0 +1,9 @@
+from chameleon.core import template
+
+import language
+
+class DynamicHTMLFile(template.TemplateFile):
+    def __init__(self, filename, **kwargs):
+        parser = language.DynamicHTMLParser(filename)
+        super(DynamicHTMLFile, self).__init__(
+            filename, parser, **kwargs)

Added: Sandbox/malthe/chameleon.html/src/chameleon/html/template.txt
===================================================================
--- Sandbox/malthe/chameleon.html/src/chameleon/html/template.txt	                        (rev 0)
+++ Sandbox/malthe/chameleon.html/src/chameleon/html/template.txt	2008-09-25 21:16:36 UTC (rev 91489)
@@ -0,0 +1,23 @@
+Dynamic HTML template
+=====================
+
+  >>> from chameleon.html.template import DynamicHTMLFile
+
+  >>> template = DynamicHTMLFile(os.path.join(path, 'layouts', 'example.html'))
+  >>> print template(
+  ...     content={'title': u"Document title",
+  ...              'content': u"Document content"},
+  ...     attributes={'meta-generator': {'content': u"Chameleon"},
+  ...                 'meta-description': {'content': u"Document description"},
+  ...                 'meta-keywords': {'content': u"Keywords"}})
+  <html>
+    <head>
+      <title>Document title</title>
+      <meta content="Chameleon" name="generator" />
+      <meta content="Document description" name="description" />
+      <meta content="Keywords" name="keywords" />
+    </head>
+    <body>
+      <div>Document content</div>
+    </body>
+  </html>

Added: Sandbox/malthe/chameleon.html/src/chameleon/html/tests/__init__.py
===================================================================

Added: Sandbox/malthe/chameleon.html/src/chameleon/html/tests/layouts/example.html
===================================================================
--- Sandbox/malthe/chameleon.html/src/chameleon/html/tests/layouts/example.html	                        (rev 0)
+++ Sandbox/malthe/chameleon.html/src/chameleon/html/tests/layouts/example.html	2008-09-25 21:16:36 UTC (rev 91489)
@@ -0,0 +1,14 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title>Example layout</title>
+    <link rel="xss" type="text/xss" href="example.xss" />
+    <meta name="generator" content="Emacs" />
+    <meta name="description" content="This is an example layout." />
+    <meta name="keywords" content="example, layout, xss." />
+  </head>
+  <body>
+    <div id="content">
+      This is the content area.
+    </div>
+  </body>
+</html>

Added: Sandbox/malthe/chameleon.html/src/chameleon/html/tests/layouts/example.xss
===================================================================
--- Sandbox/malthe/chameleon.html/src/chameleon/html/tests/layouts/example.xss	                        (rev 0)
+++ Sandbox/malthe/chameleon.html/src/chameleon/html/tests/layouts/example.xss	2008-09-25 21:16:36 UTC (rev 91489)
@@ -0,0 +1,24 @@
+html > head title {
+  name: title;
+}
+
+html > head meta[name="generator"] {
+  attributes: meta-generator;
+}
+
+html > head meta[name="description"] {
+  attributes: meta-description;
+}
+
+html > head meta[name="keywords"] {
+  attributes: meta-keywords;
+}
+
+div#content {
+  name: content;
+  structure: true;
+}
+
+body {
+  attributes: section, ltr;
+}
\ No newline at end of file

Added: Sandbox/malthe/chameleon.html/src/chameleon/html/tests/test_doctests.py
===================================================================
--- Sandbox/malthe/chameleon.html/src/chameleon/html/tests/test_doctests.py	                        (rev 0)
+++ Sandbox/malthe/chameleon.html/src/chameleon/html/tests/test_doctests.py	2008-09-25 21:16:36 UTC (rev 91489)
@@ -0,0 +1,47 @@
+import unittest
+import os
+
+import zope.testing
+import zope.component.testing
+import zope.configuration.xmlconfig
+
+import chameleon.core.config
+import chameleon.core.testing
+
+import chameleon.html
+import chameleon.html.tests
+import chameleon.html.language
+
+OPTIONFLAGS = (zope.testing.doctest.ELLIPSIS |
+               zope.testing.doctest.NORMALIZE_WHITESPACE)
+
+def render_xss(body, **kwargs):
+    parser = chameleon.html.language.XSSTemplateParser()
+    return chameleon.core.testing.compile_template(parser, body, **kwargs)
+
+def setUp(suite):
+    zope.component.testing.setUp(suite)
+
+def test_suite():
+    filesuites = 'language.txt', 'template.txt'
+    testsuites = ()
+    
+    globs = dict(render_xss=render_xss, os=os, path=chameleon.html.tests.__path__[0])
+    
+    chameleon.core.config.DISK_CACHE = False
+    
+    return unittest.TestSuite(
+        [zope.testing.doctest.DocTestSuite(
+        "chameleon.html."+doctest, optionflags=OPTIONFLAGS,
+        setUp=setUp, tearDown=zope.component.testing.tearDown) \
+         for doctest in testsuites] + 
+        
+        [zope.testing.doctest.DocFileSuite(
+        doctest, optionflags=OPTIONFLAGS,
+        globs=globs,
+        setUp=setUp, tearDown=zope.component.testing.tearDown,
+        package="chameleon.html") for doctest in filesuites]
+        )
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')

Added: Sandbox/malthe/chameleon.html/src/chameleon/html/xss.py
===================================================================
--- Sandbox/malthe/chameleon.html/src/chameleon/html/xss.py	                        (rev 0)
+++ Sandbox/malthe/chameleon.html/src/chameleon/html/xss.py	2008-09-25 21:16:36 UTC (rev 91489)
@@ -0,0 +1,44 @@
+from cssutils.parse import CSSParser
+import chameleon.core.config
+
+class Element(object):
+    def __init__(self, selector, namespaces, name="",
+                 attributes=None, structure=False, **kwargs):
+        if kwargs:
+            for name in kwargs:
+                raise ValueError("Unknown property: " % name)
+        self.namespaces = namespaces
+        self.selector = selector
+        self.name = name
+        self.attributes = attributes
+        self.structure = structure
+
+namespace = 'xmlns'
+namespaces = {namespace: chameleon.core.config.XHTML_NS}
+
+def parse_xss(stream):
+    elements = []
+
+    parser = CSSParser(loglevel=0)
+    for rule in parser.parseFile(stream):
+        properties = {}
+        for prop in rule.style:
+            properties[str(prop.name)] = prop.value
+
+        for selector in rule.selectorList:
+            selectors = []
+            for item in selector.seq:
+                if item.type == 'type-selector':
+                    extra, name = item.value
+                    selectors.append('%s|%s' % (namespace, name))
+                else:
+                    selectors.append(item.value)
+            selector = "".join(selectors)
+            element = Element(
+                selector,
+                namespaces,
+                **properties)
+
+        elements.append(element)
+
+    return elements



More information about the Checkins mailing list