'cloned_traverse', 'replacement_traverse']
class VisitableType(type):
- """Metaclass which checks for a `__visit_name__` attribute and
- applies `_compiler_dispatch` method to classes.
-
+ """Metaclass which assigns a `_compiler_dispatch` method to classes
+ having a `__visit_name__` attribute.
+
+ The _compiler_dispatch attribute becomes an instance method which
- looks approximately like this:
++ looks approximately like the following::
+
+ def _compiler_dispatch (self, visitor, **kw):
+ '''Look for an attribute named "visit_" + self.__visit_name__
+ on the visitor, and call it with the same kw params.'''
+ return getattr(visitor, 'visit_%s' % self.__visit_name__)(self, **kw)
+
+ Classes having no __visit_name__ attribute will remain unaffected.
"""
-
def __init__(cls, clsname, bases, clsdict):
if cls.__name__ == 'Visitable' or not hasattr(cls, '__visit_name__'):
super(VisitableType, cls).__init__(clsname, bases, clsdict)
def _compiler_dispatch(self, visitor, **kw):
return getter(visitor)(self, **kw)
else:
+ # The optimization opportunity is lost for this case because the
+ # __visit_name__ is not yet a string. As a result, the visit
+ # string has to be recalculated with each compilation.
def _compiler_dispatch(self, visitor, **kw):
return getattr(visitor, 'visit_%s' % self.__visit_name__)(self, **kw)
- # build a nicely formatted docstring and attach to _compiler_dispatch
- def docstring_container():
- """Look for an attribute named "visit_" + self.__visit_name__
+
- cls._compiler_dispatch = _compiler_dispatch
++ _compiler_dispatch.__doc__ = \
++ """Look for an attribute named "visit_" + self.__visit_name__
+ on the visitor, and call it with the same kw params.
+ """
- _compiler_dispatch.__doc__ = docstring_container.__doc__
+ return _compiler_dispatch
class Visitable(object):
"""Base class for visitable objects, applies the
__visit_name__ = "type_decorator"
def __init__(self, *args, **kwargs):
- """Initialize the instance accepting the same kind of parameters
- as the type being wrapped (as specified in the 'impl' class
- attribute).
-
- Assuming the class level 'impl' is a callable (the common case),
- it will be instantiated and assigned to a self.impl instance
- attribute (thus overriding the class attribute of the same name).
+ """Construct a :class:`.TypeDecorator`.
+
+ Arguments sent here are passed to the constructor
+ of the class assigned to the ``impl`` class level attribute,
- where the ``self.impl`` attribute is assigned an instance
- of the implementation type. If ``impl`` at the class level
- is already an instance, then it's assigned to ``self.impl``
- as is.
++ assuming the ``impl`` is a callable, and the resulting
++ object is assigned to the ``self.impl`` instance attribute
++ (thus overriding the class attribute of the same name).
+
- If the class level 'impl' is not a callable (the unusual case),
- it will be assigned to the same instance attribute 'as-is'.
++ If the class level ``impl`` is not a callable (the unusual case),
++ it will be assigned to the same instance attribute 'as-is',
++ ignoring those arguments passed to the constructor.
+
+ Subclasses can override this to customize the generation
- of ``self.impl``.
++ of ``self.impl`` entirely.
+
"""
++
if not hasattr(self.__class__, 'impl'):
raise AssertionError("TypeDecorator implementations "
"require a class-level variable "
tt.impl = typedesc
return tt
- @util.memoized_property
+ @property
def _type_affinity(self):
+ """
+ #todo
+ """
return self.impl._type_affinity
def type_engine(self, dialect):
return getattr(self.impl, key)
def process_bind_param(self, value, dialect):
- """Subclasses should implement this method to operate on
- the value before loading it to the database.
+ """Receive a bound parameter value to be converted.
+
+ Subclasses override this method to return the
+ value that should be passed along to the underlying
+ :class:`.TypeEngine` object, and from there to the
+ DBAPI ``execute()`` method.
- :param value: the value. Can be None.
+ The operation could be anything desired to perform custom
+ behavior, such as transforming or serializing data.
+ This could also be used as a hook for validating logic.
+
+ This operation should be designed with the reverse operation
+ in mind, which would be the process_result_value method of
+ this class.
+
- If processing is not necessary, the method should
- return ``None``.
-
+ :param value: Data to operate upon, of any type expected by
- this method in the subclass.
- :param dialect: Dialect instance in use.
++ this method in the subclass. Can be ``None``.
+ :param dialect: the :class:`.Dialect` in use.
+
"""
- #can we remove this raise given the behavior of self.bind_processor?
++
raise NotImplementedError()
def process_result_value(self, value, dialect):
- """Subclasses should implement this method to operate on data
+ """Receive a result-row column value to be converted.
+
++ Subclasses should implement this method to operate on data
+ fetched from the database.
+
+ Subclasses override this method to return the
+ value that should be passed back to the application,
+ given a value that is already processed by
+ the underlying :class:`.TypeEngine` object, originally
+ from the DBAPI cursor method ``fetchone()`` or similar.
+
- :param value: the value. Can be None.
+ The operation could be anything desired to perform custom
+ behavior, such as transforming or serializing data.
+ This could also be used as a hook for validating logic.
+
++ :param value: Data to operate upon, of any type expected by
++ this method in the subclass. Can be ``None``.
+ :param dialect: the :class:`.Dialect` in use.
+
+ This operation should be designed to be reversible by
+ the "process_bind_param" method of this class.
+
- If processing is not necessary, the method should
- return ``None``."""
- #can we remove this raise given the behavior of self.result_processor?
+ """
++
raise NotImplementedError()
def bind_processor(self, dialect):
- """Provide a bound value processing function for the given :class:`.Dialect`.
- """Returns the appropriate callable to handle the conversion of
- values *before* being sent to the database.
-
- "Appropriate" in this case means that the callable will know
- the dialect (baked in as a closure), and will know which result
- processor to call, based on the following rule:
-
- * If this class's process_result_value is implemented,
- it will be as the result processor.
++ """Provide a bound value processing function for the
++ given :class:`.Dialect`.
- * If this class's process_result_value is *not* implemented,
- then the normal result_processor method from the underlying
- datatype will be used. (The data type which this class is
- wrapping, as defined in the class attribute "impl".)
+ This is the method that fulfills the :class:`.TypeEngine`
+ contract for bound value conversion. :class:`.TypeDecorator`
+ will wrap a user-defined implementation of
+ :meth:`process_bind_param` here.
- This instance method with the following parameters:
+ User-defined code can override this method directly,
+ though its likely best to use :meth:`process_bind_param` so that
+ the processing provided by ``self.impl`` is maintained.
- :param dialect: Dialect instance in use.
++ :param dialect: Dialect instance in use.
+
+ This method is the reverse counterpart to the
+ :meth:`result_processor` method of this class.
++
"""
if self.__class__.process_bind_param.func_code \
is not TypeDecorator.process_bind_param.func_code:
return self.impl.bind_processor(dialect)
def result_processor(self, dialect, coltype):
- """Returns the appropriate callable to handle the conversion of
- values *after* being sent to the database.
-
- "Appropriate" in this case means that the callable will know
- the dialect and coltype (baked in as a closure), and will know
- which result processor to call, based on the following rule:
-
- * If this class's process_result_value is implemented,
- it will be as the result processor.
+ """Provide a result value processing function for the given :class:`.Dialect`.
- * If this class's process_result_value is *not*
- implemented, then the normal result_processor method
- from the underlying datatype will be used. (The data type
- which this class is wrapping, as defined in the class
- attribute "impl".)
+ This is the method that fulfills the :class:`.TypeEngine`
+ contract for result value conversion. :class:`.TypeDecorator`
+ will wrap a user-defined implementation of
+ :meth:`process_result_value` here.
- :param dialect: Dialect instance in use.
- :param coltype: An SQLAlchemy data type
+ User-defined code can override this method directly,
+ though its likely best to use :meth:`process_result_value` so that
+ the processing provided by ``self.impl`` is maintained.
+
++ :param dialect: Dialect instance in use.
++ :param coltype: An SQLAlchemy data type
+
+ This method is the reverse counterpart to the
+ :meth:`bind_processor` method of this class.
++
"""
if self.__class__.process_result_value.func_code \
is not TypeDecorator.process_result_value.func_code:
return self.coerce_compared_value(op, value)
def copy(self):
- """Clone this instance using the same class, copying all state."""
+ """Produce a copy of this :class:`.TypeDecorator` instance.
+
+ This is a shallow copy and is provided to fulfill part of
+ the :class:`.TypeEngine` contract. It usually does not
+ need to be overridden unless the user-defined :class:`.TypeDecorator`
+ has local state that should be deep-copied.
+
+ """
++
instance = self.__class__.__new__(self.__class__)
instance.__dict__.update(self.__dict__)
return instance