]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
add most of Brad Allen's doc updates, [ticket:2434]
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 14 Mar 2012 04:07:09 +0000 (21:07 -0700)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 14 Mar 2012 04:07:09 +0000 (21:07 -0700)
1  2 
doc/build/core/types.rst
lib/sqlalchemy/sql/visitors.py
lib/sqlalchemy/types.py

index c3fb99f0280d05e0fe289e2e0b13ee3f4e21eb05,e88e7a66c3589eb55a5c4a11a1412fb2e03b6f25..064489c9fd4283142e0b572c35dbe9c1b1998335
@@@ -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:
index cdcf40aa86ba5c7ecaa4d89b15d7f7c84d406f18,00d8f94854e88136fffd815465b7c313a73e3674..59ad2907706007575a3a1f3a679269d277258040
@@@ -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
index 8c8e6ebb962dda86f9932574af6b55c8d2d6648b,2200511e521e2ce748070bb17a7692e111a0f0bd..512ac6268e7dd7079db8789639cc11ba67b0aed2
@@@ -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 "
          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