base.py :  » Template-Engines » webstring » webstring-0.5 » webstring » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Template Engines » webstring 
webstring » webstring 0.5 » webstring » base.py
# Copyright (c) 2006 L. C. Rees.  All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1.  Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 2.  Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3.  Neither the name of the Portable Site Information Project nor the names
# of its contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

'''Base webstring Template classes.'''

import string
from keyword import kwlist

__all__ = ['_Template', '_Group', '_Field', '_checkname']

# Exceptions
_exceptions = ['maximum allowed repetitions exceeded',
    'invalid Template source',
    'invalid type for formatting',
    'not all arguments converted during formatting',
    'not enough arguments for format', '', '',
    'invalid inline template source',
    'delimiter "$" or "%" not found',
    'invalid Template filter type']
_Stemplate = string.Template
# Illegal characters for Python names
_ichar = '()[]{}@,:.`=;+-*/%&|^><\'"#\\$?!~'
# Reserve webstring specific words
kwlist.extend(['append', 'atemplates', 'current', 'default', 'exclude',
    'fromfile', 'fromstring', 'groupmark', 'include', 'mark', 'max', 'pipe',
    'purge', 'render', 'repeat', 'reset', 'template', 'templates', 'text',
    'update', 'write'])
_reserve, _keywords = string.maketrans('', ''), frozenset(kwlist)

def _checkname(name):
    '''Ensures a string is a legal Python name.'''
    # Remove characters that are illegal in a Python name
    if '}' not in name:
        name = name.replace('.', '_').translate(_reserve, _ichar)
    # Handle XML namespaced names
    else:
        name = name.split('}')[1].replace('.', '_').translate(_reserve, _ichar)
    # Add _ if value is a Python keyword
    if name in _keywords: return ''.join([name, '_'])
    return name
    

class _Base(object):

    '''Template base class.'''

    def __init__(self, auto, **kw):
        # Controls if templates become object attrs of a root/group Template
        self._auto = auto

    def __repr__(self):
        '''String representation of a Template.'''
        return '<Template "%s" at %x>' % (self.__name__, id(self))

    def __str__(self):
        '''String output of a Template.'''
        return self.render()

    def __add__(self, data):
        '''Inserts data or another Template's fields after the internal
        template and returns a modified copy of the Template.
        '''
        self.__iadd__(data)
        newself = self.current
        self.reset()
        return newself

    def __radd__(self, data):
        '''__add__ from the right.'''
        return self.__add__(data)
   
    def __mul__(self, num):
        '''Inserts a copy of the internal field after the internal field 
        "num" number of times and returns a modified copy of the Template.
        '''
        self.__imul__(num)
        newself = self.current
        self.reset()
        return newself

    def __rmul__(self, num):
        '''__mul__ from the right.'''
        return self.__mul__(num)
        
    def __imul__(self, num):
        '''Inserts a copy of the internal field after the internal field 
        "num" number of times and the Template (self) is returned modified.
        '''
        # Ensure "num" is not greater than maximum allowed repetitions
        if num <= self.max:            
            tmp = self.current
            # Concatenate "number" number of copies of self to self
            for number in xrange(num-1): self.__iadd__(tmp.current)
            return self
        raise TypeError(_exceptions[0])

    def __mod__(self, data):
        '''Substitutes text data into the internal template and returns a
        modified copy of the Template.
        '''
        self.__imod__(data)
        newself = self.current
        self.reset()
        return newself
               
    def __pow__(self, data):
        '''For each item in a tuple, the internal template is copied, the
        item is substituted into the copy's template, and the copy is inserted
        after the internal template. Finally, a modified copy of the Template
        is returned.
        '''
        self.__ipow__(data)
        newself = self.current
        self.reset()
        return newself

    def __ipow__(self, data):
        '''For each item in a tuple, the internal template is copied, the 
        content of the item is substituted into the copy's template, and
        the copy is inserted after the internal template. Finally, the modified
        Template (self) is returned.
        '''
        if len(data) <= self.max:
            if isinstance(data, tuple):
                # Put data in correct order
                data = list(reversed(data))
            else:
                raise TypeError('invalid type for formatting')
            # Substitute content into existing field
            self.__imod__(data.pop())
            # Concatenate a new field with self for each item
            while data: self.repeat(data.pop())
            return self                
        # Raise exception if data length > maximum allowed repetitions
        raise TypeError(_exceptions[0])

    def _setmark(self, mark):
        '''Sets template variable delimiter.'''
        self._mark = mark

    def pipe(self, info=None, format='xml', encoding='utf-8'):
        '''Returns the string output of the internal template and
        resets the Template.

        @param info Data to substitute into a template (default: None)
        @param format Document format (default:'xml')
        @param encoding Encoding of return string (default: 'utf-8')
        '''
        output = self.render(info, format, encoding)
        self.reset()
        return output

    def repeat(self, data=None):
        '''Copies the original state of the internal template, inserts it after
        the interal template, and, optionally, substitutes data into it.

        @data data Data to substitute into a template (default: None)
        '''
        if data is not None:
            self.__iadd__(self.default.__imod__(data))
        else:
            self.__iadd__(self.default)
            
    def write(self, path, info=None, format='xml', encoding='utf-8'):
        '''Writes a Template's string output to a file.

        @param path Output file path
        @param info Data to substitute into a template (default: None)
        @param format Document format (default:'xml')
        @param encoding Encoding of output string (default: 'utf-8')
        '''
        open(path, 'wb').write(self.render(info, format, encoding))


class _Many(_Base):

    '''Base class for Templates with subtemplates (groups or fields).'''

    def __init__(self, auto, omax, **kw):
        super(_Many, self).__init__(auto, **kw)
        # Sets maximum allowed repetitions of a Template
        self._max = omax
        # Internal tracking structures
        self._fielddict, self._fields, self._filter = dict(), list(), set()

    def __imod__(self, data):
        '''Substitutes text data into each field's template and the
        modified Template (self) is returned.
        '''
        # Get any templates
        try:
            self.templates(data.pop('templates'))
        except (AttributeError, KeyError): pass
        # Get any substitutions
        try:
            self.__ipow__(data.pop('subs'))
        except (AttributeError, KeyError): pass
        # Cache field and data length
        lself, length = len(self._fields), len(data)
        # If number of fields == number of items in data...
        if length == lself:
            if isinstance(data, dict):                
                for key, value in data.iteritems():
                    # If tuple, expand it
                    try:
                        self._fielddict[key].__ipow__(value)
                    # If dictionary, substitute it
                    except TypeError:
                        self._fielddict[key].__imod__(value) 
            elif isinstance(data, tuple):
                # Iterate with index and item through data
                for key, item in enumerate(data):
                    # If item is a tuple, expand it
                    try:
                        self._fields[key].__ipow__(item)
                    # If dictionary, substitute it
                    except TypeError:
                        self._fields[key].__imod__(item)
            else:
                raise TypeError(_exceptions[2])
        # Return self if no more items in data
        elif length == 0:
            return self
        # Raise exception if too many items to match all fields
        elif length > lself:
            raise TypeError(_exceptions[3])
        # Raise exception if too few items to match all fields
        elif length < lself:
            raise TypeError(_exceptions[4])        
        return self 

    def __getitem__(self, key):
        '''Gets a field by position or keyword.'''
        # Try getting field by position from list
        try:
            return self._fields[key]
        # Try getting field by keyword from dictionary
        except TypeError:
            return self._fielddict[key]
    
    def __setitem__(self, key, value):
        '''Stub'''
        
    def __delitem__(self, key):
        '''Deletes a field.'''
        # Handle positional indexes
        try:
            # Get field
            obj = self._fields[key] 
            # Get field name
            for name, element in self._fielddict.iteritems():
                if element == obj: break
        # Handle keys
        except TypeError:
            name = key
        # Delete object attribute
        self.__delattr__(name)

    def __contains__(self, key):
        '''Tells if a field of a given name is in a Template.'''
        return key in self._fielddict        

    def __len__(self):
        '''Gets the number of fields in a Template.'''
        return len(self._fields)    

    def __iter__(self):
        '''Iterator for the internal field list.'''
        return iter(self._fields)

    def _setfield(self, key, node):
        '''Sets a new field.'''
        self._fields.append(node)        
        self._fielddict[key] = node
        # Make field attribute of self if automagic on
        if self._auto: setattr(self, key, node)

    def _setmark(self, mark):
        '''Sets the variable delimiter for all subtemplates in a Template.'''
        super(_Many, self)._setmark(mark)
        # Set variable delimiter on all children
        for field in self._fields: field.mark = mark          

    def _setgmark(self, mark):
        '''Sets group delimiter.'''
        self._groupmark = mark

    def _setmax(self, omax):
        '''Sets the maximum repetition value for all Templates.'''
        # Set group or root to max
        self._max = omax
        # Set max on all children
        for field in self._fields: field.max = omax            
    
    # Property for setting a maximum repetition value    
    max = property(lambda self: self._max, _setmax)    
    

class _Field(_Base):

    '''Field base class.'''
    
    def __init__(self, auto, omax, **kw):
        super(_Field, self).__init__(auto, **kw)
        # Maximum repetition value and internal trackers
        self.max, self._siblings = omax, kw.get('siblings', list())
        self._tempfields = kw.get('tempfields', list())

    def __imod__(self, data):
        '''Substitutes text data into the internal element's text and
        attributes and returns this field (self) modified.
        '''
        if isinstance(data, basestring):
            self.text = data
        elif isinstance(data, dict):            
            # Try popping inline text content
            try:
                self.text = data.pop('text')
            except KeyError: pass
            # Try popping substitutions
            try:
                self.__ipow__(data.pop('sub'))                
            except KeyError: pass
        else:
            raise TypeError(_exceptions[2])
        return self
        

class _Group(_Many):

    '''Group base class.'''    

    def __init__(self, auto, omax, **kw):
        super(_Group, self).__init__(auto, omax, **kw)
        # Internal trackers
        self._siblings = kw.get('siblings', list())
        self._tempfields = kw.get('tempfields', list())


class _Template(_Many):

    '''Base class for root Templates.'''    

    def __init__(self, auto, omax, **kw):
        super(_Template, self).__init__(auto, omax, **kw)      

    def append(self, data):        
        '''Makes a string or another Template's internal template part of
        the Template's internal template.

        @param data Template or element
        '''
        self.__iadd__(data)   

    def exclude(self, *args):
        '''Excludes fields or groups from aTemplate. import 

        @param args Names of a field or group
        '''
        # Remove fields from root
        for arg in args:
            name = _checkname(arg)
            # Add to internal filter list
            self._filter.add(name)
            # Remove field if present
            self.__delitem__(name)
        # Remove fields from children
        for index, field in enumerate(self._fields):
            # Run only on groups
            if hasattr(field, 'groupmark'):
                for arg in args:                    
                    name = _checkname(arg)
                    # Remove subfield if present
                    field.__delitem__(name)                    
                # Remove empty groups
                if len(field) == 0: self.__delitem__(index)    

    def include(self, *args):
        '''Includes a field or group in a Template.

        @param args Names of fields or groups
        '''
        # Remove from internal tracker
        self._filter -= set(args)
        self.reset()
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.