Commits

Mike Bayer committed c51a9eb

- beef up the "augmenting existing types" section
- add docstirngs for everything TypeDecorator [ticket:2086]

Comments (0)

Files changed (2)

doc/build/core/types.rst

 Overriding Type Compilation
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-The string produced by any type object, when rendered in a CREATE TABLE 
-statement or other SQL function like CAST, can be changed.  See the
-section :ref:`type_compilation_extension`, a subsection of 
-:ref:`sqlalchemy.ext.compiler_toplevel`, for a short example.
+A frequent need is to force the "string" version of a type, that is
+the one rendered in a CREATE TABLE statement or other SQL function 
+like CAST, to be changed.   For example, an application may want
+to force the rendering of ``BINARY`` for all platforms
+except for one, in which is wants ``BLOB`` to be rendered.  Usage 
+of an existing generic type, in this case :class:`.LargeBinary`, is
+preferred for most use cases.  But to control
+types more accurately, a compilation directive that is per-dialect
+can be associated with any type::
+
+    from sqlalchemy.ext.compiler import compiles
+    from sqlalchemy.types import BINARY
+
+    @compiles(BINARY, "sqlite")
+    def compile_binary_sqlite(type_, compiler, **kw):
+        return "BLOB"
+
+The above code allows the usage of :class:`.types.BINARY`, which
+will produce the string ``BINARY`` against all backends except SQLite, 
+in which case it will produce ``BLOB``.
+
+See the section :ref:`type_compilation_extension`, a subsection of 
+:ref:`sqlalchemy.ext.compiler_toplevel`, for additional examples.
 
 Augmenting Existing Types
 ~~~~~~~~~~~~~~~~~~~~~~~~~

lib/sqlalchemy/types.py

         return dialect.type_descriptor(self)
 
     def adapt(self, cls, **kw):
+        """Produce an "adapted" form of this type, given an "impl" class 
+        to work with. 
+        
+        This method is used internally to associate generic 
+        types with "implementation" types that are specific to a particular
+        dialect.  
+        """
         return util.constructor_copy(self, cls, **kw)
 
     def _coerce_compared_value(self, op, value):
         return self._type_affinity is other._type_affinity
 
     def compile(self, dialect=None):
+        """Produce a string-compiled form of this :class:`.TypeEngine`.
+        
+        When called with no arguments, uses a "default" dialect
+        to produce a string result.
+        
+        :param dialect: a :class:`.Dialect` instance.
+        
+        """
         # arg, return value is inconsistent with
         # ClauseElement.compile()....this is a mistake.
 
     __visit_name__ = "type_decorator"
 
     def __init__(self, *args, **kwargs):
+        """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.
+        
+        Subclasses can override this to customize the generation
+        of ``self.impl``.
+        
+        """
         if not hasattr(self.__class__, 'impl'):
             raise AssertionError("TypeDecorator implementations "
                                  "require a class-level variable "
         return self.impl._type_affinity
 
     def type_engine(self, dialect):
-        """Return a TypeEngine instance for this TypeDecorator.
+        """Return a dialect-specific :class:`.TypeEngine` instance for this :class:`.TypeDecorator`.
+        
+        In most cases this returns a dialect-adapted form of
+        the :class:`.TypeEngine` type represented by ``self.impl``.
 
         """
         adapted = dialect.type_descriptor(self)
         return getattr(self.impl, key)
 
     def process_bind_param(self, value, dialect):
+        """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.
+        :param dialect: the :class:`.Dialect` in use.
+        
+        """
         raise NotImplementedError()
 
     def process_result_value(self, value, dialect):
+        """Receive a result-row column value to be converted.
+        
+        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.
+        :param dialect: the :class:`.Dialect` in use.
+        
+        """
         raise NotImplementedError()
 
     def bind_processor(self, dialect):
+        """Provide a bound value processing function for the given :class:`.Dialect`.
+        
+        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.  
+        
+        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.
+        
+        """
         if self.__class__.process_bind_param.func_code \
             is not TypeDecorator.process_bind_param.func_code:
             process_param = self.process_bind_param
             return self.impl.bind_processor(dialect)
 
     def result_processor(self, dialect, coltype):
+        """Provide a result value processing function for the given :class:`.Dialect`.
+        
+        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.  
+
+        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.
+        
+        """
         if self.__class__.process_result_value.func_code \
             is not TypeDecorator.process_result_value.func_code:
             process_value = self.process_result_value
         return self.coerce_compared_value(op, value)
 
     def copy(self):
+        """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
 
     def get_dbapi_type(self, dbapi):
+        """Return the DBAPI type object represented by this :class:`.TypeDecorator`.
+        
+        By default this calls upon :meth:`.TypeEngine.get_dbapi_type` of the 
+        underlying "impl".  
+        """
         return self.impl.get_dbapi_type(dbapi)
 
     def copy_value(self, value):
+        """Given a value, produce a copy of it.
+        
+        By default this calls upon :meth:`.TypeEngine.copy_value` 
+        of the underlying "impl".
+        
+        :meth:`.copy_value` will return the object
+        itself, assuming "mutability" is not enabled.  
+        Only the :class:`.MutableType` mixin provides a copy 
+        function that actually produces a new object.
+        The copying function is used by the ORM when
+        "mutable" types are used, to memoize the original
+        version of an object as loaded from the database,
+        which is then compared to the possibly mutated
+        version to check for changes.
+        
+        Modern implementations should use the 
+        ``sqlalchemy.ext.mutable`` extension described in
+        :ref:`mutable_toplevel` for intercepting in-place
+        changes to values.
+        
+        """
         return self.impl.copy_value(value)
 
     def compare_values(self, x, y):
+        """Given two values, compare them for equality.
+        
+        By default this calls upon :meth:`.TypeEngine.compare_values` 
+        of the underlying "impl", which in turn usually
+        uses the Python equals operator ``==``.
+        
+        This function is used by the ORM to compare
+        an original-loaded value with an intercepted
+        "changed" value, to determine if a net change
+        has occurred.
+        
+        """
         return self.impl.compare_values(x, y)
 
     def is_mutable(self):