From: Mike Bayer Date: Wed, 14 Mar 2012 04:07:09 +0000 (-0700) Subject: add most of Brad Allen's doc updates, [ticket:2434] X-Git-Tag: rel_0_7_6~14 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bc3ea419b63f59ca0d079c89a563e1d391199731;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git add most of Brad Allen's doc updates, [ticket:2434] --- bc3ea419b63f59ca0d079c89a563e1d391199731 diff --cc doc/build/core/types.rst index c3fb99f028,e88e7a66c3..064489c9fd --- a/doc/build/core/types.rst +++ b/doc/build/core/types.rst @@@ -440,8 -379,8 +440,10 @@@ extension - see the example in :ref:`mu Creating New Types ~~~~~~~~~~~~~~~~~~ -The :class:`UserDefinedType` class is provided as a simple base class -for defining entirely new database types: +The :class:`.UserDefinedType` class is provided as a simple base class - for defining entirely new database types: ++for defining entirely new database types. Use this to represent native ++database types not known by SQLAlchemy. If only Python translation behavior ++is needed, use :class:`.TypeDecorator` instead. .. autoclass:: UserDefinedType :members: diff --cc lib/sqlalchemy/sql/visitors.py index cdcf40aa86,00d8f94854..59ad290770 --- a/lib/sqlalchemy/sql/visitors.py +++ b/lib/sqlalchemy/sql/visitors.py @@@ -34,11 -34,19 +34,19 @@@ __all__ = ['VisitableType', 'Visitable' '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) @@@ -58,10 -71,18 +71,17 @@@ def _generate_dispatch(cls) 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 diff --cc lib/sqlalchemy/types.py index 8c8e6ebb96,2200511e52..512ac6268e --- a/lib/sqlalchemy/types.py +++ b/lib/sqlalchemy/types.py @@@ -453,19 -390,17 +453,23 @@@ class TypeDecorator(TypeEngine) __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 " @@@ -492,8 -430,11 +499,11 @@@ tt.impl = typedesc return tt - @util.memoized_property + @property def _type_affinity(self): + """ + #todo + """ return self.impl._type_affinity def type_engine(self, dialect): @@@ -534,47 -464,65 +544,74 @@@ 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: @@@ -593,17 -541,27 +630,23 @@@ 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: @@@ -646,14 -604,7 +689,15 @@@ 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