Commits

Olemis Lang  committed af4fb42

TracGViz : Initial attempt to remove relative imports. Improved indentation

  • Participants
  • Parent commits 1a177fc
  • Branches providers

Comments (0)

Files changed (18)

File trac-dev/gviz/TODO

 Outstanding tasks
 -----------------
 
+X Migrate tracgviz.testing to a new package dutest-trac
+
+X Remove relative import statements
+
+- Error message for GViz content providers when guessed mimetype is still `None`
+
 ~ Write tests for data sources included in TracGViz 1.3.4 that are 
   not about version control.
 
 ~ Upgrade GViz providers for Trac=1.0 and Apache(TM) Bloodhound.
 
-- Migrate tracgviz.testing to a new package dutest-trac
-
 - Move `dutest` to `tests_require` rather than `install_requires`.
 
 - Typecasts in CSV to GViz MIME converter.

File trac-dev/gviz/tracgviz/__init__.py

             'GVizCSVEncoder', 'GVizHtmlEncoder'
 
 try:
-    from api import TracGVizSystem
-    from rpc import *
-    from stdhash import *
-    from proto import GViz_0_5
-    from stdfmt import GVizJsonEncoder, GVizHtmlEncoder, GVizCSVEncoder
-    from extfmt import *
-    from ticket import *
-    from wiki import *
-    from search import *
-    from timeline import *
-    from vcs import *
-    from xsv import GvizXSVConverter
-    from attachment import *
+    from tracgviz.api import TracGVizSystem
+    from tracgviz.rpc import *
+    from tracgviz.stdhash import *
+    from tracgviz.proto import GViz_0_5
+    from tracgviz.stdfmt import GVizJsonEncoder, GVizHtmlEncoder, GVizCSVEncoder
+    from tracgviz.extfmt import *
+    from tracgviz.ticket import *
+    from tracgviz.wiki import *
+    from tracgviz.search import *
+    from tracgviz.timeline import *
+    from tracgviz.vcs import *
+    from tracgviz.xsv import GvizXSVConverter
+    from tracgviz.attachment import *
 #    from ig import *
     msg = 'Ok'
 except Exception, exc:

File trac-dev/gviz/tracgviz/api.py

 try:
     import gviz_api
 except ImportError:
-    import _gviz_api as gviz_api
+    import tracgviz._gviz_api as gviz_api
 import sys
 from xmlrpclib import DateTime
 from datetime import datetime
 
 try:
-  from gvizql import prepare_ql_data
+    from tracgviz.gvizql import prepare_ql_data
 except ImportError :
-  def prepare_ql_data(provider, tq, req, **params):
-    r"""Fall back to this implementation if `Pygments` import fails.
-    Please read `gvizql.prepare_ql_data` docstrings for further details.
-    """
-    if not tq:
-      sch = provider.get_data_schema.im_func.func_code
-      sch_args = (sch.co_argcount > 1) and (req,) or ()
-      data = provider.get_data(req, None, **params)
-      return (provider.get_data_schema(*sch_args), data)
-    else:
-      raise GVizUnsupportedQueryOp("Unable to process query : " \
+    def prepare_ql_data(provider, tq, req, **params):
+        r"""Fall back to this implementation if `Pygments` import fails.
+        Please read `gvizql.prepare_ql_data` docstrings for further details.
+        """
+        if not tq:
+            sch = provider.get_data_schema.im_func.func_code
+            sch_args = (sch.co_argcount > 1) and (req,) or ()
+            data = provider.get_data(req, None, **params)
+            return (provider.get_data_schema(*sch_args), data)
+        else:
+            raise GVizUnsupportedQueryOp("Unable to process query : " \
                                     "Pygments is not installed ?")
 
 class GVizException(Exception):
-  r"""Base class for all exception types defined in this package"""
+    r"""Base class for all exception types defined in this package"""
 
 class GVizProviderNotFoundError(GVizException):
     r"""Exception raised to denote that there is no GViz provider able
     
     # Public API
     def get_formatted_data(self, req, path, **params):
-      r"""Simulate a request to the data source in the same context 
-      defined by another request.
-      
-      @param req          the "real" request.
-      @param path         a string containing all the information 
-                          included in the URI after `/gviz` prefix
-                          (e.g. `/ticket/milestone?filter_by=goal[12]`).
-      @param params       further custom arguments. They supersede 
-                          those present in `path` and should be 
-                          supported by the target data provider.
-      @return             the same data that'd be returned on 
-                          accessing the data provider at the requested 
-                          `path`.
-      @throws TracError   if an error is raised or a problem is detacted.
-      """
-      from util import RedirectIntercept, IoIntercept, StringIO
+        r"""Simulate a request to the data source in the same context 
+        defined by another request.
+        
+        @param req          the "real" request.
+        @param path         a string containing all the information 
+                            included in the URI after `/gviz` prefix
+                            (e.g. `/ticket/milestone?filter_by=goal[12]`).
+        @param params       further custom arguments. They supersede 
+                            those present in `path` and should be 
+                            supported by the target data provider.
+        @return             the same data that'd be returned on 
+                            accessing the data provider at the requested 
+                            `path`.
+        @throws TracError   if an error is raised or a problem is detacted.
+        """
+        from tracgviz.util import RedirectIntercept, IoIntercept, StringIO
 
-      
-      path = '/gviz' + path
-      self.log.debug("IG: Accessing data source `%s` directly", path)
-      strm = StringIO()
-      try:
-        reqi = IoIntercept(req, strm)
-        reqi = RedirectIntercept(reqi, self.env, **params)
+        path = '/gviz' + path
+        self.log.debug("IG: Accessing data source `%s` directly", path)
+        strm = StringIO()
         try:
-          reqi.redirect(path)
-        except RequestDone:
-          pass
-        val = strm.getvalue()
-        self.log.debug("IG: Returning data: %s", val)
-        return val
-      except TracError:
-        self.log.exception("IG: Error accessing %s directly", path)
-        raise
-      except Exception, exc:
-        self.log.exception("IG: Error accessing %s directly", path)
-        raise TracError("Error %s : %s" % (exc.__class__.__name__, \
-                            exc.message))
-      finally:
-        strm.close()
+            reqi = IoIntercept(req, strm)
+            reqi = RedirectIntercept(reqi, self.env, **params)
+            try:
+                reqi.redirect(path)
+            except RequestDone:
+                pass
+            val = strm.getvalue()
+            self.log.debug("IG: Returning data: %s", val)
+            return val
+        except TracError:
+            self.log.exception("IG: Error accessing %s directly", path)
+            raise
+        except Exception, exc:
+            self.log.exception("IG: Error accessing %s directly", path)
+            raise TracError("Error %s : %s" % (exc.__class__.__name__, \
+                                exc.message))
+        finally:
+            strm.close()
 
 class IGVizTableEncoder(Interface):
     r"""Implementing this interface is mandatory for every component

File trac-dev/gviz/tracgviz/attachment.py

 """
 __author__ = 'Olemis Lang'
 
+import codecs
+from itertools import chain, count
+
 from trac.core import Component, implements
 from trac.attachment import Attachment
 from trac.mimeview.api import Mimeview, content_to_unicode
 
-from api import gviz_col, gviz_param, IGVizDataProvider, GVizBadRequestError
-from util import GVizContentProvider
-
-from itertools import chain, count
+from tracgviz.api import gviz_col, gviz_param, IGVizDataProvider, \
+                         GVizBadRequestError
+from tracgviz.util import GVizContentProvider
 
 __all__ = 'GVizAttachmentProvider',
 
         r"""Retrieve a file object used to read attachment contents.
         """
         try:
-          realm, parent, fnm = (req.args[x] for x in ('realm', 'id', 'name'))
+            realm, parent, fnm = (req.args[x] for x in ('realm', 'id', 'name'))
         except KeyError:
-          raise GVizBadRequestError("Missing realm, resource ID or file name.")
+            raise GVizBadRequestError("Missing realm, resource ID or file name.")
         a = Attachment(self.env, realm, parent, fnm)
         req.perm(a.resource).require('ATTACHMENT_VIEW')
         fd = a.open()
         content, mimetype, rewinded = self.guess_mimetype(fd, fnm)
         if rewinded:
-          mimesys = Mimeview(self.env)
-          charset = mimesys.get_charset(content, mimetype)
-          import codecs
-          fd.close()
-          fd = codecs.open(a.path, 'rb', encoding=charset)
-          return fd, mimetype
+            mimesys = Mimeview(self.env)
+            charset = mimesys.get_charset(content, mimetype)
+            fd.close()
+            fd = codecs.open(a.path, 'rb', encoding=charset)
+            return fd, mimetype
         else :
-          return content_to_unicode(self.env, content, mimetype)
+            return content_to_unicode(self.env, content, mimetype)
     
     # IGVizDataProvider methods
     @gviz_param('realm', "the realm of the resource owning the " \

File trac-dev/gviz/tracgviz/extfmt.py

 __all__ = 'GVizMoinEncoder',
 
 from trac.core import Component, implements
-from api import IGVizTableEncoder
-from util import render_gviz_value
+
+from tracgviz.api import IGVizTableEncoder
+from tracgviz.util import render_gviz_value
 
 class GVizMoinEncoder(Component):
     r"""MoinMoin wiki encoder for Google Visualization API.
                             for col in table._DataTable__columns])
         
         def rowvalues(row):
-          for col in columns_order:
-            # Do not display None values
-            value = row.get(col, None)
-            if value is None:
-              yield ""
-            else:
-#              value = table.SingleValueToJS(value, col_dict[col]["type"])
-              value = render_gviz_value(value, col_dict[col]["type"], \
-                                        table, self.env)
-              for changes in [('||', '| |'), ('\n', ' '), ('\r', ' '),]:
-                value = value.replace(*changes)
-              if isinstance(value, tuple):
-                # We have a formatted value as well ... show it
-                value = value[1]
-              yield value
+            for col in columns_order:
+                # Do not display None values
+                value = row.get(col, None)
+                if value is None:
+                    yield ""
+                else:
+#                    value = table.SingleValueToJS(value, col_dict[col]["type"])
+                    value = render_gviz_value(value, col_dict[col]["type"], \
+                                              table, self.env)
+                    for changes in [('||', '| |'), ('\n', ' '), ('\r', ' '),]:
+                        value = value.replace(*changes)
+                    if isinstance(value, tuple):
+                        # We have a formatted value as well ... show it
+                        value = value[1]
+                    yield value
         
         for row in table._PreparedData(()):
             out+= '||'

File trac-dev/gviz/tracgviz/gvizql.py

 __author__ = 'Olemis Lang'
 
 __all__ = 'prepare_ql_data', 'GVizQLClauseHandler', 'GVizQLParser', \
-          'defaultParser'
+                    'defaultParser'
 __metaclass__ = type
 
 from itertools import islice, izip
 #------------------------------------------------------
 
 class GVizQLClauseType(type):
-  r"""Keep track of all GVizQL clause handlers installed in the 
-  system, syntax order ...
-  
-  >>> ','.join(ct.get_props('keyw') for ct in GVizQLClauseType.iterparse())
-  'select,from,where,group by,pivot,order by,limit,offset,label,format,options'
-  
-  ... as well as evaluation order.
-  
-  >>> ','.join(ct.get_props('keyw') for ct in GVizQLClauseType.itereval())
-  'from,group by,pivot,where,order by,offset,limit,select,format,options,label'
-  """
-  CLAUSE_CACHE = dict()
-  SYNTAX_ORDER = list()
-  EVAL_ORDER = list()
-  PROPS = ('idx_syntax', 'idx_eval', 'keyw')
-  
-  def __new__(cls, name, bases, suite):
     r"""Keep track of all GVizQL clause handlers installed in the 
-    system, as well as evaluation order.
+    system, syntax order ...
+    
+    >>> ','.join(ct.get_props('keyw') for ct in GVizQLClauseType.iterparse())
+    'select,from,where,group by,pivot,order by,limit,offset,label,format,options'
+    
+    ... as well as evaluation order.
+    
+    >>> ','.join(ct.get_props('keyw') for ct in GVizQLClauseType.itereval())
+    'from,group by,pivot,where,order by,offset,limit,select,format,options,label'
     """
-    try:
-      abstract = suite['__abstract__']
-      del suite['__abstract__']
-    except KeyError:
-      abstract = False
+    CLAUSE_CACHE = dict()
+    SYNTAX_ORDER = list()
+    EVAL_ORDER = list()
+    PROPS = ('idx_syntax', 'idx_eval', 'keyw')
     
-    @classmethod
-    def get_props(cls, propnm):
-      try:
-        return self._PROPS[propnm]
-      except KeyError, exc:
-        raise ValueError('Unsupported property %s', exc.message)
-    suite['get_props'] = get_props
+    def __new__(cls, name, bases, suite):
+        r"""Keep track of all GVizQL clause handlers installed in the 
+        system, as well as evaluation order.
+        """
+        try:
+            abstract = suite['__abstract__']
+            del suite['__abstract__']
+        except KeyError:
+            abstract = False
+        
+        @classmethod
+        def get_props(cls, propnm):
+            try:
+                return self._PROPS[propnm]
+            except KeyError, exc:
+                raise ValueError('Unsupported property %s', exc.message)
+        suite['get_props'] = get_props
+        
+        self = super(GVizQLClauseType, cls).__new__(cls, name, bases, suite)
+        if not abstract:
+            cnm = self.get_props('keyw')
+            GVizQLClauseType.CLAUSE_CACHE[cnm] = self
+            GVizQLClauseType.SYNTAX_ORDER.append(self)
+            GVizQLClauseType.SYNTAX_ORDER.sort(None, \
+                                                  lambda x: x.get_props('idx_syntax'))
+            GVizQLClauseType.EVAL_ORDER.append(self)
+            GVizQLClauseType.EVAL_ORDER.sort(None, \
+                                                  lambda x: x.get_props('idx_eval'))
+        return self
     
-    self = super(GVizQLClauseType, cls).__new__(cls, name, bases, suite)
-    if not abstract:
-      cnm = self.get_props('keyw')
-      GVizQLClauseType.CLAUSE_CACHE[cnm] = self
-      GVizQLClauseType.SYNTAX_ORDER.append(self)
-      GVizQLClauseType.SYNTAX_ORDER.sort(None, \
-                                    lambda x: x.get_props('idx_syntax'))
-      GVizQLClauseType.EVAL_ORDER.append(self)
-      GVizQLClauseType.EVAL_ORDER.sort(None, \
-                                    lambda x: x.get_props('idx_eval'))
-    return self
-  
-  @staticmethod
-  def itereval():
-    r"""Iterate over GVizQL clause handlers following evaluation order.
-    """
-    return iter(GVizQLClauseType.EVAL_ORDER)
-  
-  @staticmethod
-  def iterparse():
-    r"""Iterate over GVizQL clause handlers following syntax order.
-    """
-    return iter(GVizQLClauseType.SYNTAX_ORDER)
-  
-  @staticmethod
-  def clauses():
-    r"""Retrieved all the keywords identifying different clauses 
-    supported in the system.
-    """
-    return GVizQLClauseType.CLAUSE_CACHE.keys()
+    @staticmethod
+    def itereval():
+        r"""Iterate over GVizQL clause handlers following evaluation order.
+        """
+        return iter(GVizQLClauseType.EVAL_ORDER)
+    
+    @staticmethod
+    def iterparse():
+        r"""Iterate over GVizQL clause handlers following syntax order.
+        """
+        return iter(GVizQLClauseType.SYNTAX_ORDER)
+    
+    @staticmethod
+    def clauses():
+        r"""Retrieved all the keywords identifying different clauses 
+        supported in the system.
+        """
+        return GVizQLClauseType.CLAUSE_CACHE.keys()
 
 class GVizQLClauseHandler:
-  r"""Objects used to define syntax and retrieve items inside a 
-  clause. It's also responsible of performing the transformations 
-  dictated by this clause on a data set.
-  
-  Instances of this class have to override the following fields:
-    - `_PROPS` :      a static dictionary containing the following keys
-      * `idx_syntax`    a number indicating the relative syntantic 
-                        order of this clause.
-      * `idx_eval`      a number indicating the relative order of 
-                        this clause at evaluation time.
-      * `keyw`          the keyword introducing (i.e. identifying) 
-                        this clause.
-    - `transform` :   override this method in order to apply the 
-                      transformations specified by the client  in the 
-                      corresponding clause to the input result set.
-  """
-  __metaclass__ = GVizQLClauseType
-  __abstract__ = True
-  
-  ERR_MSGS = {
-      'PARSING' : "%(clause)s clause is not supported yet.",
-      'EVAL' :    "Unable to evaluate %(clause)s clause. " \
-                  "Either the whole clause or an specific feature " \
-                  "is not supported yet."
-    }
-  
-  def unsupported(self, at='PARSING', **vals):
-    r"""Indicate that this clause is not supported yet.
+    r"""Objects used to define syntax and retrieve items inside a 
+    clause. It's also responsible of performing the transformations 
+    dictated by this clause on a data set.
+    
+    Instances of this class have to override the following fields:
+        - `_PROPS` :      a static dictionary containing the following keys
+            * `idx_syntax`    a number indicating the relative syntantic 
+                                      order of this clause.
+            * `idx_eval`      a number indicating the relative order of 
+                                      this clause at evaluation time.
+            * `keyw`          the keyword introducing (i.e. identifying) 
+                                      this clause.
+        - `transform` :   override this method in order to apply the 
+                                    transformations specified by the client  in the 
+                                    corresponding clause to the input result set.
     """
-    vals['clause'] = self.get_props('keyw').upper()
-    msg = self.ERR_MSGS.get(at, '') % vals
-    raise GVizUnsupportedQueryOp(msg)
-  
-  def transform(self, schema, data):
-    r"""Transform the result set as dictated by the clause included 
-    in the GViz QL expression provided by the client. This 
-    transformation *SHOULD* be performed by using iterators.
+    __metaclass__ = GVizQLClauseType
+    __abstract__ = True
     
-    Note: Default behavior is to indicate that the clause is not 
-    supported.
-    """
-    self.unsupported(at='EVAL')
+    ERR_MSGS = {
+            'PARSING' : "%(clause)s clause is not supported yet.",
+            'EVAL' :    "Unable to evaluate %(clause)s clause. " \
+                                "Either the whole clause or an specific feature " \
+                                "is not supported yet."
+        }
+    
+    def unsupported(self, at='PARSING', **vals):
+        r"""Indicate that this clause is not supported yet.
+        """
+        vals['clause'] = self.get_props('keyw').upper()
+        msg = self.ERR_MSGS.get(at, '') % vals
+        raise GVizUnsupportedQueryOp(msg)
+    
+    def transform(self, schema, data):
+        r"""Transform the result set as dictated by the clause included 
+        in the GViz QL expression provided by the client. This 
+        transformation *SHOULD* be performed by using iterators.
+        
+        Note: Default behavior is to indicate that the clause is not 
+        supported.
+        """
+        self.unsupported(at='EVAL')
 
 class GVizSelectClause(GVizQLClauseHandler):
-  _PROPS = {'idx_syntax' : 0, 'idx_eval': 7, 'keyw' : 'select'}
-  KEYWORDS = ('select',)
-  
-  def __init__(self, ctx):
-    r"""Initialize the expression using the enclosing parsing context
-    (see docs for `GVizQLParsingContext`). Try to parse the items 
-    in SELECT clause.
-    """
-    tkn, val = ctx.stream.next()
-    if tkn is Name.Other and val == '*':
-      self.cols = None
-      tkn, val = ctx.stream.next()
-    elif tkn is Name.Variable:
-      if val.startswith('`'):
-        val = val[1:-1]
-      self.cols = cols = [val]
-      for tkn, val in ctx.stream :
-        if tkn is Punctuation and val == ',' :
-          tkn, val = ctx.stream.next()
-          if tkn is Name.Variable :     # FIXME: Just column names so far
+    _PROPS = {'idx_syntax' : 0, 'idx_eval': 7, 'keyw' : 'select'}
+    KEYWORDS = ('select',)
+    
+    def __init__(self, ctx):
+        r"""Initialize the expression using the enclosing parsing context
+        (see docs for `GVizQLParsingContext`). Try to parse the items 
+        in SELECT clause.
+        """
+        tkn, val = ctx.stream.next()
+        if tkn is Name.Other and val == '*':
+            self.cols = None
+            tkn, val = ctx.stream.next()
+        elif tkn is Name.Variable:
             if val.startswith('`'):
-              val = val[1:-1]
-            cols.append(val)
-          elif tkn in (Name.Function, Name.Builtin):
+                val = val[1:-1]
+            self.cols = cols = [val]
+            for tkn, val in ctx.stream :
+                if tkn is Punctuation and val == ',' :
+                    tkn, val = ctx.stream.next()
+                    if tkn is Name.Variable :     # FIXME: Just column names so far
+                        if val.startswith('`'):
+                            val = val[1:-1]
+                        cols.append(val)
+                    elif tkn in (Name.Function, Name.Builtin):
+                        raise GVizUnsupportedQueryOp("Scalar and aggregate functions " \
+                                                        "not supported yet : token %s" % \
+                                                        (val,))
+                    else:
+                        raise GVizInvalidQuery("Syntax error or unsupported " \
+                                                  "feature. Unexpected token %s." \
+                                                                          % (val,))
+                else:
+                    break
+        elif tkn in (Name.Function, Name.Builtin):
             raise GVizUnsupportedQueryOp("Scalar and aggregate functions " \
-                                          "not supported yet : token %s" % \
-                                          (val,))
-          else:
+                                                  "not supported yet : token %s" % \
+                                                  (val,))
+        else:
+            if tkn is Whitespace and val == '\n' :
+                val = '<EOL>'
             raise GVizInvalidQuery("Syntax error or unsupported " \
-                                    "feature. Unexpected token %s." \
-                                                            % (val,))
+                                            "feature. Unexpected token %s." % (val,))
+    
+    def transform(self, schema, data):
+        r"""Arrange values considering the given column order.
+        """
+        cols = self.cols
+        if cols is None:
+            return schema, data
         else:
-          break
-    elif tkn in (Name.Function, Name.Builtin):
-      raise GVizUnsupportedQueryOp("Scalar and aggregate functions " \
-                                    "not supported yet : token %s" % \
-                                    (val,))
-    else:
-      if tkn is Whitespace and val == '\n' :
-        val = '<EOL>'
-      raise GVizInvalidQuery("Syntax error or unsupported " \
-                              "feature. Unexpected token %s." % (val,))
-  
-  def transform(self, schema, data):
-    r"""Arrange values considering the given column order.
-    """
-    cols = self.cols
-    if cols is None:
-      return schema, data
-    else:
-      idxmap = [None] * len(cols)
-      schcols = dict([col[0], i] for i, col in enumerate(schema))
-      for i, colnm in enumerate(cols):
-        try:
-          idxmap[i] = schcols[colnm]
-        except KeyError, exc:
-          raise GVizInvalidQuery("Unable to evaluate SELECT clause. " \
-                                  "Unknown column %s." % (exc.message,))
-      new_schema = [schema[i] for i in idxmap]
-      new_data = ([row[i] for i in idxmap] for row in data)
-      return new_schema, new_data
+            idxmap = [None] * len(cols)
+            schcols = dict([col[0], i] for i, col in enumerate(schema))
+            for i, colnm in enumerate(cols):
+                try:
+                    idxmap[i] = schcols[colnm]
+                except KeyError, exc:
+                    raise GVizInvalidQuery("Unable to evaluate SELECT clause. " \
+                                                "Unknown column %s." % (exc.message,))
+            new_schema = [schema[i] for i in idxmap]
+            new_data = ([row[i] for i in idxmap] for row in data)
+            return new_schema, new_data
 
 class GVizFromClause(GVizQLClauseHandler):
-  _PROPS = {'idx_syntax' : 1, 'idx_eval': 0, 'keyw' : 'from'}
-  KEYWORDS = ('from',)
-  
-  def __init__(self, ctx):
-    r"""Initialize the expression using the enclosing parsing context
-    (see docs for `GVizQLParsingContext`). Try to parse the items 
-    in FROM clause.
-    """
-    tkn, val = ctx.stream.next()
-    if tkn is Name.Variable :
-      if val.startswith('`'):
-        val = val[1:-1]
-      self.basetable = val
-      ctx.stream.next()
-    else :
-      raise GVizInvalidQuery("Syntax error: Table name expected but" \
-                              " %s found." % (val,))
-  def transform(self, schema, data):
-    r"""Notify that base table has not been processed by data source. 
-    This is made this way since it may hide a bug in the underlying 
-    data source implementation.
+    _PROPS = {'idx_syntax' : 1, 'idx_eval': 0, 'keyw' : 'from'}
+    KEYWORDS = ('from',)
     
-    FROM clause is irrevelevant for transformations. This clause is 
-    available so that the underlying data sources be able to handle 
-    it. This feature will be supported in upcoming versions.
-    """
-    raise GVizInvalidQuery("Unable to evaluate FROM clause. " \
-                              "Data source doesn't contain base " \
-                              "table %s." % (self.basetable,))
+    def __init__(self, ctx):
+        r"""Initialize the expression using the enclosing parsing context
+        (see docs for `GVizQLParsingContext`). Try to parse the items 
+        in FROM clause.
+        """
+        tkn, val = ctx.stream.next()
+        if tkn is Name.Variable :
+            if val.startswith('`'):
+                val = val[1:-1]
+            self.basetable = val
+            ctx.stream.next()
+        else :
+            raise GVizInvalidQuery("Syntax error: Table name expected but" \
+                                            " %s found." % (val,))
+    def transform(self, schema, data):
+        r"""Notify that base table has not been processed by data source. 
+        This is made this way since it may hide a bug in the underlying 
+        data source implementation.
+        
+        FROM clause is irrevelevant for transformations. This clause is 
+        available so that the underlying data sources be able to handle 
+        it. This feature will be supported in upcoming versions.
+        """
+        raise GVizInvalidQuery("Unable to evaluate FROM clause. " \
+                                            "Data source doesn't contain base " \
+                                            "table %s." % (self.basetable,))
 
 class GVizWhereClause(GVizQLClauseHandler):
-  _PROPS = {'idx_syntax' : 2, 'idx_eval': 3, 'keyw' : 'where'}
-  KEYWORDS = ('where',)
-  
-  def __init__(self, ctx):
-    r"""Initialize the expression using the enclosing parsing context
-    (see docs for `GVizQLParsingContext`). Try to parse the items 
-    in WHERE clause.
-    """
-    self.unsupported()
+    _PROPS = {'idx_syntax' : 2, 'idx_eval': 3, 'keyw' : 'where'}
+    KEYWORDS = ('where',)
+    
+    def __init__(self, ctx):
+        r"""Initialize the expression using the enclosing parsing context
+        (see docs for `GVizQLParsingContext`). Try to parse the items 
+        in WHERE clause.
+        """
+        self.unsupported()
 
 class GVizGroupByClause(GVizQLClauseHandler):
-  _PROPS = {'idx_syntax' : 3, 'idx_eval': 1, 'keyw' : 'group by'}
-  KEYWORDS = ('group', 'by')
-  
-  def __init__(self, ctx):
-    r"""Initialize the expression using the enclosing parsing context
-    (see docs for `GVizQLParsingContext`). Try to parse the items 
-    in GROUP BY clause.
-    """
-    self.unsupported()
+    _PROPS = {'idx_syntax' : 3, 'idx_eval': 1, 'keyw' : 'group by'}
+    KEYWORDS = ('group', 'by')
+    
+    def __init__(self, ctx):
+        r"""Initialize the expression using the enclosing parsing context
+        (see docs for `GVizQLParsingContext`). Try to parse the items 
+        in GROUP BY clause.
+        """
+        self.unsupported()
 
 class GVizPivotClause(GVizQLClauseHandler):
-  _PROPS = {'idx_syntax' : 4, 'idx_eval': 2, 'keyw' : 'pivot'}
-  KEYWORDS = ('pivot',)
-  
-  def __init__(self, ctx):
-    r"""Initialize the expression using the enclosing parsing context
-    (see docs for `GVizQLParsingContext`). Try to parse the items 
-    in the SELECT clause.
-    """
-    self.unsupported()
+    _PROPS = {'idx_syntax' : 4, 'idx_eval': 2, 'keyw' : 'pivot'}
+    KEYWORDS = ('pivot',)
+    
+    def __init__(self, ctx):
+        r"""Initialize the expression using the enclosing parsing context
+        (see docs for `GVizQLParsingContext`). Try to parse the items 
+        in the SELECT clause.
+        """
+        self.unsupported()
 
 class GVizOrderByClause(GVizQLClauseHandler):
-  _PROPS = {'idx_syntax' : 5, 'idx_eval': 4, 'keyw' : 'order by'}
-  KEYWORDS = ['asc', 'desc', 'order', 'by']
-  
-  def __init__(self, ctx):
-    r"""Initialize the expression using the enclosing parsing context
-    (see docs for `GVizQLParsingContext`). Try to parse the items 
-    in the SELECT clause.
-    """
-    self.unsupported()
+    _PROPS = {'idx_syntax' : 5, 'idx_eval': 4, 'keyw' : 'order by'}
+    KEYWORDS = ['asc', 'desc', 'order', 'by']
+    
+    def __init__(self, ctx):
+        r"""Initialize the expression using the enclosing parsing context
+        (see docs for `GVizQLParsingContext`). Try to parse the items 
+        in the SELECT clause.
+        """
+        self.unsupported()
 
 class GVizLimitClause(GVizQLClauseHandler):
-  _PROPS = {'idx_syntax' : 6, 'idx_eval': 6, 'keyw' : 'limit'}
-  KEYWORDS = ('limit',)
-  
-  def __init__(self, ctx):
-    r"""Initialize the expression using the enclosing parsing context
-    (see docs for `GVizQLParsingContext`). Try to parse the items 
-    in the SELECT clause.
-    """
-    tkn, val = ctx.stream.next()
-    if tkn is Number.Integer :
-      self.cnt = int(val)
-      ctx.stream.next()
-    else :
-      raise GVizInvalidQuery("Syntax error: Integer number expected " \
-                              "but %s found." % (val,))
-  
-  def transform(self, schema, data):
-    r"""Retrieve no more than `cnt` items.
-    """
-    return schema, islice(data, self.cnt)
+    _PROPS = {'idx_syntax' : 6, 'idx_eval': 6, 'keyw' : 'limit'}
+    KEYWORDS = ('limit',)
+    
+    def __init__(self, ctx):
+        r"""Initialize the expression using the enclosing parsing context
+        (see docs for `GVizQLParsingContext`). Try to parse the items 
+        in the SELECT clause.
+        """
+        tkn, val = ctx.stream.next()
+        if tkn is Number.Integer :
+            self.cnt = int(val)
+            ctx.stream.next()
+        else :
+            raise GVizInvalidQuery("Syntax error: Integer number expected " \
+                                            "but %s found." % (val,))
+    
+    def transform(self, schema, data):
+        r"""Retrieve no more than `cnt` items.
+        """
+        return schema, islice(data, self.cnt)
 
 class GVizOffsetClause(GVizQLClauseHandler):
-  _PROPS = {'idx_syntax' : 7, 'idx_eval': 5, 'keyw' : 'offset'}
-  KEYWORDS = ('offset',)
-  
-  def __init__(self, ctx):
-    r"""Initialize the expression using the enclosing parsing context
-    (see docs for `GVizQLParsingContext`). Try to parse the items 
-    in the SELECT clause.
-    """
-    tkn, val = ctx.stream.next()
-    if tkn is Number.Integer :
-      self.skip = int(val)
-      ctx.stream.next()
-    else :
-      raise GVizInvalidQuery("Syntax error: Integer number expected " \
-                              "but %s found." % (val,))
-  
-  def transform(self, schema, data):
-    r"""Skip the number of items determined by `skip` attribute.
-    """
-    return schema, islice(data, self.skip, None)
+    _PROPS = {'idx_syntax' : 7, 'idx_eval': 5, 'keyw' : 'offset'}
+    KEYWORDS = ('offset',)
+    
+    def __init__(self, ctx):
+        r"""Initialize the expression using the enclosing parsing context
+        (see docs for `GVizQLParsingContext`). Try to parse the items 
+        in the SELECT clause.
+        """
+        tkn, val = ctx.stream.next()
+        if tkn is Number.Integer :
+            self.skip = int(val)
+            ctx.stream.next()
+        else :
+            raise GVizInvalidQuery("Syntax error: Integer number expected " \
+                                            "but %s found." % (val,))
+    
+    def transform(self, schema, data):
+        r"""Skip the number of items determined by `skip` attribute.
+        """
+        return schema, islice(data, self.skip, None)
 
 class GVizLabelClause(GVizQLClauseHandler):
-  _PROPS = {'idx_syntax' : 8, 'idx_eval': 10, 'keyw' : 'label'}
-  KEYWORDS = ('label',)
-  
-  def __init__(self, ctx):
-    r"""Initialize the expression using the enclosing parsing context
-    (see docs for `GVizQLParsingContext`). Try to parse the items 
-    in the SELECT clause.
-    """
-    self.labels = labels = []
-    tkn, val = Punctuation, ','               # Enter the cycle initially
-    while tkn is Punctuation and val == ',':
-      tkn, val = ctx.stream.next()
-      if tkn is Name.Variable :
-        if val.startswith('`'):
-          val = val[1:-1]
-        colnm = val
-        tkn, val = ctx.stream.next()
-        if tkn in [String.Single, String.Double]:
-          val = val[1:-1]                         # Remove quotes
-          labels.append((colnm, val.encode('utf-8', 'ignore')))
-        else :
-          raise GVizInvalidQuery("Syntax error: String constant " \
-                                "expected but %s found." % (val,))
-        tkn, val = ctx.stream.next()
-      else :
-        raise GVizInvalidQuery("Syntax error: Column name expected " \
-                                "but %s found." % (val,))
-  
-  def transform(self, schema, data):
-    r"""(Add | modify) the schema in order to (include | update) 
-    column labels.
+    _PROPS = {'idx_syntax' : 8, 'idx_eval': 10, 'keyw' : 'label'}
+    KEYWORDS = ('label',)
     
-    Note: The process is not aborted if a label for a missing column 
-          is specified.
-    """
-    labels = dict(self.labels)
-    new_schema = [col[0] in labels and col[:2] + (labels[col[0]],) or col \
-                    for col in schema]
-    return new_schema, data
+    def __init__(self, ctx):
+        r"""Initialize the expression using the enclosing parsing context
+        (see docs for `GVizQLParsingContext`). Try to parse the items 
+        in the SELECT clause.
+        """
+        self.labels = labels = []
+        tkn, val = Punctuation, ','               # Enter the cycle initially
+        while tkn is Punctuation and val == ',':
+            tkn, val = ctx.stream.next()
+            if tkn is Name.Variable :
+                if val.startswith('`'):
+                    val = val[1:-1]
+                colnm = val
+                tkn, val = ctx.stream.next()
+                if tkn in [String.Single, String.Double]:
+                    val = val[1:-1]                         # Remove quotes
+                    labels.append((colnm, val.encode('utf-8', 'ignore')))
+                else :
+                    raise GVizInvalidQuery("Syntax error: String constant " \
+                                              "expected but %s found." % (val,))
+                tkn, val = ctx.stream.next()
+            else :
+                raise GVizInvalidQuery("Syntax error: Column name expected " \
+                                              "but %s found." % (val,))
+    
+    def transform(self, schema, data):
+        r"""(Add | modify) the schema in order to (include | update) 
+        column labels.
+        
+        Note: The process is not aborted if a label for a missing column 
+                    is specified.
+        """
+        labels = dict(self.labels)
+        new_schema = [col[0] in labels and col[:2] + (labels[col[0]],) or col \
+                                  for col in schema]
+        return new_schema, data
 
 class GVizFormatClause(GVizQLClauseHandler):
-  _PROPS = {'idx_syntax' : 9, 'idx_eval': 8, 'keyw' : 'format'}
-  KEYWORDS = ('format',)
-  
-  def __init__(self, ctx):
-    r"""Initialize the expression using the enclosing parsing context
-    (see docs for `GVizQLParsingContext`). Try to parse the items 
-    in the SELECT clause.
-    """
-    self.unsupported()
+    _PROPS = {'idx_syntax' : 9, 'idx_eval': 8, 'keyw' : 'format'}
+    KEYWORDS = ('format',)
+    
+    def __init__(self, ctx):
+        r"""Initialize the expression using the enclosing parsing context
+        (see docs for `GVizQLParsingContext`). Try to parse the items 
+        in the SELECT clause.
+        """
+        self.unsupported()
 
 class GVizOptionsClause(GVizQLClauseHandler):
-  _PROPS = {'idx_syntax' : 10, 'idx_eval': 9, 'keyw' : 'options'}
-  KEYWORDS = ('options',)
-  
-  def __init__(self, ctx):
-    r"""Initialize the expression using the enclosing parsing context
-    (see docs for `GVizQLParsingContext`). Try to parse the items 
-    in the SELECT clause.
-    """
-    self.unsupported()
+    _PROPS = {'idx_syntax' : 10, 'idx_eval': 9, 'keyw' : 'options'}
+    KEYWORDS = ('options',)
+    
+    def __init__(self, ctx):
+        r"""Initialize the expression using the enclosing parsing context
+        (see docs for `GVizQLParsingContext`). Try to parse the items 
+        in the SELECT clause.
+        """
+        self.unsupported()
 
 #------------------------------------------------------
 #   GVizQL parsing and compilation
 #------------------------------------------------------
 
 class GVizQLParser(RegexLexer):
-  r"""Objects used to parse GVizQL expressions. 
-  """
-  GVIZ_QL_KEYWS = [k for ct in GVizQLClauseType.iterparse() \
-                        for k in ct.KEYWORDS]
-  GVIZ_QL_OPS = 'and or is not'.split()
-  GVIZ_QL_FUNCS = 'max min count avg sum'.split()
-  GVIZ_QL_CPREF = 'date datetime timeofday timestamp'.split()
-  GVIZ_QL_CONST = 'true false null'.split()
-  GVIZ_QL_TOKENS = {                # Keywords generated in update_syntax
-                                    # Identifiers generated in update_syntax
-        'int' : '(?:\d+)',          # Integer numbers
-        'str' : r"""(?:(?:\'[^']*\')|(?:\"[^"]*\"))""", # Strings
-        'number' : '(?:+|-)\d*(?:\.\d+)?',              # Numbers
-        'bool' : '(?:true|false)',  # Boolean constants
-        'date' : '(?:true|false)',
-      }
-  tokens = {
-        'root' : [
-            (r'[(,)]', Punctuation),
-            (r'(?:<=)|(?:>=)|(?:!=)|(?:<>)', Operator),
-            (r'[-+*/<>=]', Operator),
-            (r'(?=date\s)', Whitespace, 'date'),
-            (r'(?=timeofday\s)', Whitespace, 'tod'),
-            (r'(?=(?:datetime|timestamp)\s)', Whitespace, 'dt'),
-            (r'`(?:\w|\s)+`', Name.Variable),
-            (r'`.*`', Generic.Error),
-            (r'`[^`]*(?=$)', Generic.Error),
-            (r'\d*\.\d+(?=[^a-zA-Z_])', Number),
-            (r'\d+(?=[^a-zA-Z_])', Number.Integer),
-            (r'(?:\d*\.)?\d+(?:[a-zA-Z_]\w*)', Generic.Error),
-            (r'(?=\w)', Whitespace, 'alpha'),
-            (r'(?:\d+)(?=[.a-zA-Z_])', Generic.Error),
-            (r"\'[^']*\'", String.Single),
-            (r'\"[^"]*\"', String.Double),
-            (r"\'[^']*(?=$)", Generic.Error),
-            (r'\"[^"]*(?=$)', Generic.Error),
-            (r'\s+', Whitespace),
-            (r'.', Generic.Error),
-          ],
-        'date' : [
-            (r"date\s+\'\d{4}-\d{1,2}-\d{1,2}\'", Literal.Date, '#pop'),
-            (r'date\s+\"\d{4}-\d{1,2}-\d{1,2}\"', Literal.Date, '#pop'),
-            (r"date\s+", Generic.Error, '#pop')
-          ],
-        'tod' : [
-            (r"timeofday\s+\'\d{1,2}(?:\:\d{1,2}){2}(?:\.\d{1-3})?\'", \
-                Literal.Date, '#pop'),
-            (r'timeofday\s+\"\d{1,2}(?:\:\d{1,2}){2}(?:\.\d{1-3})?\"', \
-                Literal.Date, '#pop'),
-            (r"timeofday\s+", Generic.Error, '#pop')
-          ],
-        'dt' : [
-            (r'(?:datetime|timestamp)\s+\"\d{4}-\d{1,2}-\d{1,2}\ ' \
-             r'\d{1,2}(?:\:\d{1,2}){2}(?:\.\d{3})?\"', \
-                Literal.Date, '#pop'),
-            (r"(?:datetime|timestamp)\s+\'\d{4}-\d{1,2}-\d{1,2}\ " \
-             r"\d{1,2}(?:\:\d{1,2}){2}(?:\.\d{3})?\'", \
-                Literal.Date, '#pop'),
-            (r"(?:datetime|timestamp)\s+", Generic.Error, '#pop')
-          ],
-        'alpha' : [
-            (r'(?:%s)(?=\s|$)' % '|'.join(GVIZ_QL_CONST), \
-                Keyword.Constant, '#pop'),
-            (r'(?:(?:%s)|(?:is\ not))(?=\s)' % '|'.join(GVIZ_QL_OPS), \
-                Operator.Word, '#pop'),
-            ('(?:%s)(?=\W)' % '|'.join(GVizQLClauseType.clauses()), \
-                Keyword.Reserved, '#pop'), 
-            ('(?:%s)(?=\W)' % '|'.join(GVIZ_QL_KEYWS), 
-                Keyword, '#pop'), 
-            ('(?:%s)(?=\W)' % '|'.join(GVIZ_QL_FUNCS), 
-                Name.Builtin, '#pop'), 
-            (r'[a-zA-Z]\w*(?=\()', Name.Function, '#pop'),
-            (r'[a-zA-Z]\w*', Name.Variable, '#pop'),
-          ],
-      }
-  
-  def __init__(self, *args, **kwds):
-    super(GVizQLParser, self).__init__(*args, **kwds)
-    self.noisy = True
-  
-  def parse(self, tq):
-    r"""Parse a GVizQL expression.
+    r"""Objects used to parse GVizQL expressions. 
+    """
+    GVIZ_QL_KEYWS = [k for ct in GVizQLClauseType.iterparse() \
+                                      for k in ct.KEYWORDS]
+    GVIZ_QL_OPS = 'and or is not'.split()
+    GVIZ_QL_FUNCS = 'max min count avg sum'.split()
+    GVIZ_QL_CPREF = 'date datetime timeofday timestamp'.split()
+    GVIZ_QL_CONST = 'true false null'.split()
+    GVIZ_QL_TOKENS = {                # Keywords generated in update_syntax
+                                                  # Identifiers generated in update_syntax
+                'int' : '(?:\d+)',          # Integer numbers
+                'str' : r"""(?:(?:\'[^']*\')|(?:\"[^"]*\"))""", # Strings
+                'number' : '(?:+|-)\d*(?:\.\d+)?',              # Numbers
+                'bool' : '(?:true|false)',  # Boolean constants
+                'date' : '(?:true|false)',
+            }
+    tokens = {
+                'root' : [
+                        (r'[(,)]', Punctuation),
+                        (r'(?:<=)|(?:>=)|(?:!=)|(?:<>)', Operator),
+                        (r'[-+*/<>=]', Operator),
+                        (r'(?=date\s)', Whitespace, 'date'),
+                        (r'(?=timeofday\s)', Whitespace, 'tod'),
+                        (r'(?=(?:datetime|timestamp)\s)', Whitespace, 'dt'),
+                        (r'`(?:\w|\s)+`', Name.Variable),
+                        (r'`.*`', Generic.Error),
+                        (r'`[^`]*(?=$)', Generic.Error),
+                        (r'\d*\.\d+(?=[^a-zA-Z_])', Number),
+                        (r'\d+(?=[^a-zA-Z_])', Number.Integer),
+                        (r'(?:\d*\.)?\d+(?:[a-zA-Z_]\w*)', Generic.Error),
+                        (r'(?=\w)', Whitespace, 'alpha'),
+                        (r'(?:\d+)(?=[.a-zA-Z_])', Generic.Error),
+                        (r"\'[^']*\'", String.Single),
+                        (r'\"[^"]*\"', String.Double),
+                        (r"\'[^']*(?=$)", Generic.Error),
+                        (r'\"[^"]*(?=$)', Generic.Error),
+                        (r'\s+', Whitespace),
+                        (r'.', Generic.Error),
+                    ],
+                'date' : [
+                        (r"date\s+\'\d{4}-\d{1,2}-\d{1,2}\'", Literal.Date, '#pop'),
+                        (r'date\s+\"\d{4}-\d{1,2}-\d{1,2}\"', Literal.Date, '#pop'),
+                        (r"date\s+", Generic.Error, '#pop')
+                    ],
+                'tod' : [
+                        (r"timeofday\s+\'\d{1,2}(?:\:\d{1,2}){2}(?:\.\d{1-3})?\'", \
+                              Literal.Date, '#pop'),
+                        (r'timeofday\s+\"\d{1,2}(?:\:\d{1,2}){2}(?:\.\d{1-3})?\"', \
+                              Literal.Date, '#pop'),
+                        (r"timeofday\s+", Generic.Error, '#pop')
+                    ],
+                'dt' : [
+                        (r'(?:datetime|timestamp)\s+\"\d{4}-\d{1,2}-\d{1,2}\ ' \
+                         r'\d{1,2}(?:\:\d{1,2}){2}(?:\.\d{3})?\"', \
+                              Literal.Date, '#pop'),
+                        (r"(?:datetime|timestamp)\s+\'\d{4}-\d{1,2}-\d{1,2}\ " \
+                         r"\d{1,2}(?:\:\d{1,2}){2}(?:\.\d{3})?\'", \
+                              Literal.Date, '#pop'),
+                        (r"(?:datetime|timestamp)\s+", Generic.Error, '#pop')
+                    ],
+                'alpha' : [
+                        (r'(?:%s)(?=\s|$)' % '|'.join(GVIZ_QL_CONST), \
+                              Keyword.Constant, '#pop'),
+                        (r'(?:(?:%s)|(?:is\ not))(?=\s)' % '|'.join(GVIZ_QL_OPS), \
+                              Operator.Word, '#pop'),
+                        ('(?:%s)(?=\W)' % '|'.join(GVizQLClauseType.clauses()), \
+                              Keyword.Reserved, '#pop'), 
+                        ('(?:%s)(?=\W)' % '|'.join(GVIZ_QL_KEYWS), 
+                              Keyword, '#pop'), 
+                        ('(?:%s)(?=\W)' % '|'.join(GVIZ_QL_FUNCS), 
+                              Name.Builtin, '#pop'), 
+                        (r'[a-zA-Z]\w*(?=\()', Name.Function, '#pop'),
+                        (r'[a-zA-Z]\w*', Name.Variable, '#pop'),
+                    ],
+            }
     
-    @param tq                       the GViz QL expression.
-    @return                         an instance 
-    @throw GVizInvalidQuery         if a syntax error or other error 
-                                    is detected in the input string.
-    @throw GVizUnsupportedQueryOp   if the GViz QL query expression 
-                                    contains a clause or feature 
-                                    that's not supported yet.
+    def __init__(self, *args, **kwds):
+        super(GVizQLParser, self).__init__(*args, **kwds)
+        self.noisy = True
+    
+    def parse(self, tq):
+        r"""Parse a GVizQL expression.
+        
+        @param tq                       the GViz QL expression.
+        @return                         an instance 
+        @throw GVizInvalidQuery         if a syntax error or other error 
+                                                  is detected in the input string.
+        @throw GVizUnsupportedQueryOp   if the GViz QL query expression 
+                                                  contains a clause or feature 
+                                                  that's not supported yet.
+        """
+        global GVizUnsupportedQueryOp, GVizInvalidQuery
+        from api import GVizUnsupportedQueryOp as GU, GVizInvalidQuery as GI
+        GVizUnsupportedQueryOp, GVizInvalidQuery = GU, GI
+        
+        tkns = iter(self.get_tokens(tq))
+        try:
+            tkn, val = tkns.next()
+        except StopIteration:
+            raise GVizInvalidQuery("No token found: empty string ?")
+        else:
+            order = None
+            expr = GVizQLExpression()
+            ch = None
+            try:
+                while True:         # Try to parse the next clause
+                    if tkn is Whitespace and val == '\n':
+                        break
+                    elif tkn is not Keyword.Reserved: # Check for a valid clause 
+                        last = ch.get_props('keyw')
+                        raise GVizInvalidQuery("Invalid token %s found %s: " \
+                                               "Clause name expected." % \
+                                                      (val, ch and 'after ' + 
+                                                       last or ''))
+                    clause_type = GVizQLClauseType.CLAUSE_CACHE[val]
+                    clause_order = clause_type.get_props('idx_syntax')
+                    if not clause_order <= order:
+                        order = clause_order
+                    else:
+                        raise GVizInvalidQuery("Invalid token %s found: " \
+                                                  "Unexpected clause." % (val,))
+                    ch = clause_type(self._ctx) # Consume tokens until the 
+                                                # clause is over
+                    expr._add_handler(ch)
+                    tkn, val = self._ctx._last
+            except StopIteration:  # TODO: Trailing whitespace? (bug ?)
+                pass
+            if not expr._handlers:            # Something was actually parsed ?
+                raise GVizInvalidQuery("No token found: empty string ?")
+            return expr
+    
+    def get_tokens(self, tq):
+        try:
+            self._ctx = ctx = GVizQLParsingContext()
+            self.filters.append(ctx)
+            strm = super(GVizQLParser, self).get_tokens(tq)
+            ctx.stream = strm
+            ctx.parser = self
+            for x in strm:
+                yield x
+        finally:
+            self.filters = []
+            self._ctx = None
+        
+class GVizQLParsingContext():
+    r"""Used to store global information generated at parsing time. 
+    The parser adds the following fields to this object:
+        
+        - stream : The token stream being processed.
+        - parser : The object that's actually parsing the GViz QL expression.
     """
-    global GVizUnsupportedQueryOp, GVizInvalidQuery
-    from api import GVizUnsupportedQueryOp as GU, GVizInvalidQuery as GI
-    GVizUnsupportedQueryOp, GVizInvalidQuery = GU, GI
+    def __init__(self):
+        for attr in 'parser stream'.split():
+            setattr(self, attr, None)
+        self._last = (None, None)
     
-    tkns = iter(self.get_tokens(tq))
-    try:
-      tkn, val = tkns.next()
-    except StopIteration:
-      raise GVizInvalidQuery("No token found: empty string ?")
-    else:
-      order = None
-      expr = GVizQLExpression()
-      ch = None
-      try:
-        while True:         # Try to parse the next clause
-          if tkn is Whitespace and val == '\n':
-            break
-          elif tkn is not Keyword.Reserved:   # Check for a valid clause 
-            last = ch.get_props('keyw')
-            raise GVizInvalidQuery("Invalid token %s found %s: " \
-                                    "Clause name expected." % \
-                                        (val, ch and 'after ' + last or ''))
-          clause_type = GVizQLClauseType.CLAUSE_CACHE[val]
-          clause_order = clause_type.get_props('idx_syntax')
-          if not clause_order <= order:
-            order = clause_order
-          else:
-            raise GVizInvalidQuery("Invalid token %s found: " \
-                                    "Unexpected clause." % (val,))
-          ch = clause_type(self._ctx)     # Consume tokens until the 
-                                          # clause is over
-          expr._add_handler(ch)
-          tkn, val = self._ctx._last
-      except StopIteration:             # TODO: Trailing whitespace? (bug ?)
-        pass
-      if not expr._handlers:            # Something was actually parsed ?
-        raise GVizInvalidQuery("No token found: empty string ?")
-      return expr
-  
-  def get_tokens(self, tq):
-    try:
-      self._ctx = ctx = GVizQLParsingContext()
-      self.filters.append(ctx)
-      strm = super(GVizQLParser, self).get_tokens(tq)
-      ctx.stream = strm
-      ctx.parser = self
-      for x in strm:
-        yield x
-    finally:
-      self.filters = []
-      self._ctx = None
+    def filter(self, p, stream):
+        r""" Store lookahead token.
+        """
+        self.parser = p
+        self._last = (None, None)
+        for t in stream:
+            if t[0] is Generic.Error :
+                if p.noisy :
+                    raise GVizInvalidQuery("Invalid token %s " % t[1:])
+            elif t[0] is not Whitespace:
+                if self._last == (Keyword.Reserved, 'select') and \
+                        t == (Operator, '*'):
+                    t = Name.Other, '*'
+            elif t[1].endswith('\n'):
+                t = (Whitespace, '\n')
+            else:
+                continue
+            self._last = t
+            yield t
+        
+    @property
+    def last_token():
+        return self._last
     
-class GVizQLParsingContext():
-  r"""Used to store global information generated at parsing time. 
-  The parser adds the following fields to this object:
+class GVizQLExpression:
+    r"""Compiled GVizQL expression.
+    """
+    def __init__(self):
+        r"""Initialize a compiled GVizQL expression.
+        """
+        self._handlers = dict()
+        self._attrmap = dict()
+    def _add_handler(self, ch):
+        r"""Add a clause handler to this expression object. Bind the 
+        handler's attributes to lazy arguments.
+        """
+        clause_nm = ch.__class__.get_props('keyw')
+        self._handlers[clause_nm] = ch
+        for attrnm in ch.__dict__ :
+            self._attrmap[attrnm] = clause_nm
+        
+    def __getattr__(self, attrnm):
+        try:
+            return getattr(self._handlers[self._attrmap[attrnm]], attrnm)
+        except :
+            raise AttributeError("'%s' object has no attribute '%s'" % \
+                                              (self.__class__.__name__, attrnm))
     
-    - stream : The token stream being processed.
-    - parser : The object that's actually parsing the GViz QL expression.
-  """
-  def __init__(self):
-    for attr in 'parser stream'.split():
-      setattr(self, attr, None)
-    self._last = (None, None)
-  
-  def filter(self, p, stream):
-    r""" Store lookahead token.
-    """
-    self.parser = p
-    self._last = (None, None)
-    for t in stream:
-      if t[0] is Generic.Error :
-        if p.noisy :
-          raise GVizInvalidQuery("Invalid token %s " % t[1:])
-      elif t[0] is not Whitespace:
-        if self._last == (Keyword.Reserved, 'select') and \
-            t == (Operator, '*'):
-          t = Name.Other, '*'
-      elif t[1].endswith('\n'):
-        t = (Whitespace, '\n')
-      else:
-        continue
-      self._last = t
-      yield t
+    def itereval(self):
+        r"""Iterate over the clause handlers present in this expression 
+        following evaluation precedence.
+        """
+        for ct in GVizQLClauseType.itereval():
+            ch = self._handlers.get(ct.get_props('keyw'))
+            if ch is not None :
+                yield ch
     
-  @property
-  def last_token():
-    return self._last
-  
-class GVizQLExpression:
-  r"""Compiled GVizQL expression.
-  """
-  def __init__(self):
-    r"""Initialize a compiled GVizQL expression.
-    """
-    self._handlers = dict()
-    self._attrmap = dict()
-  def _add_handler(self, ch):
-    r"""Add a clause handler to this expression object. Bind the 
-    handler's attributes to lazy arguments.
-    """
-    clause_nm = ch.__class__.get_props('keyw')
-    self._handlers[clause_nm] = ch
-    for attrnm in ch.__dict__ :
-      self._attrmap[attrnm] = clause_nm
-    
-  def __getattr__(self, attrnm):
-    try:
-      return getattr(self._handlers[self._attrmap[attrnm]], attrnm)
-    except :
-      raise AttributeError("'%s' object has no attribute '%s'" % \
-                                (self.__class__.__name__, attrnm))
-  
-  def itereval(self):
-    r"""Iterate over the clause handlers present in this expression 
-    following evaluation precedence.
-    """
-    for ct in GVizQLClauseType.itereval():
-      ch = self._handlers.get(ct.get_props('keyw'))
-      if ch is not None :
-        yield ch
-  
-  def transform(self, schema, data):
-    r"""Transform the result set as determined by the GVizQL 
-    expression directives and clauses.
-    """
-    for ch in self.itereval():
-      schema, data = ch.transform(schema, data)
-    return schema, data
+    def transform(self, schema, data):
+        r"""Transform the result set as determined by the GVizQL 
+        expression directives and clauses.
+        """
+        for ch in self.itereval():
+            schema, data = ch.transform(schema, data)
+        return schema, data
 
 #------------------------------------------------------
 #   Helper functions
 #------------------------------------------------------
 
 def prepare_ql_data(provider, tq, req, **params):
-  r"""Prepare the data and schema to be supplied to an instance of 
-  gviz_api.DataTable as determined by a GVizQL expression. This is 
-  accomplished by wrapping the original data with multiple iterators.
-  
-  @param provider   an instance of `IGVizDataProvider` interface 
-                    responsible for providing the base result set 
-                    (i.e. primary information) subsequently modified 
-                    by the application of the GVizQL expression.
-  @param tq         the GVizQL expression.
-  @param req        an object encapsulating the HTTP request/response 
-                    pair.
-  @param params     further parameters supported by `provider`.
-  @return           a binary tuple. The first item is the schema 
-                    describing the column names, data types, and 
-                    labels, as well as data structures from where 
-                    data will be retrieved. The second is the actual 
-                    data resulting from the application of the query 
-                    (quite often in the form of iterators).
-  """
-  sch = provider.get_data_schema.im_func.func_code
-  sch_args = (sch.co_argcount > 1) and (req,) or ()
-  if not tq:
-    sch = provider.get_data_schema(*sch_args)
-    data = provider.get_data(req, None, **params)
-    return (sch, data)
-  else:
-    tq = GVizQLParser().parse(tq)
-    data = provider.get_data(req, tq, **params)
-    sch = provider.get_data_schema(*sch_args)
-    return tq.transform(sch, data)
+    r"""Prepare the data and schema to be supplied to an instance of 
+    gviz_api.DataTable as determined by a GVizQL expression. This is 
+    accomplished by wrapping the original data with multiple iterators.
+    
+    @param provider   an instance of `IGVizDataProvider` interface 
+                                  responsible for providing the base result set 
+                                  (i.e. primary information) subsequently modified 
+                                  by the application of the GVizQL expression.
+    @param tq         the GVizQL expression.
+    @param req        an object encapsulating the HTTP request/response 
+                                  pair.
+    @param params     further parameters supported by `provider`.
+    @return           a binary tuple. The first item is the schema 
+                      describing the column names, data types, and 
+                      labels, as well as data structures from where 
+                      data will be retrieved. The second is the actual 
+                      data resulting from the application of the query 
+                      (quite often in the form of iterators).
+    """
+    sch = provider.get_data_schema.im_func.func_code
+    sch_args = (sch.co_argcount > 1) and (req,) or ()
+    if not tq:
+        sch = provider.get_data_schema(*sch_args)
+        data = provider.get_data(req, None, **params)
+        return (sch, data)
+    else:
+        tq = GVizQLParser().parse(tq)
+        data = provider.get_data(req, tq, **params)
+        sch = provider.get_data_schema(*sch_args)
+        return tq.transform(sch, data)
 
-#------------------------------------------------------
-#   Global Testing
-#------------------------------------------------------
-
-from testing.test_gvizql import __test__, TEST_DATA_GVIZSCHEMA, \
-                            TEST_DATA_GVIZDATA
-def test_suite():
-  from doctest import ELLIPSIS, NORMALIZE_WHITESPACE, REPORT_UDIFF
-  from unittest import defaultTestLoader
-  from string import whitespace
-  import sys
-  
-  from api import GVizInvalidQuery
-  from dutest import MultiTestLoader, DocTestLoader
-  
-  def parse(expr, *attrs, **kwds):
-    # Test lexical analysis
-    print "*****\n* Tokens\n*****"
-    newline = False
-    p = GVizQLParser()
-    p.noisy = False
-    for tkn, val in p.get_tokens(expr):
-      if tkn is not Whitespace:
-        if newline:
-          print
-        else:
-          newline = True
-        print tkn, val,
-    if not (tkn is Whitespace and val == '\n'):  # Check for EOL token.
-      raise AssertionError('Expected : %s , %r \n\nGot : %s , %r' % \
-                                  (Whitespace, u'\n', tkn, val))
-    if attrs :
-      # Test parsing and compilation
-      print
-      print "*****\n* Parsing\n*****",
-      from api import GVizUnsupportedQueryOp
-      try:
-        expr = p.parse(expr)
-      except GVizUnsupportedQueryOp :
-        print '\nNotSupported  :(',
-      except GVizInvalidQuery, exc :
-        print 
-        print exc.message
-      else:
-        for attrnm in attrs :
-          print
-          print getattr(expr, attrnm),
-        # Test evaluation and transformations on result sets
-        print
-        print "*****\n* Result\n*****",
-        try:
-          schema, data = expr.transform(TEST_DATA_GVIZSCHEMA, \
-                                        TEST_DATA_GVIZDATA)
-        except Exception, exc:
-          print
-          print exc.__class__.__name__, ' : ', exc.message,
-        else:
-          print
-          print "= Columns =",
-          if isinstance(schema, dict):
-            def iter_schema():
-              return ((k,) + v for k, v in schema.iteritems())
-          else:
-            iter_schema = lambda : schema
-          for col in iter_schema() :
-            print
-            print col[0], col[1],
-            try :
-              print col[2],
-            except IndexError :
-              pass
-          print 
-          for row in data :
-            print "= Row ="
-            for val, col in izip(row, iter_schema()):
-              print '  ', col[0], '=', val
-  
-  l = MultiTestLoader([defaultTestLoader, \
-                        DocTestLoader(
-                            extraglobs=dict(parse=parse),
-                            optionflags=ELLIPSIS \
-                                        | NORMALIZE_WHITESPACE \
-                                        | REPORT_UDIFF \
-                          )])
-  return l.loadTestsFromModule(sys.modules[__name__])
-

File trac-dev/gviz/tracgviz/proto.py

 from trac.resource import ResourceNotFound
 from trac.ticket.query import QuerySyntaxError
 
-from api import IGVizProtocolHandler
-from util import BaseGVizHandler, send_response
+from tracgviz.api import IGVizProtocolHandler
+from tracgviz.util import BaseGVizHandler, send_response
 
 # GViz-specific exceptions
-from api import GVizDataNotModifiedError, GVizNotSupportedError, \
+from tracgviz.api import GVizDataNotModifiedError, GVizNotSupportedError, \
                 GVizUnknownProviderError, GVizDataNotModifiedError, \
                 GVizBadRequestError, GVizNotAuthenticatedError, \
                 GVizInvalidConfigError
             contents = e.stream_contents(_table)
             if out == 'json':
                 if self.hash_obj is None and self._hlib is not None :
-                  self.hash_obj = self._hlib.new_hash_obj(self.hash_name)
+                    self.hash_obj = self._hlib.new_hash_obj(self.hash_name)
                 if self.hash_obj is not None:
-                  hash_obj = self.hash_obj.copy()
-                  hash_obj.update(contents)
-                  hash_str = hash_obj.hexdigest()
-                  if sig is not None and hash_str == sig:
-                    exc = GVizDataNotModifiedError('')
-                    self._error(_req, exc, reqId, version, responseHandler)
-                  hash_str = "'sig':'%s'," % (hash_str,)
+                    hash_obj = self.hash_obj.copy()
+                    hash_obj.update(contents)
+                    hash_str = hash_obj.hexdigest()
+                    if sig is not None and hash_str == sig:
+                        exc = GVizDataNotModifiedError('')
+                        self._error(_req, exc, reqId, version, responseHandler)
+                    hash_str = "'sig':'%s'," % (hash_str,)
                 else:
                     hash_str = ""
                 contents = "%(h)s({'version':'%(v)s', " \
         else:
             exc = GVizNotSupportedError('Invalid format id %s' % (out,))
             self._error(_req, exc, reqId, version, responseHandler)
+

File trac-dev/gviz/tracgviz/rpc.py

 from urlparse import urlunparse, urlparse
 import xmlrpclib
 
-from util import get_column_desc, rpc_to_datetime, rpc_opt_sigs, \
-                  compile_pattern
+from tracgviz.util import get_column_desc, rpc_to_datetime, rpc_opt_sigs, \
+                          compile_pattern
 
 __metaclass__ = type
 
               timeline.ticket_show_details option in trac.ini to true.
         """
         if filters is not None :
-          if not filters:
-            # No filter selected
-            return []
-          # Filter unwanted filters ... 8$
-          filters = [f for f, _ in self.getEventFilters(req) \
-                      if f in filters]
-          if not filters:
-            # No filter selected
-            return []
+            if not filters:
+                # No filter selected
+                return []
+            # Filter unwanted filters ... 8$
+            filters = [f for f, _ in self.getEventFilters(req) \
+                       if f in filters]
+            if not filters:
+                # No filter selected
+                return []
         else :
-          filters = list(f for f, _ in self.getEventFilters(req))
+            filters = list(f for f, _ in self.getEventFilters(req))
         
         if start is None:
             start = _epoc
     implements(IXMLRPCHandler)
     
     def __init__(self):
-      self.repmdl = ReportModule(self.env)
+        self.repmdl = ReportModule(self.env)
     
     # IXMLRPCHandler methods
     def xmlrpc_namespace(self):
         sql = ("SELECT id FROM report ORDER BY id")
         cursor = db.cursor()
         try:
-          cursor.execute(sql)
-          result = cursor.fetchall() or []
-          return (x[0] for x in result)
+            cursor.execute(sql)
+            result = cursor.fetchall() or []
+            return (x[0] for x in result)
         finally:
-          cursor.close()
+            cursor.close()
     
     def get(self, req, id):
         r"""Return information about an specific report as a dict 
                           request. Otherwise it is empty.
         """
         if 'REPORT_SQL_VIEW' in req.perm:
-          sql = "SELECT id,title,query,description from report " \
+            sql = "SELECT id,title,query,description from report " \
                    "WHERE id=%s" % (id,)
         else :
-          sql = "SELECT id,title,NULL,description from report " \
+            sql = "SELECT id,title,NULL,description from report " \
                    "WHERE id=%s" % (id,)
         db = self.env.get_db_cnx()
         cursor = db.cursor()
         try:
-          cursor.execute(sql)
-          for report_info in cursor:
-              return dict(zip(['id','title','query','description'], report_info))
-          else:
-              return None
+            cursor.execute(sql)
+            for report_info in cursor:
+                return dict(zip(['id','title','query','description'],
+                            report_info))
+            else:
+                return None
         finally:
-          cursor.close()
+            cursor.close()
     
     def _execute_sql(self, req, id, sql, limit=0):
         r"""Execute a SQL report and return no more than `limit` rows 
         repmdl = self.repmdl
         db = self.env.get_db_cnx()
         try:
-          args = repmdl.get_var_args(req)
+            args = repmdl.get_var_args(req)
         except ValueError,e:
-          raise ValueError(_('Report failed: %(error)s', error=e))
+            raise ValueError(_('Report failed: %(error)s', error=e))
         try:
             try:
-              # Paginated exec (>=0.11)
-              exec_proc = repmdl.execute_paginated_report
-              kwargs = dict(limit=limit)
+                # Paginated exec (>=0.11)
+                exec_proc = repmdl.execute_paginated_report
+                kwargs = dict(limit=limit)
             except AttributeError:
-              # Legacy exec (<=0.10)
-              exec_proc = repmdl.execute_report
-              kwargs = {}
+                # Legacy exec (<=0.10)
+                exec_proc = repmdl.execute_report
+                kwargs = {}
             return exec_proc(req, db, id, sql, args, **kwargs)[:2]
         except Exception, e:
             db.rollback()
         sql = self.get(req, id)['query']
         query = ''.join([line.strip() for line in sql.splitlines()])
         if query and (query[0] == '?' or query.startswith('query:?')):
-          raise NotImplementedError('Saved custom queries specified ' \
-                                  'using URLs are not supported.')
+            raise NotImplementedError('Saved custom queries specified ' \
+                                      'using URLs are not supported.')
         elif query.startswith('query:'):
-          query = Query.from_string(self.env, query[6:], report=id)
-          server_url = urlparse(req.base_url)
-          server_href = Href(urlunparse((server_url.scheme, \
-                                        server_url.netloc, \
-                                        '', '', '', '')))
-          def rel2abs(row):
-            """Turn relative value in 'href' into absolute URLs."""
-            self.log.debug('IG: Query Row %s', row)
-            url = row['href']
-            urlobj = urlparse(url)
-            if not urlobj.netloc:
-              row['href'] = server_href(url)
-            return row
+            query = Query.from_string(self.env, query[6:], report=id)
+            server_url = urlparse(req.base_url)
+            server_href = Href(urlunparse((server_url.scheme, \
+                                           server_url.netloc, \
+                                           '', '', '', '')))
+            def rel2abs(row):
+                """Turn relative value in 'href' into absolute URLs."""
+                self.log.debug('IG: Query Row %s', row)
+                url = row['href']
+                urlobj = urlparse(url)
+                if not urlobj.netloc:
+                    row['href'] = server_href(url)
+                return row
             
-          return imap(rel2abs, query.execute(req))
+            return imap(rel2abs, query.execute(req))
         else:
-          cols, results = self._execute_sql(req, id, sql)
-          return (dict(zip(cols, list(row))) for row in results)
+            cols, results = self._execute_sql(req, id, sql)
+            return (dict(zip(cols, list(row))) for row in results)
     
     def _sql_cursor(self, req, id, sql, args, limit=0, offset=0):
       r"""Retrieve a cursor to access the data returned by a SQL 
       repmdl = self.repmdl
       sql, args, missing_args = repmdl.sql_sub_vars(sql, args)
       if missing_args:
-        self.log.warning('The following arguments are missing: %s',
-            ", ".join(missing_args))
+          self.log.warning('The following arguments are missing: %s',
+                           ", ".join(missing_args))
 
       if not sql:
           raise ValueError(_('Report %(num)s has no SQL query.', num=id))
       self.log.debug('IG: Request args: %r' % req.args)
 
       with self.env.db_query as db:
-        cursor = db.cursor()
+          cursor = db.cursor()
         
-        # The column name is obtained.
-        get_col_name_sql = 'SELECT * FROM ( ' + sql + ' ) AS tab LIMIT 1'
-        cursor.execute(get_col_name_sql, args)
-        self.env.log.debug("IG: Query SQL(Get col names): " + get_col_name_sql)
-        return cursor
+          # The column name is obtained.
+          get_col_name_sql = 'SELECT * FROM ( ' + sql + ' ) AS tab LIMIT 1'
+          cursor.execute(get_col_name_sql, args)
+          self.env.log.debug("IG: Query SQL(Get col names): " + get_col_name_sql)
+          return cursor
     
     def _sql_columns(self, req, id, sql):
       r"""Retrieve the description of columns returned by a SQL 
       """
       repmdl = self.repmdl
       try:
-        args = repmdl.get_var_args(req)
+          args = repmdl.get_var_args(req)
       except ValueError,e:
-        raise ValueError(_('Report failed: %(error)s', error=e))
+          raise ValueError(_('Report failed: %(error)s', error=e))
       cursor = self._sql_cursor(req, id, sql, args)
       self.log.debug('IG: Cursor desc %s', cursor.description)
       cols = list(get_column_desc(cursor, True))
         sql = self.get(req, id)['query']
         query = ''.join([line.strip() for line in sql.splitlines()])
         if query and (query[0] == '?' or query.startswith('query:?')):
-          raise NotImplementedError('Saved custom queries specified ' \
-                                  'using URLs are not supported.')
+            raise NotImplementedError('Saved custom queries specified ' \
+                                      'using URLs are not supported.')
         elif query.startswith('query:'):
-          query = Query.from_string(self.env, query[6:], report=id)
-          fields = query.fields
-          return [(f['name'], 'string', _(f['label'])) for f in fields] + \
-                  [
-                      ('href', 'string', _('URL')), \
-                      ('id', 'number', _('Ticket')), \
-                  ]
+            query = Query.from_string(self.env, query[6:], report=id)
+            fields = query.fields
+            return [(f['name'], 'string', _(f['label'])) for f in fields] + \
+                    [
+                        ('href', 'string', _('URL')), \
+                        ('id', 'number', _('Ticket')), \
+                    ]
         else:
-          return self._sql_columns(req, id, sql)
+            return self._sql_columns(req, id, sql)
     
     def create(self, req, summary, description, query):
         r"""Create a custom report.
         db = self.env.get_db_cnx()
         cursor = db.cursor()
         try :
-          cursor.execute("INSERT INTO report (title,query,description) " \
-                         "VALUES (%s,%s,%s)", \
+            cursor.execute("INSERT INTO report (title,query,description) " \
+                           "VALUES (%s,%s,%s)", \
                                       (summary, query, description))
-          rid = db.get_last_id(cursor, 'report')
-          db.commit()
-          return rid
+            rid = db.get_last_id(cursor, 'report')
+            db.commit()
+            return rid
         finally :
-          cursor.close()
+            cursor.close()
 
 #--------------------------------------------------
 #   Version Control RPC
 #--------------------------------------------------
 
 def _normalize_timestamp(repos, req, timestamp, default=None):
-  r"""Normalize datetime and revision numbers. Return only 
-  datetime values.
-  """
-  if isinstance(timestamp, datetime):
-    return timestamp
-  elif isinstance(timestamp, xmlrpclib.DateTime):
-    return rpc_to_datetime(timestamp, req)
-  elif isinstance(default, (datetime, date, time)): # Return default
-    return default
-  else:
-    return datetime.now(req.tz)
+    r"""Normalize datetime and revision numbers. Return only 
+    datetime values.
+    """
+    if isinstance(timestamp, datetime):
+        return timestamp
+    elif isinstance(timestamp, xmlrpclib.DateTime):
+        return rpc_to_datetime(timestamp, req)
+    elif isinstance(default, (datetime, date, time)): # Return default
+        return default
+    else:
+        return datetime.now(req.tz)
 
 def _filter_revs(seq, repos, req, start, stop, full, \
-                  accessor=None, log=None):
-  r"""Filter values in seq so that only references to revisions 
-  commited during a time interval be enumerated. 
-  Items are binary tuples of the form `(revision id, changeset object)`.
-  
-  @param seq        original sequence to be filtered.
-  @param repos      the repository managed by VCS.
-  @param req        object containing information about the user 
-                    requesting information in repository and more.
-  @param start      boundary value. Revisions older than 
-                    this value will not be retrieved. Dates 
-                    and revision numbers are both accepted.
-  @param stop       boundary value. Younger revisions 
-                    will not be retrieved. Dates 
-                    and revision numbers are both accepted.
-  @param full       retrieve also the changeset object for revision.
-  @param accesor    a function used to access the revision value 
-                    stored in each item of the input sequence.
-  @return           a sequence of tuples. The firts item is the 
-                    element of the original sequence for which 
-                    revision is in input time interval. The second is 
-                    the changeset object or None (depending upon the 
-                    value of `full` parameter)
-  """
-  if seq is not None:
-    seq = iter(seq)
-  if log is None:
-    class VoidLogger:
-      def __getattr__(self, attrnm):
-        return lambda *args, **kwds: None
-    log = VoidLogger()
-  DO_NOTHING, DO_RETURN, DO_YIELD = xrange(3)
-  load_chgset = True
-  if isinstance(start, types.StringTypes) and \
-      isinstance(stop, types.StringTypes):
-    load_chgset = False
-    if not repos.rev_older_than(stop, start):
-      def cond(rev, chgset):
-        if repos.rev_older_than(rev, start):
-          return DO_RETURN
-        elif repos.rev_older_than(stop, rev):
-          return DO_NOTHING
+                 accessor=None, log=None):
+    r"""Filter values in seq so that only references to revisions 
+    commited during a time interval be enumerated. 
+    Items are binary tuples of the form `(revision id, changeset object)`.
+    
+    @param seq        original sequence to be filtered.
+    @param repos      the repository managed by VCS.
+    @param req        object containing information about the user 
+                      requesting information in repository and more.
+    @param start      boundary value. Revisions older than 
+                      this value will not be retrieved. Dates 
+                      and revision numbers are both accepted.
+    @param stop       boundary value. Younger revisions 
+                      will not be retrieved. Dates 
+                      and revision numbers are both accepted.
+    @param full       retrieve also the changeset object for revision.
+    @param accesor    a function used to access the revision value 
+                      stored in each item of the input sequence.
+    @return           a sequence of tuples. The firts item is the 
+                      element of the original sequence for which 
+                      revision is in input time interval. The second is 
+                      the changeset object or None (depending upon the 
+                      value of `full` parameter)
+    """
+    if seq is not None:
+        seq = iter(seq)
+    if log is None:
+        class VoidLogger:
+            def __getattr__(self, attrnm):
+                return lambda *args, **kwds: None
+        log = VoidLogger()
+    DO_NOTHING, DO_RETURN, DO_YIELD = xrange(3)
+    load_chgset = True
+    if isinstance(start, types.StringTypes) and \
+            isinstance(stop, types.StringTypes):
+        load_chgset = False
+        if not repos.rev_older_than(stop, start):
+            def cond(rev, chgset):
+                if repos.rev_older_than(rev, start):
+                    return DO_RETURN
+                elif repos.rev_older_than(stop, rev):
+                    return DO_NOTHING