summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/sql/base.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2014-02-25 19:52:17 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2014-02-25 19:52:17 -0500
commit33f07202ce2d9d34f346e9629dc602d920091cf1 (patch)
treef41459bbfad1e2068c640405e682adb8236d3896 /lib/sqlalchemy/sql/base.py
parente60529da797491e9e88e9fcc581334ad3a09bcc2 (diff)
downloadsqlalchemy-33f07202ce2d9d34f346e9629dc602d920091cf1.tar.gz
- The new dialect-level keyword argument system for schema-level
constructs has been enhanced in order to assist with existing schemes that rely upon addition of ad-hoc keyword arguments to constructs. - To suit the use case of allowing custom arguments at construction time, the :meth:`.DialectKWArgs.argument_for` method now allows this registration. fixes #2962
Diffstat (limited to 'lib/sqlalchemy/sql/base.py')
-rw-r--r--lib/sqlalchemy/sql/base.py169
1 files changed, 156 insertions, 13 deletions
diff --git a/lib/sqlalchemy/sql/base.py b/lib/sqlalchemy/sql/base.py
index 4a7dd65d3..26007b598 100644
--- a/lib/sqlalchemy/sql/base.py
+++ b/lib/sqlalchemy/sql/base.py
@@ -44,12 +44,149 @@ def _generative(fn, *args, **kw):
return self
+
+class _DialectArgDictBase(object):
+ """base for dynamic dictionaries that handle dialect-level keyword
+ arguments."""
+
+ def _keys_iter(self):
+ raise NotImplementedError()
+ if util.py2k:
+ def keys(self):
+ return list(self._keys_iter())
+ def items(self):
+ return [(key, self[key]) for key in self._keys_iter()]
+ else:
+ def keys(self):
+ return self._keys_iter()
+ def items(self):
+ return ((key, self[key]) for key in self._keys_iter())
+
+ def get(self, key, default=None):
+ if key in self:
+ return self[key]
+ else:
+ return default
+
+ def __iter__(self):
+ return self._keys_iter()
+
+ def __eq__(self, other):
+ return dict(self) == dict(other)
+
+ def __repr__(self):
+ return repr(dict(self))
+
+class _DialectArgView(_DialectArgDictBase):
+ """A dictionary view of dialect-level arguments in the form
+ <dialectname>_<argument_name>.
+
+ """
+ def __init__(self, obj):
+ self.obj = obj
+
+ def __getitem__(self, key):
+ if "_" not in key:
+ raise KeyError(key)
+ dialect, value_key = key.split("_", 1)
+
+ try:
+ opt = self.obj.dialect_options[dialect]
+ except exc.NoSuchModuleError:
+ raise KeyError(key)
+ else:
+ return opt[value_key]
+
+ def __setitem__(self, key, value):
+ if "_" not in key:
+ raise exc.ArgumentError(
+ "Keys must be of the form <dialectname>_<argname>")
+
+ dialect, value_key = key.split("_", 1)
+ self.obj.dialect_options[dialect][value_key] = value
+
+ def _keys_iter(self):
+ return (
+ "%s_%s" % (dialect_name, value_name)
+ for dialect_name in self.obj.dialect_options
+ for value_name in self.obj.dialect_options[dialect_name]._non_defaults
+ )
+
+class _DialectArgDict(_DialectArgDictBase):
+ """A dictionary view of dialect-level arguments for a specific
+ dialect.
+
+ Maintains a separate collection of user-specified arguments
+ and dialect-specified default arguments.
+
+ """
+ def __init__(self, obj, dialect_name):
+ self._non_defaults = {}
+ self._defaults = {}
+
+ def _keys_iter(self):
+ return iter(set(self._non_defaults).union(self._defaults))
+
+ def __getitem__(self, key):
+ if key in self._non_defaults:
+ return self._non_defaults[key]
+ else:
+ return self._defaults[key]
+
+ def __setitem__(self, key, value):
+ self._non_defaults[key] = value
+
class DialectKWArgs(object):
"""Establish the ability for a class to have dialect-specific arguments
with defaults and validation.
"""
+ @classmethod
+ def argument_for(cls, dialect_name, argument_name, default):
+ """Add a new kind of dialect-specific keyword argument for this class.
+
+ E.g.::
+
+ Index.argument_for("mydialect", "length", None)
+
+ some_index = Index('a', 'b', mydialect_length=5)
+
+ The :meth:`.DialectKWArgs.argument_for` method is a per-argument
+ way adding extra arguments to the :attr:`.Dialect.construct_arguments`
+ dictionary. This dictionary provides a list of argument names accepted by
+ various schema-level constructs on behalf of a dialect.
+
+ New dialects should typically specify this dictionary all at once as a data
+ member of the dialect class. The use case for ad-hoc addition of
+ argument names is typically for end-user code that is also using
+ a custom compilation scheme which consumes the additional arguments.
+
+ :param dialect_name: name of a dialect. The dialect must be locatable,
+ else a :class:`.NoSuchModuleError` is raised. The dialect must
+ also include an existing :attr:`.Dialect.construct_arguments` collection,
+ indicating that it participates in the keyword-argument validation and
+ default system, else :class:`.ArgumentError` is raised.
+ If the dialect does not include this collection, then any keyword argument
+ can be specified on behalf of this dialect already. All dialects
+ packaged within SQLAlchemy include this collection, however for third
+ party dialects, support may vary.
+
+ :param argument_name: name of the parameter.
+
+ :param default: default value of the parameter.
+
+ .. versionadded:: 0.9.4
+
+ """
+
+ construct_arg_dictionary = DialectKWArgs._kw_registry[dialect_name]
+ if construct_arg_dictionary is None:
+ raise exc.ArgumentError("Dialect '%s' does have keyword-argument "
+ "validation and defaults enabled configured" %
+ dialect_name)
+ construct_arg_dictionary[cls][argument_name] = default
+
@util.memoized_property
def dialect_kwargs(self):
"""A collection of keyword arguments specified as dialect-specific
@@ -60,19 +197,25 @@ class DialectKWArgs(object):
unlike the :attr:`.DialectKWArgs.dialect_options` collection, which
contains all options known by this dialect including defaults.
+ The collection is also writable; keys are accepted of the
+ form ``<dialect>_<kwarg>`` where the value will be assembled
+ into the list of options.
+
.. versionadded:: 0.9.2
+ .. versionchanged:: 0.9.4 The :attr:`.DialectKWArgs.dialect_kwargs`
+ collection is now writable.
+
.. seealso::
:attr:`.DialectKWArgs.dialect_options` - nested dictionary form
"""
-
- return util.immutabledict()
+ return _DialectArgView(self)
@property
def kwargs(self):
- """Deprecated; see :attr:`.DialectKWArgs.dialect_kwargs"""
+ """A synonym for :attr:`.DialectKWArgs.dialect_kwargs`."""
return self.dialect_kwargs
@util.dependencies("sqlalchemy.dialects")
@@ -85,14 +228,15 @@ class DialectKWArgs(object):
def _kw_reg_for_dialect_cls(self, dialect_name):
construct_arg_dictionary = DialectKWArgs._kw_registry[dialect_name]
+ d = _DialectArgDict(self, dialect_name)
+
if construct_arg_dictionary is None:
- return {"*": None}
+ d._defaults.update({"*": None})
else:
- d = {}
for cls in reversed(self.__class__.__mro__):
if cls in construct_arg_dictionary:
- d.update(construct_arg_dictionary[cls])
- return d
+ d._defaults.update(construct_arg_dictionary[cls])
+ return d
@util.memoized_property
def dialect_options(self):
@@ -123,11 +267,9 @@ class DialectKWArgs(object):
if not kwargs:
return
- self.dialect_kwargs = self.dialect_kwargs.union(kwargs)
-
for k in kwargs:
m = re.match('^(.+?)_(.+)$', k)
- if m is None:
+ if not m:
raise TypeError("Additional arguments should be "
"named <dialectname>_<argument>, got '%s'" % k)
dialect_name, arg_name = m.group(1, 2)
@@ -139,9 +281,10 @@ class DialectKWArgs(object):
"Can't validate argument %r; can't "
"locate any SQLAlchemy dialect named %r" %
(k, dialect_name))
- self.dialect_options[dialect_name] = {
- "*": None,
- arg_name: kwargs[k]}
+ self.dialect_options[dialect_name] = d = \
+ _DialectArgDict(self, dialect_name)
+ d._defaults.update({"*": None})
+ d._non_defaults[arg_name] = kwargs[k]
else:
if "*" not in construct_arg_dictionary and \
arg_name not in construct_arg_dictionary: