diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-04-02 17:22:16 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-04-02 17:22:16 -0400 |
commit | e1c47d12bf871bf30a149c7fc167d5d22ddaa222 (patch) | |
tree | 94e26bd6a8d72589278548a874a7fcd54598b437 | |
parent | d057f98b8a0eb5f993bdcc7748b486fe5c31d392 (diff) | |
download | sqlalchemy-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-- | CHANGES | 8 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/__init__.py | 24 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/attributes.py | 11 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/properties.py | 25 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/strategies.py | 1 | ||||
-rw-r--r-- | lib/sqlalchemy/schema.py | 5 | ||||
-rw-r--r-- | test/orm/test_mapper.py | 46 |
7 files changed, 102 insertions, 18 deletions
@@ -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') |