From: Mike Bayer Date: Wed, 30 Mar 2011 18:07:27 +0000 (-0400) Subject: - beef up the "augmenting existing types" section X-Git-Tag: rel_0_7b4~43 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b99cd60cb893220d025bca1d40b916f0d7a4754e;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - beef up the "augmenting existing types" section - add docstirngs for everything TypeDecorator [ticket:2086] --- diff --git a/doc/build/core/types.rst b/doc/build/core/types.rst index 1590041185..74a04a8c09 100644 --- a/doc/build/core/types.rst +++ b/doc/build/core/types.rst @@ -245,10 +245,29 @@ as well as to provide new ones. 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 ~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/lib/sqlalchemy/types.py b/lib/sqlalchemy/types.py index de74b20319..1a894ce63e 100644 --- a/lib/sqlalchemy/types.py +++ b/lib/sqlalchemy/types.py @@ -184,6 +184,13 @@ class TypeEngine(AbstractType): 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): @@ -216,6 +223,14 @@ class TypeEngine(AbstractType): 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. @@ -391,6 +406,19 @@ class TypeDecorator(TypeEngine): __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 " @@ -422,7 +450,10 @@ class TypeDecorator(TypeEngine): 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) @@ -449,12 +480,47 @@ class TypeDecorator(TypeEngine): 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 @@ -472,6 +538,18 @@ class TypeDecorator(TypeEngine): 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 @@ -513,17 +591,63 @@ class TypeDecorator(TypeEngine): 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):