Commits

Kirill Simonov committed 2068658

Separated backend-specific code into different packages.

Comments (0)

Files changed (361)

 INCLUDE_PACKAGE_DATA = True
 ZIP_SAFE = False
 ENTRY_POINTS = {
-    'console_scripts': ['htsql-ctl = htsql_ctl:main'],
+    'console_scripts': ['htsql-ctl = htsql.ctl:main'],
     'htsql.addons': [
-        'htsql = htsql:HTSQLAddon',
-        'engine = htsql_engine:EngineAddon',
-        'engine.sqlite = htsql_engine.sqlite:EngineSQLiteAddon',
-        'engine.pgsql = htsql_engine.pgsql:EnginePGSQLAddon',
-        'engine.mysql = htsql_engine.mysql:EngineMySQLAddon',
-        'engine.oracle = htsql_engine.oracle:EngineOracleAddon',
-        'engine.mssql = htsql_engine.mssql:EngineMSSQLAddon',
-        'tweak = htsql_tweak:TweakAddon',
-        'tweak.autolimit = htsql_tweak.autolimit:TweakAutolimitAddon',
-        'tweak.cors = htsql_tweak.cors:TweakCORSAddon',
-        'tweak.hello = htsql_tweak.hello:TweakHelloAddon',
-        'tweak.django = htsql_tweak.django:TweakDjangoAddon',
-        'tweak.inet = htsql_tweak.inet:TweakINetAddon',
-        'tweak.inet.pgsql = htsql_tweak.inet.pgsql:TweakINetPGSQLAddon',
-        'tweak.meta = htsql_tweak.meta:TweakMetaAddon',
-        'tweak.meta.slave = htsql_tweak.meta.slave:TweakMetaSlaveAddon',
-        'tweak.override = htsql_tweak.override:TweakOverrideAddon',
-        'tweak.resource = htsql_tweak.resource:TweakResourceAddon',
-        'tweak.shell = htsql_tweak.shell:TweakShellAddon',
-        'tweak.shell.default = htsql_tweak.shell.default:TweakShellDefaultAddon',
-        'tweak.sqlalchemy = htsql_tweak.sqlalchemy:TweakSQLAlchemyAddon',
-        'tweak.system = htsql_tweak.system:TweakSystemAddon',
-        'tweak.system.pgsql = htsql_tweak.system.pgsql:TweakSystemPGSQLAddon',
-        'tweak.timeout = htsql_tweak.timeout:TweakTimeoutAddon',
+        'htsql = htsql.core:HTSQLAddon',
+        'engine = htsql.core:EngineAddon',
+        'engine.sqlite = htsql_sqlite.core:EngineSQLiteAddon',
+        'engine.pgsql = htsql_pgsql.core:EnginePGSQLAddon',
+        'engine.mysql = htsql_mysql.core:EngineMySQLAddon',
+        'engine.oracle = htsql_oracle.core:EngineOracleAddon',
+        'engine.mssql = htsql_mssql.core:EngineMSSQLAddon',
+        'tweak = htsql.tweak:TweakAddon',
+        'tweak.autolimit = htsql.tweak.autolimit:TweakAutolimitAddon',
+        'tweak.cors = htsql.tweak.cors:TweakCORSAddon',
+        'tweak.hello = htsql.tweak.hello:TweakHelloAddon',
+        'tweak.django = htsql.tweak.django:TweakDjangoAddon',
+        'tweak.inet = htsql.tweak.inet:TweakINetAddon',
+        'tweak.inet.pgsql = htsql_pgsql.tweak.inet:TweakINetPGSQLAddon',
+        'tweak.meta = htsql.tweak.meta:TweakMetaAddon',
+        'tweak.meta.slave = htsql.tweak.meta.slave:TweakMetaSlaveAddon',
+        'tweak.override = htsql.tweak.override:TweakOverrideAddon',
+        'tweak.resource = htsql.tweak.resource:TweakResourceAddon',
+        'tweak.shell = htsql.tweak.shell:TweakShellAddon',
+        'tweak.shell.default = htsql.tweak.shell.default:TweakShellDefaultAddon',
+        'tweak.sqlalchemy = htsql.tweak.sqlalchemy:TweakSQLAlchemyAddon',
+        'tweak.system = htsql.tweak.system:TweakSystemAddon',
+        'tweak.system.pgsql = htsql_pgsql.tweak.system:TweakSystemPGSQLAddon',
+        'tweak.timeout = htsql.tweak.timeout:TweakTimeoutAddon',
         'tweak.timeout.pgsql'
-            ' = htsql_tweak.timeout.pgsql:TweakTimeoutPGSQLAddon',
-        'tweak.view = htsql_tweak.view:TweakViewAddon',
-        'tweak.view.pgsql = htsql_tweak.view.pgsql:TweakViewPGSQLAddon',
+            ' = htsql_pgsql.tweak.timeout:TweakTimeoutPGSQLAddon',
+        'tweak.view = htsql.tweak.view:TweakViewAddon',
+        'tweak.view.pgsql = htsql_pgsql.tweak.view:TweakViewPGSQLAddon',
     ],
 }
 INSTALL_REQUIRES = [

src/htsql/__init__.py

-#
-# Copyright (c) 2006-2011, Prometheus Research, LLC
-# See `LICENSE` for license information, `AUTHORS` for the list of authors.
-#
-
-
-"""
-:mod:`htsql`
-============
-
-:copyright: 2006-2011, Prometheus Research, LLC
-:authors: Clark C. Evans <cce@clarkevans.com>,
-          Kirill Simonov <xi@resolvent.net>;
-          see ``AUTHORS`` file in the source distribution
-          for the full list of contributors
-:license: See ``LICENSE`` file in the source distribution
-
-This package provides HTSQL, a query language for the accidental programmer.
-
-HTSQL is implemented as a WSGI application.  To create an application, run::
-
-    >>> from htsql import HTSQL
-    >>> app = HTSQL(db)
-
-where `db` is a connection URI, a string of the form::
-
-    engine://username:password@host:port/database
-
-`engine`
-    The type of the database server; ``pgsql`` or ``sqlite``.
-
-`username:password`
-    Used for authentication; optional.
-
-`host:port`
-    The server address; optional.
-
-`database`
-    The name of the database; for SQLite, the path to the database file.
-
-To execute a WSGI request, run
-
-    >>> app(environ, start_response)
-"""
-
-
-__version__ = '2.3.0+'
-
-
-from . import (adapter, addon, application, cache, cmd, connect, context,
-               domain, entity, error, introspect, mark, split_sql,
-               tr, util, validator, wsgi)
-from .validator import DBVal
-from .addon import Addon, Parameter
-from .connect import ConnectionPool, connect, DBError
-from .introspect import introspect
-from .cache import GeneralCache
-
-from .application import Application as HTSQL
-
-
-class HTSQLAddon(Addon):
-    """
-    Declares the `htsql` addon.
-    """
-
-    name = 'htsql'
-    hint = """HTSQL translator and HTTP service"""
-    help = """
-    This extension implements the HTSQL translator and HTTP service.
-    It is included to every HTSQL application.
-
-    The parameter `DB` specifies parameters of the database connection;
-    it must have the form:
-
-        ENGINE://USERNAME:PASSWORD@HOST:PORT/DATABASE
-
-    Here,
-
-      - ENGINE is the type of the database server; possible values
-        are `sqlite`, `pgsql`, `mysql`, `oracle` or `mssql`.
-      - USERNAME:PASSWORD are used for authentication to the database
-        server.
-      - HOST:PORT is the address of the database server.
-      - DATABASE is the name of the database, or, for file-based
-        backends, the path to the file containing the database.
-    """
-
-    parameters = [
-            Parameter('db', DBVal(),
-                      value_name="""engine:database""",
-                      hint="""the connection URI"""),
-    ]
-
-    packages = ['.', '.cmd', '.fmt', '.tr', '.tr.fn']
-    prerequisites = []
-    postrequisites = ['engine']
-
-    def __init__(self, app, attributes):
-        super(HTSQLAddon, self).__init__(app, attributes)
-        self.cache = GeneralCache()
-        self.pool = ConnectionPool()
-
-    def validate(self):
-        if self.db is None:
-            raise ValueError("database address is not specified")
-        try:
-            connect().release()
-        except DBError, exc:
-            raise ValueError("failed to establish database connection: %s"
-                             % exc)
-        try:
-            introspect()
-        except DBError, exc:
-            raise ValueError("failed to introspect the database: %s" % exc)
-
-

src/htsql/adapter.py

-#
-# Copyright (c) 2006-2011, Prometheus Research, LLC
-# See `LICENSE` for license information, `AUTHORS` for the list of authors.
-#
-
-
-"""
-:mod:`htsql.adapter`
-====================
-
-This module provides a mechanism for pluggable extensions.
-"""
-
-
-from .util import listof, aresubclasses, toposort
-from .context import context
-import sys
-import types
-
-
-class Component(object):
-    """
-    A unit of extension in the HTSQL component architecture.
-
-    *HTSQL component architecture* allows you to:
-
-    - declare *interfaces* that provide various services;
-
-    - define *components* implementing the interfaces;
-
-    - given an interface and a *dispatch key*, produce a component which
-      implements the interface for the given key.
-
-    Three types of interfaces are supported: *utilities*, *adapters* and
-    *protocols*; see :class:`Utility`, :class:`Adapter`, :class:`Protocol`
-    respectively.
-    """
-
-    # Augment method names with prefix `<name>.` to make the adapter
-    # name visible in tracebacks.
-    class __metaclass__(type):
-
-        def __new__(mcls, name, bases, content):
-            # Iterate over all values in the class namespace.
-            for value in content.values():
-                # Ignore non-function attributes.
-                if not isinstance(value, types.FunctionType):
-                    continue
-                # Update the code name and regenerate the code object.
-                code = value.func_code
-                code_name = code.co_name
-                if '.' in code_name:
-                    continue
-                code_name = '%s.%s' % (name, code_name)
-                code = types.CodeType(code.co_argcount, code.co_nlocals,
-                                      code.co_stacksize, code.co_flags,
-                                      code.co_code, code.co_consts,
-                                      code.co_names, code.co_varnames,
-                                      code.co_filename, code_name,
-                                      code.co_firstlineno, code.co_lnotab,
-                                      code.co_freevars, code.co_cellvars)
-                # Patch the function object.
-                value.func_code = code
-            # Create the class.
-            return type.__new__(mcls, name, bases, content)
-
-    @staticmethod
-    def components():
-        """
-        Produce a list of all components of the active application.
-        """
-        # Get the component registry of the active application.
-        registry = context.app.component_registry
-        # A shortcut: return cached components.
-        if registry.components is not None:
-            return registry.components
-        # A list of `Component` subclasses defined in modules exported by addons.
-        components = [Component]
-        idx = 0
-        while idx < len(components):
-            for subclass in components[idx].__subclasses__():
-                # Skip realizations.
-                if issubclass(subclass, Realization):
-                    continue
-                # Check if the component belongs to the current application.
-                if subclass.active():
-                    components.append(subclass)
-            idx += 1
-        # Cache and return the components.
-        registry.components = components
-        return components
-
-    @classmethod
-    def implementations(interface):
-        """
-        Produces a list of all components implementing the interface.
-        """
-        # Get the component registry of the active application.
-        registry = context.app.component_registry
-        # A shortcut: return cached implementations.
-        try:
-            return registry.implementations[interface]
-        except KeyError:
-            pass
-        # Get all active components.
-        components = interface.components()
-        # Leave only components implementing the interface.
-        implementations = [component
-                           for component in components
-                           if component.implements(interface)]
-        # Cache and return the implementations.
-        registry.implementations[interface] = implementations
-        return implementations
-
-    @classmethod
-    def realize(interface, dispatch_key):
-        """
-        Produces a realization of the interface for the given dispatch key.
-        """
-        # Get the component registry of the active application.
-        registry = context.app.component_registry
-
-        # A shortcut: if the realization for the given interface and the
-        # dispatch key is already built, return it.
-        try:
-            return registry.realizations[interface, dispatch_key]
-        except KeyError:
-            pass
-
-        # Get the implementations of the interface.
-        implementations = interface.implementations()
-        # Leave only implementations matching the dispatch key.
-        implementations = [implementation
-                           for implementation in implementations
-                           if implementation.matches(dispatch_key)]
-        # Note: commented out since we force the interface component
-        # to match any dispatch keys.
-        ## Check that we have at least one matching implementation.
-        #if not implementations:
-        #    raise RuntimeError("when realizing interface %s.%s for key %r,"
-        #                       " unable to find matching implementations"
-        #                       % (interface.__module__, interface.__name__,
-        #                          dispatch_key))
-
-        # Generate a function:
-        # order(implementation) -> [dominated implementations].
-        order_graph = {}
-        for dominating in implementations:
-            order_graph[dominating] = []
-            for dominated in implementations:
-                if dominating is dominated:
-                    continue
-                if dominating.dominates(dominated):
-                    order_graph[dominating].append(dominated)
-        order = (lambda implementation: order_graph[implementation])
-
-        # Now we need to order the implementations unambiguously.
-        try:
-            implementations = toposort(implementations, order, is_total=True)
-        except RuntimeError, exc:
-            # We intercept exceptions to provide a nicer error message.
-            # `message` is an explanation we discard; `conflict` is a list
-            # of implementations which either form a domination loop or
-            # have no ordering relation between them.
-            message, conflict = exc
-            interface_name = "%s.%s" % (interface.__module__,
-                                        interface.__name__)
-            component_names = ", ".join("%s.%s" % (component.__module__,
-                                                   component.__name__)
-                                        for component in conflict)
-            if conflict[0] is conflict[-1]:
-                problem = "an ordering loop"
-            else:
-                problem = "ambiguous ordering"
-            # Report a problem.
-            raise RuntimeError("when realizing interface %s for key %r,"
-                               " detected %s in components: %s"
-                               % (interface_name, dispatch_key,
-                                  problem, component_names))
-
-        # We want the most specific implementations first.
-        implementations.reverse()
-
-        # Force the interface component to the list of implementations.
-        if interface not in implementations:
-            implementations.append(interface)
-
-        # Generate the name of the realization of the form:
-        #   interface[implementation1,implementation2,...]
-        module = interface.__module__
-        name = "%s[%s]" % (interface.__name__,
-                           ",".join("%s.%s" % (component.__module__,
-                                               component.__name__)
-                                    for component in implementations
-                                    if component is not interface))
-        # Get the list of bases for the realization.
-        bases = tuple([Realization] + implementations)
-        # Class attributes for the realization.
-        attributes = {
-                '__module__': module,
-                'interface': interface,
-                'dispatch_key': dispatch_key,
-        }
-        # Generate the realization.
-        realization = type(name, bases, attributes)
-
-        # Cache and return the realization.
-        registry.realizations[interface, dispatch_key] = realization
-        return realization
-
-    @classmethod
-    def active(component):
-        """
-        Tests if the component is a part of the current application.
-        """
-        registry = context.app.component_registry
-        return (component.__module__ in registry.modules)
-
-    @classmethod
-    def implements(component, interface):
-        """
-        Tests if the component implements the interface.
-        """
-        return issubclass(component, interface)
-
-    @classmethod
-    def dominates(component, other):
-        """
-        Tests if the component dominates another component.
-        """
-        # Refine in subclasses.
-        return issubclass(component, other)
-
-    @classmethod
-    def matches(component, dispatch_key):
-        """
-        Tests if the component matches a dispatch key.
-        """
-        # Override in subclasses.
-        return False
-
-    @classmethod
-    def dispatch(interface, *args, **kwds):
-        """
-        Extract the dispatch key from the constructor arguments.
-        """
-        # Override in subclasses.
-        return None
-
-    def __new__(interface, *args, **kwds):
-        # Extract polymorphic parameters.
-        dispatch_key = interface.dispatch(*args, **kwds)
-        # Realize the interface.
-        realization = interface.realize(dispatch_key)
-        # Create an instance of the realization.
-        return super(Component, realization).__new__(realization)
-
-
-class Realization(Component):
-    """
-    A realization of an interface for some dispatch key.
-    """
-
-    interface = None
-    dispatch_key = None
-
-    def __new__(cls, *args, **kwds):
-        # Bypass `Component.__new__`.
-        return object.__new__(cls)
-
-
-class Utility(Component):
-    """
-    Implements utility interfaces.
-
-    An utility is an interface with a single realization.
-
-    This is an abstract class; to declare an utility interface, create
-    a subclass of :class:`Utility`.  To add an implementation of the
-    interface, create a subclass of the interface class.
-
-    The following example declared an interface ``SayHello`` and provide
-    an implementation ``PrintHello`` that prints ``'Hello, World!`` to
-    the standard output::
-
-        class SayHello(Utility):
-            def __call__(self):
-                raise NotImplementedError("interface is not implemented")
-
-        class PrintHello(SayHello):
-            def __call__(self):
-                print "Hello, World!"
-
-        def hello():
-            hello = SayHello()
-            hello()
-
-        >>> hello()
-        Hello, World!
-    """
-
-    weight = 0.0
-
-    @classmethod
-    def dominates(component, other):
-        if issubclass(component, other):
-            return True
-        if component.weight > other.weight:
-            return True
-        return False
-
-    @classmethod
-    def matches(component, dispatch_key):
-        # For an utility, the dispatch key is always a 0-tuple.
-        assert dispatch_key == ()
-        return True
-
-    @classmethod
-    def dispatch(interface, *args, **kwds):
-        # The dispatch key is always a 0-tuple.
-        return ()
-
-
-def weigh(value):
-    assert isinstance(value, (int, float))
-    frame = sys._getframe(1)
-    frame.f_locals['weight'] = value
-
-
-class Adapter(Component):
-    """
-    Implements adapter interfaces.
-
-    An adapter interface provides mechanism for polymorphic dispatch
-    based on the types of the arguments.
-
-    This is an abstract class; to declare an adapter interface, create
-    a subclass of :class:`Adapter` and indicate the most generic type
-    signature of the polymorphic arguments using function :func:`adapts`.
-
-    To add an implementation of an adapter interface, create a subclass
-    of the interface class and indicate the matching type signatures
-    using functions :func:`adapts`, :func:`adapts_many`, or
-    :func:`adapts_none`.
-
-    Class attributes:
-
-    `types` (a list of type signatures)
-        List of signatures that the component matches.
-    
-    `arity` (an integer)
-        Number of polymorphic arguments.
-
-    The following example declares an adapter interface ``Format``
-    and implements it for several data types::
-
-        class Format(Adapter):
-            adapts(object)
-            def __init__(self, value):
-                self.value = value
-            def __call__(self):
-                # The default implementation.
-                return str(self.value)
-
-        class FormatString(Format):
-            adapts(str)
-            def __call__(self):
-                # Display alphanumeric values unquoted, the others quoted.
-                if self.value.isalnum():
-                    return self.value
-                else:
-                    return repr(self.value)
-
-        class FormatList(Format):
-            adapts(list)
-            def __call__(self):
-                # Apply `format` to the list elements.
-                return "[%s]" % ",".join(format(item) for item in self.value)
-
-        def format(value):
-            format = Format(value)
-            return format()
-
-        >>> print format(123)
-        123
-        >>> print format("ABC")
-        ABC
-        >>> print format("Hello, World!")
-        'Hello, World!'
-        >>> print format([123, "ABC", "Hello, World!"])
-        [123, ABC, 'Hello, World!']
-    """
-
-    types = []
-    arity = 0
-
-    @classmethod
-    def dominates(component, other):
-        # A component implementing an adapter interface dominates
-        # over another component implementing the same interface
-        # if one of the following two conditions holds:
-        
-        # (1) The component is a subclass of the other component.
-        if issubclass(component, other):
-            return True
-
-        # (2) The signature of the component is more specific than
-        #     the signature of the other component.
-        # Note: In case if the component has more than one signature,
-        # we require that at least one of the signatures is more
-        # specific than some signature of the other component.  This
-        # rule does not guarantee anti-symmetricity, so ambiguously
-        # defined implementations may make the ordering ill defined.
-        # Validness of the ordering is verified in `Component.realize()`.
-        for type_vector in component.types:
-            for other_type_vector in other.types:
-                if aresubclasses(type_vector, other_type_vector):
-                    if type_vector != other_type_vector:
-                        return True
-
-        return False
-
-    @classmethod
-    def matches(component, dispatch_key):
-        # For an adapter interface, the dispatch key is a signature.
-        # A component matches the dispatch key the component signature
-        # is equal or less specific than the dispatch key.
-        # Note: if the component has more than one signature, it
-        # matches the dispatch key if at least one of its signatures
-        # is equal or less specific than the dispatch key.
-        assert isinstance(list(dispatch_key), listof(type))
-        return any(aresubclasses(dispatch_key, type_vector)
-                   for type_vector in component.types)
-
-    @classmethod
-    def dispatch(interface, *args, **kwds):
-        # The types of the leading arguments of the constructor
-        # form a dispatch key.
-        assert interface.arity <= len(args)
-        type_vector = tuple(type(arg) for arg in args[:interface.arity])
-        return type_vector
-
-
-def adapts(*type_vector):
-    """
-    Specifies the adapter signature.
-
-    The component matches the specified or any more specific
-    signature.
-
-    Use it in the namespace of the component, for example::
-
-        class DoSmth(Adapter):
-
-            adapts(T1, T2, ...)
-    """
-    assert isinstance(list(type_vector), listof(type))
-    frame = sys._getframe(1)
-    frame.f_locals['types'] = [type_vector]
-    frame.f_locals['arity'] = len(type_vector)
-
-
-def adapts_none():
-    """
-    Indicates that the adapter does not match any signatures.
-
-    Use it in the namespace of the adapter, for example::
-
-        class DoSmth(Adapter):
-
-            adapts_none()
-    """
-    frame = sys._getframe(1)
-    frame.f_locals['types'] = []
-
-
-def adapts_many(*type_vectors):
-    """
-    Specifies signatures of the adapter.
-
-    The component matches any of the specified signatures as well
-    all more specific signatures.
-
-    Use it in the namespace of the adapter, for example::
-
-        class DoSmth(Adapter):
-
-            adapts_many((T11, T12, ...),
-                        (T21, T22, ...),
-                        ...)
-    """
-    # Normalize the given type vectors.
-    type_vectors = [type_vector if isinstance(type_vector, tuple)
-                                else (type_vector,)
-                  for type_vector in type_vectors]
-    assert len(type_vectors) > 0
-    arity = len(type_vectors[0])
-    assert all(len(type_vector) == arity
-               for type_vector in type_vectors)
-    frame = sys._getframe(1)
-    frame.f_locals['types'] = type_vectors
-    frame.f_locals['arity'] = arity
-
-
-class Protocol(Component):
-    """
-    Implements protocol interfaces.
-
-    A protocol interface provides mechanism for name-based dispatch.
-
-    This is an abstract class; to declare a protocol interface, create
-    a subclass of :class:`Protocol`.
-
-    To add an implementation of a protocol interface, create a subclass
-    of the interface class and specify its name using function :func:`named`.
-
-    Class attributes:
-
-    `names` (a list of strings)
-        List of names that the component matches.
-
-    The following example declares a protocol interface ``Weigh``
-    and adds several implementations::
-
-        class Weigh(Protocol):
-            def __init__(self, name):
-                self.name = name
-            def __call__(self):
-                # The default implementation.
-                return -1
-
-        class WeighAlice(Weigh):
-            named("Alice")
-            def __call__(self):
-                return 150
-
-        class WeighBob(Weigh):
-            named("Bob")
-            def __call__(self):
-                return 160
-
-        def weigh(name):
-            weigh = Weigh(name)
-            return weigh()
-
-        >>> weigh("Alice")
-        150
-        >>> weigh("Bob")
-        160
-        >>> weigh("Clark")
-        -1
-    """
-
-    names = []
-
-    @classmethod
-    def dispatch(interface, name, *args, **kwds):
-        # The first argument of the constructor is the protocol name.
-        return name
-
-    @classmethod
-    def matches(component, dispatch_key):
-        # The dispatch key is the protocol name.
-        assert isinstance(dispatch_key, str)
-        return (dispatch_key in component.names)
-
-
-def named(*names):
-    """
-    Specifies the names of the protocol.
-
-    Use it in the namespace of the protocol, for example::
-
-        class DoSmth(Protocol):
-
-            named("...")
-    """
-    frame = sys._getframe(1)
-    frame.f_locals['names'] = list(names)
-
-
-class ComponentRegistry(object):
-    """
-    Contains cached components and realizations.
-    """
-
-    def __init__(self, addons):
-        # Packages exported by addons.
-        packages = set()
-        for addon in addons:
-            # In Python 2.6+:
-            # root_package = sys.modules[addon.__module__].__package__
-            root_package = addon.__module__
-            if not hasattr(sys.modules[root_package], '__path__'):
-                root_package = root_package.rsplit('.', 1)[0]
-            # An addon exports packages defined in `packages` attribute.
-            for package in addon.packages:
-                # Resolve relative package names.
-                if package == '.':
-                    package = root_package
-                elif package.startswith('.'):
-                    package = root_package+package
-                packages.add(package)
-        # All modules exported by the addons.
-        modules = set()
-        for module in sorted(sys.modules):
-            # In Python 2.6+:
-            # package = sys.modules[module].__package__
-            package = module
-            if not hasattr(sys.modules[package], '__path__'):
-                package = package.rsplit('.', 1)[0]
-            if package in packages:
-                modules.add(module)
-        self.modules = modules
-        # List of active components (populated by `Component.components()`).
-        self.components = None
-        # A mapping: interface -> [components]  (populated by
-        # `Component.implementations()`).
-        self.implementations = {}
-        # A mapping: (interface, dispatch_key) -> realization (populated by
-        # `Component.realize()`).
-        self.realizations = {}
-
-

src/htsql/addon.py

-#
-# Copyright (c) 2006-2011, Prometheus Research, LLC
-# See `LICENSE` for license information, `AUTHORS` for the list of authors.
-#
-
-
-"""
-:mod:`htsql.addon`
-==================
-
-This module declares HTSQL addons.
-"""
-
-
-from __future__ import with_statement
-from .util import maybe, trim_doc
-from .validator import Validator
-import re
-import threading
-import pkg_resources
-
-
-class GlobalAddonRegistry(object):
-
-    global_state = {}
-
-    def __init__(self):
-        self.__dict__ = self.global_state
-        if self.global_state:
-            return
-        self.entry_by_name = None
-        self.lock = threading.Lock()
-
-    def refresh(self):
-        if self.entry_by_name is None:
-            with self.lock:
-                if self.entry_by_name is not None:
-                    return
-                self.entry_by_name = {}
-                for entry in pkg_resources.iter_entry_points('htsql.addons'):
-                    if entry.name in self.entry_by_name:
-                        continue
-                    self.entry_by_name[entry.name] = entry
-
-    def __iter__(self):
-        self.refresh()
-        return iter(sorted(self.entry_by_name))
-
-    def __contains__(self, name):
-        self.refresh()
-        return (name in self.entry_by_name)
-
-    def load(self, name):
-        self.refresh()
-        if name not in self.entry_by_name:
-            raise ImportError("unknown addon %r" % name)
-        entry = self.entry_by_name[name]
-        addon_class = entry.load()
-        if not (isinstance(addon_class, type) and
-                issubclass(addon_class, Addon) and
-                addon_class.name == name):
-            raise ImportError("invalid addon %r" % name)
-        return addon_class
-
-
-addon_registry = GlobalAddonRegistry()
-
-
-class Parameter(object):
-
-    def __init__(self, attribute, validator, default=None,
-                 value_name=None, hint=None):
-        assert isinstance(attribute, str)
-        assert re.match(r'^[a-zA-Z_][0-9a-zA-Z_]*$', attribute)
-        assert isinstance(validator, Validator)
-        assert isinstance(value_name, maybe(str))
-        assert isinstance(hint, maybe(str))
-        self.attribute = attribute
-        self.validator = validator
-        self.default = default
-        self.value_name = value_name
-        self.hint = hint
-
-    def get_hint(self):
-        """
-        Returns a short one-line description of the parameter.
-        """
-        return self.hint
-
-    def get_signature(self):
-        """
-        Returns the parameter signature.
-        """
-        # Parameter signature has the form:
-        #   {attribute}={VALUE_NAME}
-        attribute = self.attribute.replace('_', '-')
-        value_name = self.value_name
-        if value_name is None:
-            value_name = attribute
-        value_name = value_name.upper()
-        return "%s=%s" % (attribute, value_name)
-
-
-class Addon(object):
-    """
-    Implements an addon for HTSQL applications.
-
-    This is an abstract class; to add a new addon, create a subclass
-    of :class:`Addon`.
-    """
-
-    name = None
-    parameters = []
-    hint = None
-    help = None
-
-    packages = ['.']
-    prerequisites = ['htsql']
-    postrequisites = []
-
-    @classmethod
-    def get_hint(cls):
-        """
-        Returns a short one-line description of the addon.
-        """
-        return cls.hint
-
-    @classmethod
-    def get_help(cls):
-        """
-        Returns a long description of the addon.
-        """
-        if cls.help is None:
-            return None
-        return trim_doc(cls.help)
-
-    @classmethod
-    def get_prerequisites(cls):
-        prerequisites = cls.prerequisites[:]
-        name = cls.name
-        while '.' in name:
-            name = name.rsplit('.', 1)[0]
-            prerequisites.append(name)
-        return prerequisites
-
-    @classmethod
-    def get_postrequisites(cls):
-        return cls.postrequisites
-
-    @classmethod
-    def get_extension(cls, app, attributes):
-        return {}
-
-    def __init__(self, app, attributes):
-        names = self.name.split('.')
-        parent_names = names[:-1]
-        name = names[-1]
-        parent = app
-        for parent_name in parent_names:
-            assert hasattr(parent, parent_name)
-            parent = getattr(parent, parent_name)
-            assert isinstance(parent, Addon)
-        assert not hasattr(parent, name)
-        setattr(parent, name, self)
-        for name in attributes:
-            setattr(self, name, attributes[name])
-
-    def validate(self):
-        pass
-
-

src/htsql/application.py

-#
-# Copyright (c) 2006-2011, Prometheus Research, LLC
-# See `LICENSE` for license information, `AUTHORS` for the list of authors.
-#
-
-
-"""
-:mod:`htsql.application`
-========================
-
-This module implements an HTSQL application.
-"""
-
-
-from __future__ import with_statement
-from .context import context
-from .addon import addon_registry
-from .adapter import ComponentRegistry
-from .util import maybe, oneof, listof, dictof, tupleof
-from .wsgi import WSGI
-from .cmd.command import UniversalCmd
-from .cmd.act import produce
-
-
-class Application(object):
-    """
-    Implements an HTSQL application.
-
-    `db`
-        The connection URI.
-
-    `extensions`
-        List of addons activated with the application.
-    """
-
-    def __init__(self, db, *extensions):
-        assert isinstance(list(extensions),
-                listof(oneof(str,
-                             tupleof(str, maybe(dictof(str, object))),
-                             dictof(str, maybe(dictof(str, object))))))
-        self.addons = []
-        htsql_extension = {'htsql': {} }
-        if db is not None:
-            htsql_extension['htsql']['db'] = db
-        extensions = [htsql_extension] + list(extensions)
-        configuration = {}
-        dependencies = {}
-        addon_class_by_name = {}
-        addon_instance_by_name = {}
-        while extensions:
-            while extensions:
-                extension = extensions.pop(0)
-                if isinstance(extension, str):
-                    extension = { extension: None }
-                elif isinstance(extension, tuple):
-                    extension = dict([extension])
-                for addon_name in sorted(extension):
-                    addon_parameters = extension[addon_name]
-                    addon_name = addon_name.replace('-', '_')
-                    if addon_parameters is None:
-                        addon_parameters = {}
-                    if addon_name in addon_instance_by_name:
-                        if addon_parameters:
-                            raise ImportError("invalid addon dependency at %r"
-                                              % addon_name)
-                        continue
-                    configuration.setdefault(addon_name, {})
-                    for key in sorted(addon_parameters):
-                        value = addon_parameters[key]
-                        key = key.replace('-', '_')
-                        if key not in configuration[addon_name]:
-                            configuration[addon_name][key] = value
-                    if addon_name not in addon_class_by_name:
-                        addon_class = addon_registry.load(addon_name)
-                        addon_class_by_name[addon_name] = addon_class
-                        prerequisites = addon_class.get_prerequisites()
-                        postrequisites = addon_class.get_postrequisites()
-                        requisites_extension = dict((name, {})
-                                    for name in prerequisites+postrequisites)
-                        if requisites_extension:
-                            extensions.append(requisites_extension)
-                        dependencies.setdefault(addon_name, [])
-                        for name in prerequisites:
-                            name = name.replace('-', '_')
-                            dependencies[addon_name].append(name)
-                        for name in postrequisites:
-                            name = name.replace('-', '_')
-                            dependencies.setdefault(name, [])
-                            dependencies[name].append(addon_name)
-            while not extensions and (len(addon_instance_by_name)
-                                        < len(addon_class_by_name)):
-                for addon_name in sorted(dependencies):
-                    dependencies[addon_name] = [name
-                                for name in dependencies[addon_name]
-                                if name not in addon_instance_by_name]
-                candidates = [addon_name
-                            for addon_name in sorted(addon_class_by_name)
-                            if addon_name not in addon_instance_by_name]
-                for addon_name in candidates:
-                    if not dependencies[addon_name]:
-                        break
-                else:
-                    raise ImportError("circular addon dependency at %r"
-                                      % candidates[0])
-                addon_class = addon_class_by_name[addon_name]
-                attributes = {}
-                valid_attributes = set()
-                for parameter in addon_class.parameters:
-                    valid_attributes.add(parameter.attribute)
-                for key in sorted(configuration[addon_name]):
-                    if key not in valid_attributes:
-                        raise ImportError("unknown parameter %r of addon %r"
-                                          % (key, addon_name))
-                for parameter in addon_class.parameters:
-                    if parameter.attribute in configuration[addon_name]:
-                        value = configuration[addon_name][parameter.attribute]
-                        try:
-                            value = parameter.validator(value)
-                        except ValueError, exc:
-                            raise ImportError("invalid parameter %r"
-                                              " of addon %r: %s"
-                                              % (parameter.attribute,
-                                                 addon_name, exc))
-                    else:
-                        value = parameter.default
-                    attributes[parameter.attribute] = value
-                extension = addon_class.get_extension(self, attributes)
-                if extension:
-                    extensions.append(extension)
-                addon_instance = addon_class(self, attributes)
-                addon_instance_by_name[addon_name] = addon_instance
-        for addon_name in sorted(addon_instance_by_name):
-            self.addons.append(addon_instance_by_name[addon_name])
-        self.component_registry = ComponentRegistry(self.addons)
-        with self:
-            for addon in self.addons:
-                try:
-                    addon.validate()
-                except ValueError, exc:
-                    raise ImportError("failed to initialize %r: %s"
-                                      % (addon.name, exc))
-
-    def __enter__(self):
-        """
-        Activates the application in the current thread.
-        """
-        context.push(self)
-
-    def __exit__(self, exc_type, exc_value, exc_traceback):
-        """
-        Inactivates the application in the current thread.
-        """
-        context.pop(self)
-
-    def __call__(self, environ, start_response):
-        """
-        Implements the WSGI entry point.
-        """
-        with self:
-            wsgi = WSGI()
-            body = wsgi(environ, start_response)
-            for chunk in body:
-                yield chunk
-
-    def produce(self, uri, **parameters):
-        with self:
-            command = UniversalCmd(uri, parameters)
-            return produce(command)
-
-

src/htsql/cache.py

-#
-# Copyright (c) 2006-2011, Prometheus Research, LLC
-# See `LICENSE` for license information, `AUTHORS` for the list of authors.
-#
-
-
-from __future__ import with_statement
-from .context import context
-import threading
-import functools
-
-
-class GeneralCache(object):
-
-    def __init__(self):
-        self.values = {}
-        self.locks = {}
-        self.cache_lock = threading.Lock()
-
-    def lock(self, service):
-        try:
-            return self.locks[service]
-        except KeyError:
-            with self.cache_lock:
-                if service not in self.locks:
-                    self.locks[service] = threading.Lock()
-            return self.locks[service]
-
-    def set(self, key, value):
-        with self.cache_lock:
-            assert key not in self.values
-            self.values[key] = value
-
-
-def once(service):
-    @functools.wraps(service)
-    def wrapper(*args, **kwds):
-        cache = context.app.htsql.cache
-        key = (service.__module__, service.__name__) + args
-        try:
-            return cache.values[key]
-        except KeyError:
-            with cache.lock(service):
-                if key not in cache.values:
-                    value = service(*args, **kwds)
-                    if key not in cache.values:
-                        cache.set(key, value)
-                    else:
-                        assert value is cache.values[key]
-            return cache.values[key]
-    return wrapper
-
-

src/htsql/classify.py

-#
-# Copyright (c) 2006-2011, Prometheus Research, LLC
-# See `LICENSE` for license information, `AUTHORS` for the list of authors.
-#
-
-
-from .context import context
-from .cache import once
-from .adapter import Adapter, adapts
-from .model import (Node, Arc, Label, HomeNode, TableNode, TableArc, ChainArc,
-                    ColumnArc, SyntaxArc, AmbiguousArc)
-from .entity import DirectJoin, ReverseJoin
-from .introspect import introspect
-import re
-import unicodedata
-
-
-def normalize(name):
-    """
-    Normalizes a name to provide a valid HTSQL identifier.
-
-    We assume `name` is a Unicode string.  Then it is:
-
-    - translated to Unicode normal form C;
-    - converted to lowercase;
-    - has non-alphanumeric characters replaced with underscores;
-    - preceded with an underscore if it starts with a digit.
-
-    The result is a valid HTSQL identifier.
-    """
-    assert isinstance(name, unicode) and len(name) > 0
-    name = unicodedata.normalize('NFC', name).lower()
-    name = re.sub(ur"(?u)^(?=\d)|\W", u"_", name)
-    return name
-
-
-class Classify(Adapter):
-
-    adapts(Node)
-
-    def __init__(self, node):
-        self.node = node
-
-    def __call__(self):
-        arcs = self.trace(self.node)
-        bids_by_arc = {}
-        for arc in arcs:
-            bids_by_arc[arc] = self.call(arc)
-
-        names_by_weight = {}
-        arcs_by_bid = {}
-        for arc in arcs:
-            for bid in bids_by_arc[arc]:
-                name, weight = bid
-                names_by_weight.setdefault(weight, set()).add(name)
-                arcs_by_bid.setdefault(bid, []).append(arc)
-
-        arc_by_signature = {}
-        name_by_arc = {}
-        rejections_by_signature = {}
-
-        for weight in sorted(names_by_weight, reverse=True):
-            names = sorted(names_by_weight[weight],
-                           key=(lambda name: (len(name), name)))
-            for name in names:
-                contenders_by_arity = {}
-                for arc in arcs_by_bid[name, weight]:
-                    contenders_by_arity.setdefault(arc.arity, []).append(arc)
-                for arity in sorted(contenders_by_arity):
-                    signature = (name, arity)
-                    contenders = contenders_by_arity[arity]
-                    if signature in arc_by_signature:
-                        continue
-                    if (len(contenders) > 1 or
-                            signature in rejections_by_signature):
-                        rejections_by_signature.setdefault(signature, [])
-                        rejections_by_signature[signature].extend(contenders)
-                        continue
-                    [arc] = contenders
-                    if arc in name_by_arc:
-                        rejections_by_signature[signature] = [arc]
-                        continue
-                    arc_by_signature[signature] = arc
-                    name_by_arc[arc] = name
-
-        labels = []
-        for arc in arcs:
-            if arc not in name_by_arc:
-                continue
-            name = name_by_arc[arc]
-            label = Label(name, arc, False)
-            labels.append(label)
-        for signature in sorted(rejections_by_signature):
-            name, arity = signature
-            alternatives = []
-            duplicates = set()
-            for arc in rejections_by_signature[signature]:
-                if arc in duplicates:
-                    continue
-                alternatives.append(arc)
-                duplicates.add(arc)
-            arc = AmbiguousArc(arity, alternatives)
-            label = Label(name, arc, False)
-            labels.append(label)
-
-        labels = self.order(labels)
-
-        return labels
-
-    def trace(self, node):
-        trace = Trace(node)
-        arcs = []
-        duplicates = set()
-        for arc in trace():
-            if arc in duplicates:
-                continue
-            arcs.append(arc)
-            duplicates.add(arc)
-        return arcs
-
-    def call(self, arc):
-        call = Call(arc)
-        bids = []
-        duplicates = set()
-        for name, weight in call():
-            name = normalize(name)
-            if (name, weight) in duplicates:
-                continue
-            bids.append((name, weight))
-            duplicates.add((name, weight))
-        return bids
-
-    def order(self, labels):
-        order = Order(self.node, labels)
-        return order()
-
-
-class Trace(Adapter):
-
-    adapts(Node)
-
-    def __init__(self, node):
-        self.node = node
-
-    def __call__(self):
-        return []
-
-
-class TraceHome(Trace):
-
-    adapts(HomeNode)
-
-    def __call__(self):
-        catalog = introspect()
-        for schema in catalog.schemas:
-            for table in schema.tables:
-                yield TableArc(table)
-
-
-class TraceTable(Trace):
-
-    adapts(TableNode)
-
-    def __call__(self):
-        table = self.node.table
-        for column in table.columns:
-            link = self.find_link(column)
-            yield ColumnArc(table, column, link)
-        for foreign_key in table.foreign_keys:
-            join = DirectJoin(foreign_key)
-            yield ChainArc(table, [join])
-        for foreign_key in table.referring_foreign_keys:
-            join = ReverseJoin(foreign_key)
-            yield ChainArc(table, [join])
-
-    def find_link(self, column):
-        # Determines if the column may represents a link to another table.
-        # This is the case when the column is associated with a foreign key.
-
-        # Get a list of foreign keys associated with the given column.
-        candidates = [foreign_key for foreign_key in column.foreign_keys
-                                  if len(foreign_key.origin_columns) == 1]
-
-        # Return immediately if there are no candidate keys.
-        if not candidates:
-            return None
-
-        # Generate the joins corresponding to each alternative.
-        alternatives = []
-        for foreign_key in candidates:
-            join = DirectJoin(foreign_key)
-            arc = ChainArc(column.table, [join])
-            alternatives.append(arc)
-        # We got an unambiguous link if there's only one foreign key
-        # associated with the column.
-        if len(alternatives) == 1:
-            return alternatives[0]
-        else:
-            return AmbiguousArc(alternatives)
-
-
-class Call(Adapter):
-
-    adapts(Arc)
-
-    def __init__(self, arc):
-        self.arc = arc
-
-    def __call__(self):
-        return []
-
-
-class CallTable(Call):
-
-    adapts(TableArc)
-
-    def __call__(self):
-        table = self.arc.table
-        yield table.name, table.schema.priority
-        if table.schema.name:
-            name = u"%s %s" % (table.schema.name, table.name)
-            yield name, -1
-
-
-class CallColumn(Call):
-
-    adapts(ColumnArc)
-
-    def __call__(self):
-        yield self.arc.column.name, 10
-
-
-class CallChain(Call):
-
-    adapts(ChainArc)
-
-    path_word = u"via"
-
-    def __call__(self):
-        is_primary = True
-        for join in self.arc.joins:
-            foreign_key = join.foreign_key
-            primary_key = foreign_key.origin.primary_key
-            if primary_key is None:
-                is_primary = False
-                break
-            if not all(column in primary_key.origin_columns
-                       for column in foreign_key.origin_columns):
-                is_primary = False
-                break
-
-        is_direct = all(join.is_direct for join in self.arc.joins)
-
-        target = self.arc.target.table.name
-        prefix = None
-        column = None
-        if len(self.arc.joins) == 1:
-            foreign_key = join.foreign_key
-            origin_name = foreign_key.origin_columns[-1].name
-            target_name = foreign_key.target_columns[-1].name
-            if origin_name.endswith(target_name):
-                prefix = origin_name[:-len(target_name)].rstrip(u' _-')
-                if not prefix:
-                    prefix = target
-            column = origin_name
-
-        if is_direct and prefix:
-            yield prefix, 5
-        if is_primary:
-            yield target, 4
-        else:
-            yield target, 3
-        if not is_direct and prefix:
-            name = u"%s %s %s" % (target, self.path_word, prefix)
-            yield name, 2
-        if not is_direct and column:
-            name = u"%s %s %s" % (target, self.path_word, column)
-            yield name, 1
-
-
-class CallSyntax(Call):
-
-    adapts(SyntaxArc)
-
-
-class Order(Adapter):
-
-    adapts(Node)
-
-    def __init__(self, node, labels):
-        self.node = node
-        self.labels = labels
-
-    def __call__(self):
-        return self.labels
-
-
-class OrderHome(Order):
-
-    adapts(HomeNode)
-
-
-class OrderTable(Order):
-
-    adapts(TableNode)
-
-    def __call__(self):
-        return [label.clone(is_public=(label.is_public or
-                                       isinstance(label.arc, ColumnArc)))
-                for label in self.labels]
-
-
-@once
-def classify(node):
-    assert isinstance(node, Node)
-    classify = Classify(node)
-    labels = classify()
-    return labels
-
-
-@once
-def relabel(arc):
-    assert isinstance(arc, Arc)
-    cache = context.app.htsql.cache
-    labels = classify(arc.origin)
-    duplicates = set()
-    labels_by_arc = {}
-    labels_by_arc[arc] = []
-    arcs = [arc]
-    for label in labels:
-        assert label.name not in duplicates, label
-        duplicates.add(label.name)
-        arc = label.arc
-        if arc not in labels_by_arc:
-            labels_by_arc[arc] = []
-            arcs.append(arc)
-        labels_by_arc[arc].append(label)
-    for arc in arcs:
-        key = (relabel.__module__, relabel.__name__, arc)
-        value = labels_by_arc[arc]
-        if key not in cache.values:
-            cache.set(key, value)
-    return labels_by_arc[arcs[0]]
-
-

src/htsql/cmd/__init__.py

-#
-# Copyright (c) 2006-2011, Prometheus Research, LLC
-# See `LICENSE` for license information, `AUTHORS` for the list of authors.
-#
-
-
-"""
-:mod:`htsql.cmd`
-================
-
-This package implements commands and actions.
-"""
-
-
-from . import act, command, retrieve
-
-

src/htsql/cmd/act.py

-#
-# Copyright (c) 2006-2011, Prometheus Research, LLC
-# See `LICENSE` for license information, `AUTHORS` for the list of authors.
-#
-
-
-from ..adapter import Adapter, adapts
-from ..error import BadRequestError
-from .command import (Command, UniversalCmd, DefaultCmd, RetrieveCmd,
-                      ProducerCmd, RendererCmd)
-from ..tr.lookup import lookup_command
-from ..tr.parse import parse
-from ..tr.bind import bind
-from ..tr.embed import embed
-from ..fmt.format import FindRenderer
-
-
-class UnsupportedActionError(BadRequestError):
-    pass
-
-
-class Action(object):
-    pass
-
-
-class ProduceAction(Action):
-    pass
-
-
-class SafeProduceAction(ProduceAction):
-
-    def __init__(self, limit):
-        assert isinstance(limit, int) and limit > 0
-        self.limit = limit
-
-
-class AnalyzeAction(Action):
-    pass
-
-
-class RenderAction(Action):
-
-    def __init__(self, environ):
-        self.environ = environ
-
-
-class Act(Adapter):
-
-    adapts(Command, Action)
-
-    def __init__(self, command, action):
-        assert isinstance(command, Command)
-        assert isinstance(action, Action)
-        self.command = command
-        self.action = action
-
-    def __call__(self):
-        raise UnsupportedActionError("unsupported action")
-
-
-class ActUniversal(Act):
-
-    adapts(UniversalCmd, Action)
-
-    def __call__(self):
-        syntax = parse(self.command.query)
-        environment = []
-        if self.command.parameters is not None:
-            for name in sorted(self.command.parameters):
-                value = self.command.parameters[name]
-                if isinstance(name, str):
-                    name = name.decode('utf-8')
-                recipe = embed(value)
-                environment.append((name, recipe))
-        binding = bind(syntax, environment=environment)
-        command = lookup_command(binding)
-        if command is None:
-            command = DefaultCmd(binding)
-        return act(command, self.action)
-
-
-class ActDefault(Act):
-
-    adapts(DefaultCmd, Action)
-
-    def __call__(self):
-        command = RetrieveCmd(self.command.binding)
-        return act(command, self.action)
-
-
-class RenderProducer(Act):
-
-    adapts(ProducerCmd, RenderAction)
-
-    def __call__(self):
-        product = produce(self.command)
-        environ = self.action.environ
-        find_renderer = FindRenderer()
-        accept = set([''])
-        if 'HTTP_ACCEPT' in environ:
-            for name in environ['HTTP_ACCEPT'].split(','):
-                if ';' in name:
-                    name = name.split(';', 1)[0]
-                name = name.strip()
-                accept.add(name)
-        renderer_class = find_renderer(accept)
-        assert renderer_class is not None
-        renderer = renderer_class()
-        return renderer.render(product)
-
-
-class RenderRenderer(Act):
-
-    adapts(RendererCmd, RenderAction)
-
-    def __call__(self):
-        product = produce(self.command.producer)
-        renderer_class = self.command.format
-        renderer = renderer_class()
-        return renderer.render(product)
-
-
-def act(command, action):
-    act = Act(command, action)
-    return act()
-
-
-def produce(command):
-    action = ProduceAction()
-    return act(command, action)
-
-
-def safe_produce(command, limit):
-    action = SafeProduceAction(limit)
-    return act(command, action)
-
-
-def analyze(command):
-    action = AnalyzeAction()
-    return act(command, action)
-
-
-def render(command, environ):
-    action = RenderAction(environ)
-    return act(command, action)
-
-

src/htsql/cmd/command.py

-#
-# Copyright (c) 2006-2011, Prometheus Research, LLC
-# See `LICENSE` for license information, `AUTHORS` for the list of authors.
-#
-
-
-from ..util import Printable, maybe, dictof, oneof
-from ..fmt import (TextRenderer, HTMLRenderer, JSONRenderer,
-                   CSVRenderer, TSVRenderer)
-
-
-class Command(Printable):
-    pass
-
-