Commits

Lynn Rees  committed 3c13756

[svn]

  • Participants
  • Parent commits 37ef31a
  • Branches webstring

Comments (0)

Files changed (2)

File branches/0.5/trunk/webstring/text.py

 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 # POSSIBILITY OF SUCH DAMAGE.
 
-'''Text template for webstring.'''
+'''Text template base classes.'''
 
 import re
 from copy import deepcopy
 
 __all__ = ['TextTemplate', 'WSGITextTemplate', 'texttemplate']
 
+# Default variable and group delimiter
 MARK = GROUPMARK = '$'
+# Variable and group discovery regex
 PATTERN = r'%s{2}(.+?)%s{2}|%s{1}(\w+?)%s{1}'
 
 def getpattern(mark=MARK, group=GROUPMARK):
+    '''Fetches a regex pattern set with the current delimiters for a Template.
+
+    @param mark Variable delimiter (default: MARK)
+    @param group Group delimiter (default: GROUPMARK)
+    '''
+    # Escape delimiters
     mark, group = re.escape(mark), re.escape(group)
-    marks = (group, group, mark, mark)    
+    # Arrange in tuple
+    marks = (group, group, mark, mark)
+    # Return compiled regex pattern
     return re.compile(PATTERN % marks, re.DOTALL|re.UNICODE)
 
+# Default regex pattern
 _match = getpattern()
 
 def texttemplate(source, **kw):
 
 class _NonRoot(object):
 
-    '''Base class for non-root templates.'''
+    '''Base class for non-root text Templates.'''
 
     def append(self, data):        
-        '''Makes an element or another Template's elements children of this
-        Template's internal element.
+        '''Appends a string or another Template's template to the
+        Template's internal template.
 
-        @param data Template or element
+        @param data Template or string
         '''
         return self.__iadd__(data)
 
 
     '''Base class for Fields.'''
 
+    # Default variable delimiter
     _mark = MARK
     
     def __init__(self, name, auto=True, omax=25, **kw):
-        '''Initialization method for fields.
-        
+        '''
         @param src Field name
         @param auto Turns automagic on and off (default: True)
         @param omax Maximum number of times a field can repeat (default: 25)
         '''
         super(_TextField, self).__init__(auto, omax, **kw)
         self.__name__ = name
+        # Set text to empty unicode string
         self.text = self._btext = u''
 
     def __iadd__(self, data):
         '''Inserts a string or another Template's strings after the internal
-        string and this Template is returned modified.
+        string and the Field (self) is returned modified.
         '''
         # Process strings
         if isinstance(data, basestring):
             self._siblings.append(data)
         # Process Templates
         elif hasattr(data, 'mark'):
+            # Append rendered string of other Templates
             self._siblings.append(data.render())
         else:
             raise TypeError(_exceptions[2])
         return self
 
     def render(self, info=None, format='text', encoding='utf-8'):
-        '''Returns a string version the internal element's parent.
+        '''Returns a string version of this Field.
 
-        @param info Data to substitute into a document (default: None)
-        @param format Document format (default:'text')
-        @param encoding Encoding type for return string (default: 'utf-8')
+        @param info Data to substitute into a template (default: None)
+        @param format Text format (default: 'text')
+        @param encoding Encoding of eturn string (default: 'utf-8')
         '''
         if info is not None: self.__imod__(info)
+        # Join internal text with any sibling text
         self.text = u''.join([self.text, u''.join(self._siblings)])
+        # Return with encoding
         return self.text.encode(encoding)   
 
     def reset(self, **kw):
         '''Returns a Template object to its default state.'''
-        # Re-initialize self
         self.__init__(self.__name__, self._auto, self.max)
 
     # Sets the delimiter for template variables
 
 class _TextMany(object):
 
-    '''Base class for group-like Templates.'''
+    '''Base class for text root and group Templates.'''
 
+    # Variable and group delimiters
     _mark, _groupmark = MARK, GROUPMARK
-    _btext = text = _template = None
     
     def __init__(self, auto, omax, **kw):
         super(_TextMany, self).__init__(auto, omax, **kw)
+        # Assign match object to internal reference
         self._match = _match
 
-    def __iadd__(self, data):
-        '''Inserts a string or another Template's strings after the internal
-        string and this Template is returned modified.
-        '''
-        # Process strings
-        if isinstance(data, basestring):
-            # Insert into sibling tracking list
-            self._template = u''.join([self._template, data])
-        # Process Templates
-        elif hasattr(data, 'mark'):
-            if hasattr(data, 'groupmark'):                
-                self._fields.extend(data._fields)
-                self._template = u''.join([self._template, data._template])
-            else:
-                self._fields.append(data)
-                self._template = u''.join([self._template, '%s'])
-        else:
-            raise TypeError(_exceptions[2])
-        return self        
-
     def __delattr__(self, attr):
         '''Delete a _TextMany attribute.'''
         try:
             try:
                 # Delete from internal field dictionary
                 obj = self._fielddict.pop(attr)
+                # Get object's index
+                index = self._fields.index(obj)
                 # Remove from internal field list
-                index = self._fields.index(obj)
-                self._fields.remove(obj)
-                splits, cnt = self._template.split('%s'), 0    
+                self._fields.remove(obj)                
+                splits, cnt = self._template.split('%s'), 0
+                # Remove '%s' from template string
                 for idx, item in enumerate(splits):
-                    if item.rstrip() == u'':                      
+                    # Find a split point
+                    if item.rstrip() == u'':
+                        # Find the corresponding split point
                         if cnt == index:
+                            # Remove the split point and break
                             del splits[idx]
                             break
+                        # Increment separate counter
                         cnt += 1
+                # Recreate internal template
                 self._template = '%s'.join(splits)
             except KeyError: pass
-        # Always delete object attribute
+        # Always delete object attribute if set
         finally:
             if hasattr(self, attr): object.__delattr__(self, attr)         
 
     def _addfield(self, name):
-        '''Gets a field from an element.'''
+        '''Adds a field from an element.'''
         # Check if processed already
         if name not in self._filter:
             # Add to filter list if unprocessed
             self._setfield(name, _TextField(name, self._auto, self._max))            
 
     def render(self, info=None, format='text', encoding='utf-8'):
-        '''Returns a string version the internal element's parent.
+        '''Returns the string version of the internal template's current state.
 
-        @param info Data to substitute into a document (default: None)
+        @param info Data to substitute into internal template (default: None)
         @param format Format of document (default:'text')
-        @param encoding Encoding type for return string (default: 'utf-8')        
+        @param encoding Encoding type for output string (default: 'utf-8')        
         '''
         if info is not None: self.__imod__(info)
+        # Render internal fields and store in tuple
         content = tuple(i.render(None, format, encoding) for i in self._fields)
+        # Interpolate into template
         self._text = self._template % content
+        # Output w/ correct encoding
         return self._text.encode(encoding)            
 
     def reset(self, **kw):
         '''Returns a Template object to its default state.'''
-        # Re-initialize self
         self.__init__(self._btext, self._auto, self._max)   
     
 
 class _TextGroup(_TextMany, _Group, _NonRoot):
 
-    '''Class for group Templates.'''
+    '''Class for text group Templates.'''
 
     def __init__(self, src=None, auto=True, omax=25, **kw):
-        '''Initialization method for a group Template
-        
+        '''
         @param src Template string (default: None)
         @param auto Turns automagic on and off (default: True)
         @param omax Maximum number of times a group can repeat (default: 25)
         '''
         super(_TextGroup, self).__init__(auto, omax, **kw)
+        # Internal temp field tracker and temp template
         self._tempfields, self._ttemplate = list(), ''
         if src is not None: self._settemplate(src)
 
     def __iadd__(self, data):
         '''Inserts a string or another Template's strings after the internal
-        string and this Template is returned modified.
+        string and the Template (self) is returned modified.
         '''
         # Process strings
         if isinstance(data, basestring):
-            # Insert into sibling tracking list
+            # Append string onto internal template
             self._template = u''.join([self._template, data])
         # Process Templates
         elif hasattr(data, 'mark'):
-            if hasattr(data, 'groupmark'):                
+            if hasattr(data, 'groupmark'):
+                # Add group-like Template fields to _tempfield tracker
                 self._tempfields.extend(data._fields)
+                # Add group-like Template's template to temporary template
                 self._ttemplate = u''.join([self._ttemplate, data._template])
             else:
+                # Add fields to _tempfield tracker
                 self._tempfields.append(data)
+                # Add delimiter onto temp template
                 self._ttemplate = u''.join([self._ttemplate, '%s'])
         else:
             raise TypeError(_exceptions[2])
-        return self               
+        return self
+
+    def __deepcopy__(self, memo):
+        # Python 2.4 deepcopy copies regexes while Python 2.5 does not
+        idict = self.__dict__
+        # Remove _match regex if present
+        try:
+            match = idict.pop('_match')
+        # Use global if object does not have _match attribute
+        except KeyError:
+            match = _match
+        # Deep copy original object's __dict__
+        ndict = deepcopy(idict)
+        # Re-add _match object
+        ndict['_match'] = match
+        # Create blank group Template
+        cls = _TextGroup()
+        # Update with self's dictionary
+        cls.__dict__.update(ndict)
+        return cls
 
     def _changematch(self):
-        '''Changes the *mark regex pattern.'''
+        '''Changes the delimiter regex pattern.'''
         self._match = getpattern(self._mark, self._groupmark)
+        # Change delimiter on children
         for field in self._fields:
             if hasattr(field, 'groupmark'): field._match = self._match
 
         self._changematch()
 
     def _settemplate(self, instr):
+        '''Sets the internal group template.'''
+        # Iterate over any found fields
         for mo in self._match.finditer(instr):
             first, second = mo.groups()
+            # Extract field if found
             if second is not None: self._addfield(second)
+        # Check if field is empty
         if self._fields:
+            # Create internal template
             self._template = self._match.sub('%s', instr)
+            # Create text stubs and backup text
             self._text, self._btext = u'', instr
 
     def render(self, info=None, format='text', encoding='utf-8'):
-        '''Returns a string version the internal element's parent.
+        '''Returns the string version of the internal template's current state.
 
-        @param info Data to substitute into a document (default: None)
+        @param info Data to substitute into internal template (default: None)
         @param format Format of document (default:'text')
-        @param encoding Encoding type for return string (default: 'utf-8')        
+        @param encoding Encoding type for output string (default: 'utf-8')        
         '''
+        # Run superclass render
         text = super(_TextGroup, self).render(info, format, encoding)
+        # Render any tempfield siblings and store in tuple
         content = tuple(i.render(None, format, encoding) for i in self._tempfields)
+        # Join existing content with temporary content
         self._text = u''.join([text, self._ttemplate % content])
+        # Return with correct encoding
         return self._text.encode(encoding)  
 
     # Sets the delimiter for template variables
     '''Text root Template class.'''
 
     __name__ = 'root'
+    # Pattern to split group name from group template
     _groupbr = re.compile('(\w+)(\W.+)', re.DOTALL|re.UNICODE)
 
     def __init__(self, src=None, auto=True, omax=25, **kw):
         super(TextTemplate, self).__init__(auto, omax, **kw)
         # Check if source exists
         if src is not None:
-            # Try reading source from a file
+            # Try reading text source from a file
             try:
                 self.fromfile(src)
             except IOError:
                 try:
                     self.fromstring(src)
                 except SyntaxError:
-                    raise IOError(_exceptions[1]) 
+                    raise IOError(_exceptions[1])
+
+    def __iadd__(self, data):
+        '''Inserts a string or another Template's strings after the internal
+        string and the Template (self) is returned modified.
+        '''
+        # Process strings
+        if isinstance(data, basestring):
+            # Append string to internal template
+            self._template = u''.join([self._template, data])
+        # Process Templates
+        elif hasattr(data, 'mark'):
+            if hasattr(data, 'groupmark'):
+                # Extend internal fields with other Template's fields
+                self._fields.extend(data._fields)
+                # Append other Template's internal template
+                self._template = u''.join([self._template, data._template])
+            else:
+                # Append field to internal field list
+                self._fields.append(data)
+                # Append delimiter to internal template
+                self._template = u''.join([self._template, '%s'])
+        else:
+            raise TypeError(_exceptions[2])
+        return self
+
+    def __deepcopy__(self, memo):
+        '''Customizes deep copies w/ 'deepcopy'.'''
+        # Python 2.4 deepcopy copies regex objects while Python 2.5 does not
+        idict = self.__dict__
+        # Remove _match regex if present
+        try:
+            match = idict.pop('_match')
+        # Use global if object does not have _match attribute
+        except KeyError:
+            match = _match
+        # Deep copy original object's __dict__
+        ndict = deepcopy(idict)
+        # Re-add _match object
+        ndict['_match'] = match
+        # Create blank group Template
+        cls = _TextGroup()
+        # Update with deepcopied dictionary
+        cls.__dict__.update(ndict)
+        # Return copied class
+        return cls  
 
     def _addgroup(self, group):
         '''Creates group Templates.'''
+        # Separate group name and template
         realname, template = self._groupbr.match(group).groups()
         name = _checkname(realname)
         # Check if group already processed
             # Mark as processed
             self._filter.add(name)
             # Make new group Template w/o passing child
-            node = _TextGroup(template, self._auto, self._max)       
+            node = _TextGroup(template, self._auto, self._max)
+            # Name group
             node.__name__ = name
+            # Set field
             self._setfield(name, node)
 
-    def _changematch(self):
-        '''Changes the *mark regex pattern.'''
-        self._match = getpattern(self._mark, self._groupmark)
-        for field in self._fields:
-            if isinstance(field, _TextGroup): field._match = self._match 
-
     def _setgmark(self, mark):
         '''Sets the group delimiter for the Template and its children.'''
         super(TextTemplate, self)._setgmark(mark)
     def fromfile(self, path):
         '''Creates an internal element from a file source.
 
-        @param path String value of path to source
+        @param path Path to source
         '''
         self.fromstring(open(path, 'rb').read())
 
         '''Creates an internal template from a string source.
 
         @param instr String source
-        '''  
+        '''
+        # Extract fields, groups from source
         for mo in self._match.finditer(instr):
             first, second = mo.groups()
+            # Add groups
             if first is not None:
                 self._addgroup(first)
+            # Add fields
             elif second is not None:
                 self._addfield(second)
+        # Only initialize Template's w/ fields
         if self._fields:
+            # Create internal template
             self._template = self._match.sub(u'%s', instr)
+            # Create text stub and backup text
             self._text, self._btext = u'', instr
 
     # Sets the delimiter for template variables
 
 class WSGITextTemplate(WSGIBase):
 
-    '''WSGI middleware for using HTMLTemplate to render web content.'''
+    '''WSGI middleware for using TextTemplate to render web content.'''
 
     _format, _klass = 'text', TextTemplate    

File branches/0.5/trunk/webstring/wsgi.py

 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 # POSSIBILITY OF SUCH DAMAGE.
 
-'''WSGI middleware base class for webstring.'''
+'''WSGI middleware base class.'''
 
 __all__ = ['WSGIBase']
 
         @param application The WSGI application using this class must return a
         tuple or dictionary for its iterable and, at minimum, create an entry
         in the "environ" dictionary for a valid source template under the key
-        "webstring.source". Other entries can be set as needed. Simple example:
+        "webstring.source". Other entries can be set as needed.
+
+        Simple example:
 
         @template('template.html')
         def simple_app(environ, start_response):
         temps, eng = kw.get('templates'), kw.get('engine', 'etree')        
         self.encoding = kw.get('encoding', 'utf-8')
         self.format = kw.get('format', self._format)        
-        self.template = self._klass(source, auto, mx, engine=eng, templates=temps)        
+        self.template = self._klass(source, auto, mx, format=self._format, engine=eng, templates=temps)        
 
     def __call__(self, environ, start_response):
-        '''Runs the Template.'''
         result = self.application(environ, start_response)        
         return [str(self.template.render(result, self.format, self.encoding))]