Commits

Mike Bayer committed be872b3 Merge

add most of Brad Allen's doc updates, [ticket:2434]

Comments (0)

Files changed (3)

doc/build/core/types.rst

 ~~~~~~~~~~~~~~~~~~
 
 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:

lib/sqlalchemy/sql/visitors.py

     '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 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)
             return
 
-        _generate_dispatch(cls)
+        cls._compiler_dispatch = _generate_dispatch(cls)
 
         super(VisitableType, cls).__init__(clsname, bases, clsdict)
 
+
 def _generate_dispatch(cls):
-    # set up an optimized visit dispatch function
-    # for use by the compiler
+    """Return an optimized visit dispatch function for the cls
+    for use by the compiler.
+    """
     if '__visit_name__' in cls.__dict__:
         visit_name = cls.__visit_name__
         if isinstance(visit_name, str):
+            # There is an optimization opportunity here because the
+            # the string name of the class's __visit_name__ is known at
+            # this early stage (import time) so it can be pre-constructed.
             getter = operator.attrgetter("visit_%s" % visit_name)
             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)
 
-        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.
+            """
+        return _compiler_dispatch
 
 class Visitable(object):
     """Base class for visitable objects, applies the

lib/sqlalchemy/types.py

           def copy(self):
               return MyType(self.impl.length)
 
-    The class-level "impl" variable is required, and can reference any
+    The class-level "impl" attribute is required, and can reference any
     TypeEngine class.  Alternatively, the load_dialect_impl() method
     can be used to provide different type classes based on the dialect
     given; in this case, the "impl" variable can reference
 
         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', 
+        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 "
 
 
     def _gen_dialect_impl(self, dialect):
+        """
+        #todo
+        """
         adapted = dialect.type_descriptor(self)
         if adapted is not self:
             return adapted
 
     @property
     def _type_affinity(self):
+        """
+        #todo
+        """
         return self.impl._type_affinity
 
     def type_engine(self, dialect):
     def __getattr__(self, key):
         """Proxy all other undefined accessors to the underlying
         implementation."""
-
         return getattr(self.impl, key)
 
     def process_bind_param(self, value, dialect):
         :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.
+
+        :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.
 
         """
+
         raise NotImplementedError()
 
     def process_result_value(self, value, dialect):
         """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.
+
         """
+
         raise NotImplementedError()
 
     def bind_processor(self, dialect):
-        """Provide a bound value processing function for the given :class:`.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`
         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.
+
+        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:
         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:
         has local state that should be deep-copied.
 
         """
+
         instance = self.__class__.__new__(self.__class__)
         instance.__dict__.update(self.__dict__)
         return instance
         return self.impl.is_mutable()
 
     def _adapt_expression(self, op, othertype):
+        """
+        #todo
+        """
         op, typ =self.impl._adapt_expression(op, othertype)
         if typ is self.impl:
             return op, self