Edgewall Software

WorkInProgress/PluggableDirectivesLibraries

Version 14 (modified by Carsten Klein <carsten.klein@…>, 5 years ago)

--

Pluggable Directives Libraries (#395)

Goals

  • provide for a framework for introducing additional directives into the system, even from different namespace
  • TODO existing directives may be overridden, i.e. alternate versions will simply replace existing directives from a different library
    • might require some configuration by the user for choosing the correct implementation, if multiple such replacements are available
  • zero configuration effort by the user
    • detection of available directives libraries is via the [genshi.libraries.directives] entry point
    • available directives libraries in the system will be automatically loaded by the system
  • api for programmatically adding new directives libraries (similar to the current concept of pluggable DirectiveFactory's, which will be dropped once this is stable)
  • extended text based templates, see WorkInProgress/PluggableDirectivesLibraries/TextTemplates
  • execution order of directives must be redefined so that directives defined by third party libraries will be executed in the correct order, and if a given directive does not impose any restrictions on its execution order, then it will be executed in document order
    • this allows for adding new directives to existing directive namespaces
    • this seems more natural since we are not dealing with operators here and as it is also found in common programming languages where the in-source order of statements is the normally the same order that they are executed in
  • refactoring of the i18n filter into a filter module and a directives library (DONE, untested)
  • refactoring of the standard directives into a base module and a directives library (DONE, tested, text template will fail since it is currently under development)

Current Development State

  • currently in early alpha phase (existing test cases do not break and performance is comparable to standard genshi ~ 100..200..400 ms for rendering the trac templates incl. a site.html on my local system)

More information will be made available as soon as the initial prototype is working.

Wishlist

Feel free to add your wishes for this feature. Please do not remove existing wishes from other users, extend upon them.

  • see #296 for a py:element directive
  • see #321 for a py:comment directive
  • see #104 for a proposal on CDATA output (which could be made a directive)
  • working branch to develop this in a joint effort on g.e.o. directly

Example Directives Library

This example serves as a skeleton for ongoing development. It also serves as the initial documentation for the new feature.

Example setup.py

try:
    from setuptools import setup, Feature
    from setuptools.command.bdist_egg import bdist_egg
except ImportError:
    from distutils.core import setup
    Feature = None
    bdist_egg = None

setup(
    name = 'GenshiSampleDirectivesLibrary',
    version = '1.0.0proto_proto',
    description = 'A sample genshi directives library',
    long_description = \
"""""",
    author = 'axn-software.de, Carsten Klein',
    author_email = 'info@axn-software.de',
    license = 'BSD',
    url = 'http://genshi.edgewall.org/wiki/WorkInProgress/PluggableDirectivesLibraries',
    #download_url = 'http://genshi.edgewall.org/wiki/Download',

    classifiers = [
        'Development Status :: 2 - Prototype',
        'Environment :: Web Environment',
        'Intended Audience :: Developers',
        'License :: OSI Approved :: BSD License',
        'Operating System :: OS Independent',
        'Programming Language :: Python',
        'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
        'Topic :: Software Development :: Libraries :: Python Modules',
        'Topic :: Text Processing :: Markup :: HTML',
        'Topic :: Text Processing :: Markup :: XML'
    ],
    keywords = ['python.templating.engines'],
    packages = ['sample'],
    #test_suite = 'genshi.tests.suite',

    entry_points = """
    [genshi.libraries.directives]
    genshi-sample = sample.directives_library:SampleDirectivesLibrary
    """
)

Example lib.py

from genshi.core import Markup, TEXT
from genshi.template.base import TemplateSyntaxError, _apply_directives
from genshi.template.default_directives_library import WhenDirective
from genshi.template.directives import Directive, DirectivesLibrary


NAMESPACE = "http://genshi.edgewall.org/directives/sample/"


class SampleDirectivesLibrary(DirectivesLibrary):

    def __init__(self):
        DirectivesLibrary.__init__(self, 
                                   [CommentDirective,
                                    #ElementDirective
                                    #CDATADirective
                                   ],
                                   default_namespace = NAMESPACE)


# taken from ticket #321 comment directive patch
class CommentDirective(Directive): 
        """Implementation of the ``sample:comment`` template directive. 
         
        This directive replaces the content of the element with the result of 
        evaluating the value of the ``sample:comment`` attribute: 
         
        >>> from genshi.template import MarkupTemplate 
        >>> tmpl = MarkupTemplate('''<div xmlns:sample="http://genshi.edgewall.org/directives/sample"> 
        ...   <sample:comment>${2 + 2}</sample:comment> 
        ... </div>''') 
        >>> print tmpl.generate(bar='Bye') 
        <div> 
          <!--4--> 
        </div> 
        """ 
        __slots__ = [] 

        @classmethod 
        def attach(cls, template, stream, value, namespaces, pos): 
            if type(value) is not dict: 
                raise TemplateSyntaxError('The comment directive can only be used ' 
                                          'as an element', template.filepath, 
                                          *pos[1:]) 
            return super(CommentDirective, cls).attach(template, stream, 
                                                       value, namespaces, pos) 

        def __call__(self, stream, directives, ctxt, **vars): 
            stream = list(stream) 
            
            stream.insert(0, (TEXT, Markup("<!--"), (None, 1, 0))) 
            stream.append((TEXT, Markup("-->"), (None, 1, 0))) 
            
            return _apply_directives(stream, directives, ctxt, vars)

        @classmethod
        def get_execute_before(cls):
            return WhenDirective