1. pombredanne
  2. htsql2

Commits

Kirill Simonov  committed 43d2338

produce() API: refactored embedding parameters.

  • Participants
  • Parent commits 54d133d
  • Branches default

Comments (0)

Files changed (11)

File src/htsql/core/application.py

View file
             for chunk in body:
                 yield chunk
 
-    def produce(self, uri, **parameters):
+    def produce(self, command, environment=None, **parameters):
         with self:
-            command = UniversalCmd(uri)
-            return produce(command, parameters)
+            return produce(command, environment, **parameters)
 
 

File src/htsql/core/cmd/__init__.py

View file
 #
 
 
-from . import act, command, fetch, summon
+from . import act, command, embed, fetch, summon
 
 

File src/htsql/core/cmd/act.py

View file
 from ..util import Clonable
 from .command import Command, UniversalCmd, DefaultCmd, FormatCmd, FetchCmd
 from .summon import recognize
+from .embed import embed
 from ..syn.parse import parse
 from ..syn.syntax import Syntax
 from ..fmt.emit import emit, emit_headers
 
 class ProduceAction(Action):
 
-    def __init__(self, parameters=None):
-        self.parameters = parameters
+    def __init__(self, environment=None):
+        self.environment = environment
 
 
 class SafeProduceAction(ProduceAction):
 
-    def __init__(self, parameters=None, cut=None):
-        self.parameters = parameters
+    def __init__(self, environment=None, cut=None):
+        self.environment = environment
         self.cut = cut
 
 
 class AnalyzeAction(Action):
 
-    def __init__(self, parameters=None):
-        self.parameters = parameters
+    def __init__(self, environment=None):
+        self.environment = environment
 
 
 class RenderAction(Action):
     return Act.__invoke__(command, action)
 
 
-def produce(command, parameters=None):
-    action = ProduceAction(parameters)
+def produce(command, environment=None, **parameters):
+    environment = embed(environment, **parameters)
+    action = ProduceAction(environment)
     return act(command, action)
 
 
-def safe_produce(command, parameters=None, cut=None):
-    action = SafeProduceAction(parameters, cut)
+def safe_produce(command, cut, environment=None, **parameters):
+    environment = embed(environment, **parameters)
+    action = SafeProduceAction(environment, cut)
     return act(command, action)
 
 
-def analyze(command, parameters=None):
+def analyze(command, environment=None, **parameters):
+    environment = embed(environment, **parameters)
     action = AnalyzeAction(parameters)
     return act(command, action)
 

File src/htsql/core/cmd/embed.py

View file
+#
+# Copyright (c) 2006-2012, Prometheus Research, LLC
+#
+
+
+from ..util import isfinite, to_name
+from ..adapter import Adapter, adapt, adapt_many
+from ..domain import (UntypedDomain, BooleanDomain, IntegerDomain, FloatDomain,
+        DecimalDomain, DateDomain, TimeDomain, DateTimeDomain, ListDomain,
+        Value)
+import types
+import datetime
+import decimal
+
+
+class Embed(Adapter):
+
+    adapt(object)
+
+    def __init__(self, data):
+        self.data = data
+
+    def __call__(self):
+        raise TypeError("unable to embed a value of type %s"
+                        % type(self.data))
+
+
+class EmbedValue(Embed):
+
+    adapt(Value)
+
+    def __call__(self):
+        return self.data
+
+
+class EmbedUntyped(Embed):
+
+    adapt_many(str, unicode)
+
+    def __call__(self):
+        data = self.data
+        if isinstance(data, str):
+            try:
+                data = data.decode('utf-8')
+            except UnicodeDecodeError:
+                raise TypeError("a string is expected to be encoded in UTF-8:"
+                                " %s" % repr(data))
+        if u"\0" in data:
+            raise TypeError("a string should not contain a NIL character:"
+                            " %s" % repr(data))
+        return Value(UntypedDomain(), data)
+
+
+class EmbedNull(Embed):
+
+    adapt(types.NoneType)
+
+    def __call__(self):
+        return Value(UntypedDomain(), None)
+
+
+class EmbedBoolean(Embed):
+
+    adapt(bool)
+
+    def __call__(self):
+        return Value(BooleanDomain(), self.data)
+
+
+class EmbedInteger(Embed):
+
+    adapt_many(int, long)
+
+    def __call__(self):
+        return Value(IntegerDomain(), self.data)
+
+
+class EmbedFloat(Embed):
+
+    adapt(float)
+
+    def __call__(self):
+        if not isfinite(self.data):
+            raise TypeError("a float value must be finite")
+        return Value(FloatDomain(), self.data)
+
+
+class EmbedDecimal(Embed):
+
+    adapt(decimal.Decimal)
+
+    def __call__(self):
+        if not isfinite(self.data):
+            raise TypeError("a decimal value must be finite")
+        return Value(DecimalDomain(), self.data)
+
+
+class EmbedDate(Embed):
+
+    adapt(datetime.date)
+
+    def __call__(self):
+        return Value(DateDomain(), self.data)
+
+
+class EmbedTime(Embed):
+
+    adapt(datetime.time)
+
+    def __call__(self):
+        return Value(TimeDomain(), self.data)
+
+
+class EmbedDateTime(Embed):
+
+    adapt(datetime.datetime)
+
+    def __call__(self):
+        return Value(DateTimeDomain(), self.data)
+
+
+class EmbedList(Embed):
+
+    adapt_many(list, tuple)
+
+    def __call__(self):
+        entry_values = [Embed.__invoke__(entry) for entry in self.data]
+        domain_set = set(entry_value.domain for entry_value in entry_values
+                         if not isinstance(entry_value.domain, UntypedDomain))
+        if not domain_set:
+            domain = UntypedDomain()
+            return Value(ListDomain(domain), [entry_value.data
+                                              for entry_value in entry_values])
+        if len(domain_set) > 1:
+            domain_names = sorted(str(domain) for domain in domain_set)
+            raise TypeError("multiple entry domains: %s"
+                            % ", ".join(domain_names))
+        domain = domain_set.pop()
+        entries = [entry_value.data if entry_value.domain == domain else
+                   domain.parse(entry_value.data)
+                   for entry_value in entry_values]
+        return Value(ListDomain(domain), entries)
+
+
+def embed(base_environment, **parameters):
+    environment = {}
+    if base_environment:
+        for name in sorted(base_environment):
+            value = base_environment[name]
+            if not isinstance(value, Value):
+                value = Embed.__invoke__(value)
+            name = to_name(name)
+            environment[name] = value
+    for name in sorted(parameters):
+        value = parameters[name]
+        if not isinstance(value, Value):
+            value = Embed.__invoke__(value)
+        name = to_name(name)
+        environment[name] = value
+    return environment
+
+

File src/htsql/core/cmd/fetch.py

View file
 from .command import FetchCmd, SkipCmd, SQLCmd
 from .act import (analyze, Act, ProduceAction, SafeProduceAction,
                   AnalyzeAction, RenderAction)
-from ..tr.embed import embed
 from ..tr.bind import bind
 from ..tr.binding import Binding
 from ..tr.encode import encode
 
 class BuildFetch(Utility):
 
-    def __init__(self, syntax, parameters=None, limit=None):
+    def __init__(self, syntax, environment=None, limit=None):
         self.syntax = syntax
-        self.parameters = parameters
+        self.environment = environment
         self.limit = limit
 
     def __call__(self):
         if not isinstance(self.syntax, Binding):
-            environment = []
-            if self.parameters is not None:
-                if isinstance(self.parameters, dict):
-                    for name in sorted(self.parameters):
-                        value = self.parameters[name]
-                        if isinstance(name, str):
-                            name = name.decode('utf-8')
-                        recipe = embed(value)
-                        environment.append((name, recipe))
-                else:
-                    environment = self.parameters[:]
-            binding = bind(self.syntax, environment=environment)
+            binding = bind(self.syntax, environment=self.environment)
         else:
             binding = self.syntax
         expression = encode(binding)
         cut = None
         if isinstance(self.action, SafeProduceAction):
             cut = self.action.cut
-        pipe = build_fetch(self.command.syntax, self.action.parameters, cut)
+        pipe = build_fetch(self.command.syntax, self.action.environment, cut)
         return pipe()
 
 
     adapt(FetchCmd, AnalyzeAction)
 
     def __call__(self):
-        pipe = build_fetch(self.command.syntax, self.action.parameters)
+        pipe = build_fetch(self.command.syntax, self.action.environment)
         return pipe.plan
 
 

File src/htsql/core/syn/syntax.py

View file
 
     `rarms`: [:class:`IdentifierSyntax` or :class:`ReferenceSyntax`] or ``None``
         Formal parameters; ``None`` if no ``()``.
+
+    `identifier`: :class:`IdentifierSyntax`
+        Set only if the specifier is an identifier.
+
+    `reference`: :class:`ReferenceSyntax`
+        Set only if the specifier is a reference.
     """
 
     def __init__(self, larms, rarms, mark):
         super(SpecifySyntax, self).__init__(mark)
         self.larms = larms
         self.rarms = rarms
+        # Unpack the specifier for common cases:
+        #   <identifier> := ...
+        #   $ <reference> := ...
+        self.identifier = None
+        self.reference = None
+        if rarms is None and len(larms) == 1:
+            if isinstance(larms[0], IdentifierSyntax):
+                self.identifier = larms[0]
+            if isinstance(larms[0], ReferenceSyntax):
+                self.reference = larms[0]
 
     def __basis__(self):
         return (tuple(self.larms), tuple(self.rarms)

File src/htsql/core/tr/__init__.py

View file
 """
 
 
-from . import (assemble, binding, bind, coerce, compile, dump, encode, embed,
-        error, flow, fn, frame, lookup, plan, reduce, rewrite, signature,
-        stitch, term)
+from . import (assemble, binding, bind, coerce, compile, dump, encode, error,
+        flow, fn, frame, lookup, plan, reduce, rewrite, signature, stitch,
+        term)
 
 

File src/htsql/core/tr/bind.py

View file
             state.pop_scope()
         return binding
     else:
-        state = BindingState(environment)
+        recipes = []
+        if environment is not None:
+            for name in sorted(environment):
+                value = environment[name]
+                if isinstance(value.domain, ListDomain):
+                    item_recipes = [LiteralRecipe(item,
+                                                  value.domain.item_domain)
+                                    for item in value.data]
+                    recipe = SelectionRecipe(item_recipes)
+                elif isinstance(value.domain, IdentityDomain):
+                    def convert(domain, data):
+                        items = []
+                        for element, item in zip(domain.labels, data):
+                            if isinstance(element, IdentityDomain):
+                                item = convert(element, item)
+                            else:
+                                item = LiteralRecipe(item, element)
+                            items.append(item)
+                        return IdentityRecipe(items)
+                    recipe = convert(value.domain, value.data)
+                else:
+                    recipe = LiteralRecipe(value.data, value.domain)
+                recipes.append((name, recipe))
+        state = BindingState(recipes)
         root = RootBinding(syntax)
         state.set_root(root)
         if isinstance(syntax, AssignSyntax):

File src/htsql/core/tr/embed.py

-#
-# Copyright (c) 2006-2012, Prometheus Research, LLC
-#
-
-
-from ..adapter import Adapter, adapt, adapt_many
-from ..domain import (UntypedDomain, BooleanDomain, IntegerDomain, FloatDomain,
-                      DecimalDomain, DateDomain, TimeDomain, DateTimeDomain)
-from .binding import LiteralRecipe, SelectionRecipe
-import types
-import datetime
-import decimal
-
-
-class Embed(Adapter):
-
-    adapt(object)
-
-    def __init__(self, value):
-        self.value = value
-
-    def __call__(self):
-        raise ValueError("unable to embed a value of type %s"
-                         % type(self.value))
-
-
-class EmbedUntyped(Embed):
-
-    adapt_many(str, unicode)
-
-    def __call__(self):
-        value = self.value
-        if isinstance(value, str):
-            try:
-                value = value.decode('utf-8')
-            except UnicodeDecodeError:
-                raise ValueError("a string is expected to be encoded in UTF-8:"
-                                 " %s" % repr(value))
-        if u"\0" in value:
-            raise ValueError("a string should not contain a NIL character:"
-                             " %s" % repr(value))
-        return LiteralRecipe(value, UntypedDomain())
-
-
-class EmbedNull(Embed):
-
-    adapt(types.NoneType)
-
-    def __call__(self):
-        return LiteralRecipe(None, UntypedDomain())
-
-
-class EmbedBoolean(Embed):
-
-    adapt(bool)
-
-    def __call__(self):
-        return LiteralRecipe(self.value, BooleanDomain())
-
-
-class EmbedInteger(Embed):
-
-    adapt_many(int, long)
-
-    def __call__(self):
-        return LiteralRecipe(self.value, IntegerDomain())
-
-
-class EmbedFloat(Embed):
-
-    adapt(float)
-
-    def __call__(self):
-        return LiteralRecipe(self.value, FloatDomain())
-
-
-class EmbedDecimal(Embed):
-
-    adapt(decimal.Decimal)
-
-    def __call__(self):
-        return LiteralRecipe(self.value, DecimalDomain())
-
-
-class EmbedDate(Embed):
-
-    adapt(datetime.date)
-
-    def __call__(self):
-        return LiteralRecipe(self.value, DateDomain())
-
-
-class EmbedTime(Embed):
-
-    adapt(datetime.time)
-
-    def __call__(self):
-        return LiteralRecipe(self.value, TimeDomain())
-
-
-class EmbedDateTime(Embed):
-
-    adapt(datetime.datetime)
-
-    def __call__(self):
-        return LiteralRecipe(self.value, DateTimeDomain())
-
-
-class EmbedList(Embed):
-
-    adapt_many(list, tuple)
-
-    def __call__(self):
-        recipes = []
-        for value in self.value:
-            recipe = embed(value)
-            recipes.append(recipe)
-        return SelectionRecipe(recipes)
-
-
-embed = Embed.__invoke__
-
-

File src/htsql/tweak/etl/cmd/do.py

View file
 from ....core.cmd.command import DefaultCmd
 from ....core.cmd.act import Act, ProduceAction, act
 from ....core.tr.bind import BindingState
-from ....core.tr.embed import embed
 from ....core.tr.binding import LiteralRecipe, IdentityRecipe, ClosedRecipe
 from .command import DoCmd
 
     adapt(DoCmd, ProduceAction)
 
     def __call__(self):
-        environment = []
-        parameters = self.action.parameters
-        if parameters is not None:
-            if isinstance(parameters, dict):
-                for name in sorted(parameters):
-                    value = parameters[name]
-                    if isinstance(name, str):
-                        name = name.decode('utf-8')
-                    recipe = embed(value)
-                    environment.append((name, recipe))
-            else:
-                environment = self.parameters[:]
+        environment = self.action.environment.copy()
         product = None
         with transaction():
             for reference, command in self.command.blocks:
-                action = self.action.clone(parameters=environment)
+                action = self.action.clone(environment=environment)
                 product = act(command, action)
                 if reference is not None:
-                    if (isinstance(product.meta.domain, IdentityDomain) and
-                            product.data is not None):
-                        def convert(domain, data):
-                            items = []
-                            for element, item in zip(domain.labels, data):
-                                if isinstance(element, IdentityDomain):
-                                    item = convert(element, item)
-                                else:
-                                    item = LiteralRecipe(item, element)
-                                items.append(item)
-                            return IdentityRecipe(items)
-                        literal = convert(product.meta.domain, product.data)
-                    else:
-                        literal = LiteralRecipe(product.data,
-                                                product.meta.domain)
-                    recipe = ClosedRecipe(literal)
-                    environment.append((reference, recipe))
+                    environment[reference] = product
         return product
 
 

File src/htsql/tweak/etl/cmd/summon.py

View file
             syntax = argument
             if isinstance(argument, AssignSyntax):
                 specifier = argument.larm
-                if not (len(specifier.larms) == 1 and
-                        isinstance(specifier.larms[0], ReferenceSyntax) and
-                        specifier.rarms is None):
+                if specifier.reference is None:
                     raise RecognizeError("a reference is expected",
                                          specifier.mark)
-                name = specifier.larms[0].identifier.name
+                name = specifier.reference.name
                 syntax = argument.rarm
             command = recognize(syntax)
             blocks.append((name, command))