summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2010-04-02 17:22:16 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2010-04-02 17:22:16 -0400
commite1c47d12bf871bf30a149c7fc167d5d22ddaa222 (patch)
tree94e26bd6a8d72589278548a874a7fcd54598b437
parentd057f98b8a0eb5f993bdcc7748b486fe5c31d392 (diff)
downloadsqlalchemy-e1c47d12bf871bf30a149c7fc167d5d22ddaa222.tar.gz
- The ORM will set the docstring of all generated descriptors
to None by default. This can be overridden using 'doc' (or if using Sphinx, attribute docstrings work too). - Added kw argument 'doc' to all mapper property callables as well as Column(). Will assemble the string 'doc' as the '__doc__' attribute on the descriptor.
-rw-r--r--CHANGES8
-rw-r--r--lib/sqlalchemy/orm/__init__.py24
-rw-r--r--lib/sqlalchemy/orm/attributes.py11
-rw-r--r--lib/sqlalchemy/orm/properties.py25
-rw-r--r--lib/sqlalchemy/orm/strategies.py1
-rw-r--r--lib/sqlalchemy/schema.py5
-rw-r--r--test/orm/test_mapper.py46
7 files changed, 102 insertions, 18 deletions
diff --git a/CHANGES b/CHANGES
index 483129591..34458da77 100644
--- a/CHANGES
+++ b/CHANGES
@@ -34,6 +34,14 @@ CHANGES
- id(obj) is no longer used internally within topological.py,
as the sorting functions now require hashable objects
only. [ticket:1756]
+
+ - The ORM will set the docstring of all generated descriptors
+ to None by default. This can be overridden using 'doc'
+ (or if using Sphinx, attribute docstrings work too).
+
+ - Added kw argument 'doc' to all mapper property callables
+ as well as Column(). Will assemble the string 'doc' as
+ the '__doc__' attribute on the descriptor.
- sql
- Restored some bind-labeling logic from 0.5 which ensures
diff --git a/lib/sqlalchemy/orm/__init__.py b/lib/sqlalchemy/orm/__init__.py
index 206c8d0c2..c2f6337bc 100644
--- a/lib/sqlalchemy/orm/__init__.py
+++ b/lib/sqlalchemy/orm/__init__.py
@@ -266,6 +266,9 @@ def relationship(argument, secondary=None, **kwargs):
a class which extends :class:`RelationshipProperty.Comparator` which
provides custom SQL clause generation for comparison operations.
+ :param doc:
+ docstring which will be applied to the resulting descriptor.
+
:param extension:
an :class:`AttributeExtension` instance, or list of extensions,
which will be prepended to the list of attribute listeners for
@@ -469,7 +472,7 @@ def relation(*arg, **kw):
def dynamic_loader(argument, secondary=None, primaryjoin=None,
secondaryjoin=None, foreign_keys=None, backref=None,
post_update=False, cascade=False, remote_side=None,
- enable_typechecks=True, passive_deletes=False,
+ enable_typechecks=True, passive_deletes=False, doc=None,
order_by=None, comparator_factory=None, query_class=None):
"""Construct a dynamically-loading mapper property.
@@ -508,7 +511,7 @@ def dynamic_loader(argument, secondary=None, primaryjoin=None,
secondaryjoin=secondaryjoin, foreign_keys=foreign_keys, backref=backref,
post_update=post_update, cascade=cascade, remote_side=remote_side,
enable_typechecks=enable_typechecks, passive_deletes=passive_deletes,
- order_by=order_by, comparator_factory=comparator_factory,
+ order_by=order_by, comparator_factory=comparator_factory,doc=doc,
strategy_class=DynaLoader, query_class=query_class)
def column_property(*args, **kwargs):
@@ -538,7 +541,11 @@ def column_property(*args, **kwargs):
it does not load immediately, and is instead loaded when the
attribute is first accessed on an instance. See also
:func:`~sqlalchemy.orm.deferred`.
-
+
+ doc
+ optional string that will be applied as the doc on the
+ class-bound descriptor.
+
extension
an :class:`~sqlalchemy.orm.interfaces.AttributeExtension` instance,
or list of extensions, which will be prepended to the list of
@@ -612,6 +619,10 @@ def composite(class_, *cols, **kwargs):
a class which extends ``sqlalchemy.orm.properties.CompositeProperty.Comparator``
which provides custom SQL clause generation for comparison operations.
+ doc
+ optional string that will be applied as the doc on the
+ class-bound descriptor.
+
extension
an :class:`~sqlalchemy.orm.interfaces.AttributeExtension` instance,
or list of extensions, which will be prepended to the list of
@@ -813,7 +824,7 @@ def mapper(class_, local_table=None, *args, **params):
"""
return Mapper(class_, local_table, *args, **params)
-def synonym(name, map_column=False, descriptor=None, comparator_factory=None):
+def synonym(name, map_column=False, descriptor=None, comparator_factory=None, doc=None):
"""Set up `name` as a synonym to another mapped property.
Used with the ``properties`` dictionary sent to :func:`~sqlalchemy.orm.mapper`.
@@ -851,7 +862,10 @@ def synonym(name, map_column=False, descriptor=None, comparator_factory=None):
proxy access to the column-based attribute.
"""
- return SynonymProperty(name, map_column=map_column, descriptor=descriptor, comparator_factory=comparator_factory)
+ return SynonymProperty(name, map_column=map_column,
+ descriptor=descriptor,
+ comparator_factory=comparator_factory,
+ doc=doc)
def comparable_property(comparator_factory, descriptor=None):
"""Provide query semantics for an unmanaged attribute.
diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py
index 887d9a9c1..b631ea2c9 100644
--- a/lib/sqlalchemy/orm/attributes.py
+++ b/lib/sqlalchemy/orm/attributes.py
@@ -1366,12 +1366,12 @@ def unregister_class(class_):
instrumentation_registry.unregister(class_)
def register_attribute(class_, key, **kw):
-
proxy_property = kw.pop('proxy_property', None)
comparator = kw.pop('comparator', None)
parententity = kw.pop('parententity', None)
- register_descriptor(class_, key, proxy_property, comparator, parententity)
+ doc = kw.pop('doc', None)
+ register_descriptor(class_, key, proxy_property, comparator, parententity, doc=doc)
if not proxy_property:
register_attribute_impl(class_, key, **kw)
@@ -1405,7 +1405,8 @@ def register_attribute_impl(class_, key,
manager.post_configure_attribute(key)
-def register_descriptor(class_, key, proxy_property=None, comparator=None, parententity=None, property_=None):
+def register_descriptor(class_, key, proxy_property=None, comparator=None,
+ parententity=None, property_=None, doc=None):
manager = manager_of_class(class_)
if proxy_property:
@@ -1413,7 +1414,9 @@ def register_descriptor(class_, key, proxy_property=None, comparator=None, paren
descriptor = proxy_type(key, proxy_property, comparator, parententity)
else:
descriptor = InstrumentedAttribute(key, comparator=comparator, parententity=parententity)
-
+
+ descriptor.__doc__ = doc
+
manager.instrument_attribute(key, descriptor)
def unregister_attribute(class_, key):
diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py
index 2a5e92c1a..41024101b 100644
--- a/lib/sqlalchemy/orm/properties.py
+++ b/lib/sqlalchemy/orm/properties.py
@@ -58,6 +58,8 @@ class ColumnProperty(StrategizedProperty):
self.comparator_factory = kwargs.pop('comparator_factory', self.__class__.Comparator)
self.descriptor = kwargs.pop('descriptor', None)
self.extension = kwargs.pop('extension', None)
+ self.doc = kwargs.pop('doc', getattr(columns[0], 'doc', None))
+
if kwargs:
raise TypeError(
"%s received unexpected keyword argument(s): %s" % (
@@ -80,7 +82,8 @@ class ColumnProperty(StrategizedProperty):
self.key,
comparator=self.comparator_factory(self, mapper),
parententity=mapper,
- property_=self
+ property_=self,
+ doc=self.doc
)
def do_init(self):
@@ -259,11 +262,12 @@ class SynonymProperty(MapperProperty):
extension = None
- def __init__(self, name, map_column=None, descriptor=None, comparator_factory=None):
+ def __init__(self, name, map_column=None, descriptor=None, comparator_factory=None, doc=None):
self.name = name
self.map_column = map_column
self.descriptor = descriptor
self.comparator_factory = comparator_factory
+ self.doc = doc or (descriptor and descriptor.__doc__) or None
util.set_creation_order(self)
def setup(self, context, entity, path, adapter, **kwargs):
@@ -303,7 +307,8 @@ class SynonymProperty(MapperProperty):
comparator=comparator_callable(self, mapper),
parententity=mapper,
property_=self,
- proxy_property=self.descriptor
+ proxy_property=self.descriptor,
+ doc=self.doc
)
def merge(self, session, source_state, source_dict, dest_state, dest_dict, load, _recursive):
@@ -316,9 +321,10 @@ class ComparableProperty(MapperProperty):
extension = None
- def __init__(self, comparator_factory, descriptor=None):
+ def __init__(self, comparator_factory, descriptor=None, doc=None):
self.descriptor = descriptor
self.comparator_factory = comparator_factory
+ self.doc = doc or (descriptor and descriptor.__doc__) or None
util.set_creation_order(self)
def instrument_class(self, mapper):
@@ -330,7 +336,8 @@ class ComparableProperty(MapperProperty):
comparator=self.comparator_factory(self, mapper),
parententity=mapper,
property_=self,
- proxy_property=self.descriptor
+ proxy_property=self.descriptor,
+ doc=self.doc,
)
def setup(self, context, entity, path, adapter, **kwargs):
@@ -364,6 +371,7 @@ class RelationshipProperty(StrategizedProperty):
enable_typechecks=True, join_depth=None,
comparator_factory=None,
single_parent=False, innerjoin=False,
+ doc=None,
strategy_class=None, _local_remote_pairs=None, query_class=None):
self.uselist = uselist
@@ -384,7 +392,7 @@ class RelationshipProperty(StrategizedProperty):
self.enable_typechecks = enable_typechecks
self.query_class = query_class
self.innerjoin = innerjoin
-
+ self.doc = doc
self.join_depth = join_depth
self.local_remote_pairs = _local_remote_pairs
self.extension = extension
@@ -433,7 +441,8 @@ class RelationshipProperty(StrategizedProperty):
self.key,
comparator=self.comparator_factory(self, mapper),
parententity=mapper,
- property_=self
+ property_=self,
+ doc=self.doc,
)
class Comparator(PropComparator):
@@ -1149,7 +1158,7 @@ class RelationshipProperty(StrategizedProperty):
parent = self.parent.primary_mapper()
kwargs.setdefault('viewonly', self.viewonly)
kwargs.setdefault('post_update', self.post_update)
-
+
self.back_populates = backref_key
relationship = RelationshipProperty(
parent,
diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py
index 93b1170f4..39657564a 100644
--- a/lib/sqlalchemy/orm/strategies.py
+++ b/lib/sqlalchemy/orm/strategies.py
@@ -66,6 +66,7 @@ def _register_attribute(strategy, mapper, useobject,
callable_=callable_,
active_history=active_history,
impl_class=impl_class,
+ doc=prop.doc,
**kw
)
diff --git a/lib/sqlalchemy/schema.py b/lib/sqlalchemy/schema.py
index 8ffb68a4e..0e03be686 100644
--- a/lib/sqlalchemy/schema.py
+++ b/lib/sqlalchemy/schema.py
@@ -535,6 +535,10 @@ class Column(SchemaItem, expression.ColumnClause):
Contrast this argument to ``server_default`` which creates a
default generator on the database side.
+ :param doc: optional String that can be used by the ORM or similar
+ to document attributes. This attribute does not render SQL
+ comments (a future attribute 'comment' will achieve that).
+
:param key: An optional string identifier which will identify this
``Column`` object on the :class:`Table`. When a key is provided,
this is the only identifier referencing the ``Column`` within the
@@ -651,6 +655,7 @@ class Column(SchemaItem, expression.ColumnClause):
self.index = kwargs.pop('index', None)
self.unique = kwargs.pop('unique', None)
self.quote = kwargs.pop('quote', None)
+ self.doc = kwargs.pop('doc', None)
self.onupdate = kwargs.pop('onupdate', None)
self.autoincrement = kwargs.pop('autoincrement', True)
self.constraints = set()
diff --git a/test/orm/test_mapper.py b/test/orm/test_mapper.py
index 02be04edc..6687a9a61 100644
--- a/test/orm/test_mapper.py
+++ b/test/orm/test_mapper.py
@@ -1079,7 +1079,51 @@ class MapperTest(_fixtures.FixtureTest):
mapper(B, users)
-
+class DocumentTest(_base.MappedTest):
+ @classmethod
+ def define_tables(cls, metadata):
+ Table('t1', metadata,
+ Column('col1', Integer, primary_key=True, doc="primary key column"),
+ Column('col2', String, doc="data col"),
+ Column('col3', String, doc="data col 2"),
+ Column('col4', String, doc="data col 3"),
+ Column('col5', String),
+ )
+ Table('t2', metadata,
+ Column('col1', Integer, primary_key=True, doc="primary key column"),
+ Column('col2', String, doc="data col"),
+ Column('col3', Integer, ForeignKey('t1.col1'), doc="foreign key to t1.col1")
+ )
+
+ @testing.resolve_artifact_names
+ def test_doc_propagate(self):
+ class Foo(object):
+ pass
+
+ class Bar(object):
+ pass
+
+ mapper(Foo, t1, properties={
+ 'bars':relationship(Bar,
+ doc="bar relationship",
+ backref=backref('foo',doc='foo relationship')
+ ),
+ 'foober':column_property(t1.c.col3, doc='alternate data col'),
+ 'hoho':synonym(t1.c.col4, doc="syn of col4")
+ })
+ mapper(Bar, t2)
+ compile_mappers()
+ eq_(Foo.col1.__doc__, "primary key column")
+ eq_(Foo.col2.__doc__, "data col")
+ eq_(Foo.col5.__doc__, None)
+ eq_(Foo.foober.__doc__, "alternate data col")
+ eq_(Foo.bars.__doc__, "bar relationship")
+ eq_(Foo.hoho.__doc__, "syn of col4")
+ eq_(Bar.col1.__doc__, "primary key column")
+ eq_(Bar.foo.__doc__, "foo relationship")
+
+
+
class OptionsTest(_fixtures.FixtureTest):
@testing.fails_on('maxdb', 'FIXME: unknown')