diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-08-13 19:45:34 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-08-13 19:45:34 -0400 |
commit | 7fc08fe89af9760750899346cf81bd74e0d9150f (patch) | |
tree | d1a9c485c7795bf33bda15900411aefcec91d193 | |
parent | ea85c7053dc9532a95fd487628768fdfc1ca5c30 (diff) | |
download | sqlalchemy-7fc08fe89af9760750899346cf81bd74e0d9150f.tar.gz |
- The ``info`` parameter has been added to the constructor for
:class:`.SynonymProperty` and :class:`.ComparableProperty`.
- The ``info`` parameter has been added as a constructor argument
to all schema constructs including :class:`.MetaData`,
:class:`.Index`, :class:`.ForeignKey`, :class:`.ForeignKeyConstraint`,
:class:`.UniqueConstraint`, :class:`.PrimaryKeyConstraint`,
:class:`.CheckConstraint`.
fixes #2963
-rw-r--r-- | doc/build/changelog/changelog_10.rst | 17 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/descriptor_props.py | 19 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/schema.py | 54 | ||||
-rw-r--r-- | lib/sqlalchemy/util/langhelpers.py | 4 | ||||
-rw-r--r-- | test/orm/test_mapper.py | 4 | ||||
-rw-r--r-- | test/sql/test_metadata.py | 65 |
6 files changed, 152 insertions, 11 deletions
diff --git a/doc/build/changelog/changelog_10.rst b/doc/build/changelog/changelog_10.rst index c59d7c912..815de72c7 100644 --- a/doc/build/changelog/changelog_10.rst +++ b/doc/build/changelog/changelog_10.rst @@ -18,6 +18,23 @@ .. change:: :tags: orm, feature + :tickets: 2963 + + The ``info`` parameter has been added to the constructor for + :class:`.SynonymProperty` and :class:`.ComparableProperty`. + + .. change:: + :tags: sql, feature + :tickets: 2963 + + The ``info`` parameter has been added as a constructor argument + to all schema constructs including :class:`.MetaData`, + :class:`.Index`, :class:`.ForeignKey`, :class:`.ForeignKeyConstraint`, + :class:`.UniqueConstraint`, :class:`.PrimaryKeyConstraint`, + :class:`.CheckConstraint`. + + .. change:: + :tags: orm, feature :tickets: 2971 The :meth:`.InspectionAttr.info` collection is now moved down to diff --git a/lib/sqlalchemy/orm/descriptor_props.py b/lib/sqlalchemy/orm/descriptor_props.py index 5ed24b8c0..f0f9a6468 100644 --- a/lib/sqlalchemy/orm/descriptor_props.py +++ b/lib/sqlalchemy/orm/descriptor_props.py @@ -496,7 +496,7 @@ class SynonymProperty(DescriptorProperty): def __init__(self, name, map_column=None, descriptor=None, comparator_factory=None, - doc=None): + doc=None, info=None): """Denote an attribute name as a synonym to a mapped property, in that the attribute will mirror the value and expression behavior of another attribute. @@ -531,6 +531,11 @@ class SynonymProperty(DescriptorProperty): conjunction with the ``descriptor`` argument in order to link a user-defined descriptor as a "wrapper" for an existing column. + :param info: Optional data dictionary which will be populated into the + :attr:`.InspectionAttr.info` attribute of this object. + + .. versionadded:: 1.0.0 + :param comparator_factory: A subclass of :class:`.PropComparator` that will provide custom comparison behavior at the SQL expression level. @@ -556,6 +561,8 @@ class SynonymProperty(DescriptorProperty): self.descriptor = descriptor self.comparator_factory = comparator_factory self.doc = doc or (descriptor and descriptor.__doc__) or None + if info: + self.info = info util.set_creation_order(self) @@ -608,7 +615,8 @@ class SynonymProperty(DescriptorProperty): class ComparableProperty(DescriptorProperty): """Instruments a Python property for use in query expressions.""" - def __init__(self, comparator_factory, descriptor=None, doc=None): + def __init__( + self, comparator_factory, descriptor=None, doc=None, info=None): """Provides a method of applying a :class:`.PropComparator` to any Python descriptor attribute. @@ -670,10 +678,17 @@ class ComparableProperty(DescriptorProperty): The like-named descriptor will be automatically retrieved from the mapped class if left blank in a ``properties`` declaration. + :param info: Optional data dictionary which will be populated into the + :attr:`.InspectionAttr.info` attribute of this object. + + .. versionadded:: 1.0.0 + """ self.descriptor = descriptor self.comparator_factory = comparator_factory self.doc = doc or (descriptor and descriptor.__doc__) or None + if info: + self.info = info util.set_creation_order(self) def _comparator_factory(self, mapper): diff --git a/lib/sqlalchemy/sql/schema.py b/lib/sqlalchemy/sql/schema.py index 69b3af306..8099dca75 100644 --- a/lib/sqlalchemy/sql/schema.py +++ b/lib/sqlalchemy/sql/schema.py @@ -76,7 +76,7 @@ class SchemaItem(SchemaEventTarget, visitors.Visitable): return [] def __repr__(self): - return util.generic_repr(self) + return util.generic_repr(self, omit_kwarg=['info']) @property @util.deprecated('0.9', 'Use ``<obj>.name.quote``') @@ -1403,6 +1403,7 @@ class ForeignKey(DialectKWArgs, SchemaItem): def __init__(self, column, _constraint=None, use_alter=False, name=None, onupdate=None, ondelete=None, deferrable=None, initially=None, link_to_name=False, match=None, + info=None, **dialect_kw): """ Construct a column-level FOREIGN KEY. @@ -1453,6 +1454,11 @@ class ForeignKey(DialectKWArgs, SchemaItem): DDL for this constraint. Typical values include SIMPLE, PARTIAL and FULL. + :param info: Optional data dictionary which will be populated into the + :attr:`.SchemaItem.info` attribute of this object. + + .. versionadded:: 1.0.0 + :param \**dialect_kw: Additional keyword arguments are dialect specific, and passed in the form ``<dialectname>_<argname>``. The arguments are ultimately handled by a corresponding @@ -1499,6 +1505,8 @@ class ForeignKey(DialectKWArgs, SchemaItem): self.initially = initially self.link_to_name = link_to_name self.match = match + if info: + self.info = info self._unvalidated_dialect_kw = dialect_kw def __repr__(self): @@ -2223,7 +2231,7 @@ class Constraint(DialectKWArgs, SchemaItem): __visit_name__ = 'constraint' def __init__(self, name=None, deferrable=None, initially=None, - _create_rule=None, + _create_rule=None, info=None, **dialect_kw): """Create a SQL constraint. @@ -2238,6 +2246,11 @@ class Constraint(DialectKWArgs, SchemaItem): Optional string. If set, emit INITIALLY <value> when issuing DDL for this constraint. + :param info: Optional data dictionary which will be populated into the + :attr:`.SchemaItem.info` attribute of this object. + + .. versionadded:: 1.0.0 + :param _create_rule: a callable which is passed the DDLCompiler object during compilation. Returns True or False to signal inline generation of @@ -2265,6 +2278,8 @@ class Constraint(DialectKWArgs, SchemaItem): self.name = name self.deferrable = deferrable self.initially = initially + if info: + self.info = info self._create_rule = _create_rule util.set_creation_order(self) self._validate_dialect_kwargs(dialect_kw) @@ -2381,7 +2396,7 @@ class CheckConstraint(Constraint): """ def __init__(self, sqltext, name=None, deferrable=None, - initially=None, table=None, _create_rule=None, + initially=None, table=None, info=None, _create_rule=None, _autoattach=True): """Construct a CHECK constraint. @@ -2404,10 +2419,15 @@ class CheckConstraint(Constraint): Optional string. If set, emit INITIALLY <value> when issuing DDL for this constraint. + :param info: Optional data dictionary which will be populated into the + :attr:`.SchemaItem.info` attribute of this object. + + .. versionadded:: 1.0.0 + """ super(CheckConstraint, self).\ - __init__(name, deferrable, initially, _create_rule) + __init__(name, deferrable, initially, _create_rule, info=info) self.sqltext = _literal_as_text(sqltext) if table is not None: self._set_parent_with_dispatch(table) @@ -2463,7 +2483,7 @@ class ForeignKeyConstraint(Constraint): def __init__(self, columns, refcolumns, name=None, onupdate=None, ondelete=None, deferrable=None, initially=None, use_alter=False, link_to_name=False, match=None, - table=None, **dialect_kw): + table=None, info=None, **dialect_kw): """Construct a composite-capable FOREIGN KEY. :param columns: A sequence of local column names. The named columns @@ -2508,6 +2528,11 @@ class ForeignKeyConstraint(Constraint): DDL for this constraint. Typical values include SIMPLE, PARTIAL and FULL. + :param info: Optional data dictionary which will be populated into the + :attr:`.SchemaItem.info` attribute of this object. + + .. versionadded:: 1.0.0 + :param \**dialect_kw: Additional keyword arguments are dialect specific, and passed in the form ``<dialectname>_<argname>``. See the documentation regarding an individual dialect at @@ -2517,7 +2542,7 @@ class ForeignKeyConstraint(Constraint): """ super(ForeignKeyConstraint, self).\ - __init__(name, deferrable, initially, **dialect_kw) + __init__(name, deferrable, initially, info=info, **dialect_kw) self.onupdate = onupdate self.ondelete = ondelete @@ -2888,6 +2913,11 @@ class Index(DialectKWArgs, ColumnCollectionMixin, SchemaItem): the index. Works in the same manner as that of :paramref:`.Column.quote`. + :param info=None: Optional data dictionary which will be populated + into the :attr:`.SchemaItem.info` attribute of this object. + + .. versionadded:: 1.0.0 + :param \**kw: Additional keyword arguments not mentioned above are dialect specific, and passed in the form ``<dialectname>_<argname>``. See the documentation regarding an @@ -2910,6 +2940,8 @@ class Index(DialectKWArgs, ColumnCollectionMixin, SchemaItem): self.expressions = expressions self.name = quoted_name(name, kw.pop("quote", None)) self.unique = kw.pop('unique', False) + if 'info' in kw: + self.info = kw.pop('info') self._validate_dialect_kwargs(kw) # will call _set_parent() if table-bound column @@ -3020,7 +3052,8 @@ class MetaData(SchemaItem): def __init__(self, bind=None, reflect=False, schema=None, quote_schema=None, - naming_convention=DEFAULT_NAMING_CONVENTION + naming_convention=DEFAULT_NAMING_CONVENTION, + info=None ): """Create a new MetaData object. @@ -3046,6 +3079,11 @@ class MetaData(SchemaItem): :class:`.Sequence`, and other objects which make usage of the local ``schema`` name. + :param info: Optional data dictionary which will be populated into the + :attr:`.SchemaItem.info` attribute of this object. + + .. versionadded:: 1.0.0 + :param naming_convention: a dictionary referring to values which will establish default naming conventions for :class:`.Constraint` and :class:`.Index` objects, for those objects which are not given @@ -3117,6 +3155,8 @@ class MetaData(SchemaItem): self.tables = util.immutabledict() self.schema = quoted_name(schema, quote_schema) self.naming_convention = naming_convention + if info: + self.info = info self._schemas = set() self._sequences = {} self._fk_memos = collections.defaultdict(list) diff --git a/lib/sqlalchemy/util/langhelpers.py b/lib/sqlalchemy/util/langhelpers.py index 8d6fe5a28..828e8f1f3 100644 --- a/lib/sqlalchemy/util/langhelpers.py +++ b/lib/sqlalchemy/util/langhelpers.py @@ -435,7 +435,7 @@ def unbound_method_to_callable(func_or_cls): return func_or_cls -def generic_repr(obj, additional_kw=(), to_inspect=None): +def generic_repr(obj, additional_kw=(), to_inspect=None, omit_kwarg=()): """Produce a __repr__() based on direct association of the __init__() specification vs. same-named attributes present. @@ -484,6 +484,8 @@ def generic_repr(obj, additional_kw=(), to_inspect=None): output.extend([repr(val) for val in getattr(obj, vargs)]) for arg, defval in kw_args.items(): + if arg in omit_kwarg: + continue try: val = getattr(obj, arg, missing) if val is not missing and val != defval: diff --git a/test/orm/test_mapper.py b/test/orm/test_mapper.py index e33c93977..0a9cbfc71 100644 --- a/test/orm/test_mapper.py +++ b/test/orm/test_mapper.py @@ -414,7 +414,9 @@ class MapperTest(_fixtures.FixtureTest, AssertsCompiledSQL): for constructor, args in [ (column_property, (users.c.name,)), (relationship, (Address,)), - (composite, (MyComposite, 'id', 'name')) + (composite, (MyComposite, 'id', 'name')), + (synonym, 'foo'), + (comparable_property, 'foo') ]: obj = constructor(info={"x": "y"}, *args) eq_(obj.info, {"x": "y"}) diff --git a/test/sql/test_metadata.py b/test/sql/test_metadata.py index 3a252c646..ff2755ab1 100644 --- a/test/sql/test_metadata.py +++ b/test/sql/test_metadata.py @@ -956,6 +956,71 @@ class ToMetaDataTest(fixtures.TestBase, ComparesTables): 'mytable.myid = othertable.myid') +class InfoTest(fixtures.TestBase): + def test_metadata_info(self): + m1 = MetaData() + eq_(m1.info, {}) + + m1 = MetaData(info={"foo": "bar"}) + eq_(m1.info, {"foo": "bar"}) + + def test_foreignkey_constraint_info(self): + fkc = ForeignKeyConstraint(['a'], ['b'], name='bar') + eq_(fkc.info, {}) + + fkc = ForeignKeyConstraint( + ['a'], ['b'], name='bar', info={"foo": "bar"}) + eq_(fkc.info, {"foo": "bar"}) + + def test_foreignkey_info(self): + fkc = ForeignKey('a') + eq_(fkc.info, {}) + + fkc = ForeignKey('a', info={"foo": "bar"}) + eq_(fkc.info, {"foo": "bar"}) + + def test_primarykey_constraint_info(self): + pkc = PrimaryKeyConstraint('a', name='x') + eq_(pkc.info, {}) + + pkc = PrimaryKeyConstraint('a', name='x', info={'foo': 'bar'}) + eq_(pkc.info, {'foo': 'bar'}) + + def test_unique_constraint_info(self): + uc = UniqueConstraint('a', name='x') + eq_(uc.info, {}) + + uc = UniqueConstraint('a', name='x', info={'foo': 'bar'}) + eq_(uc.info, {'foo': 'bar'}) + + def test_check_constraint_info(self): + cc = CheckConstraint('foo=bar', name='x') + eq_(cc.info, {}) + + cc = CheckConstraint('foo=bar', name='x', info={'foo': 'bar'}) + eq_(cc.info, {'foo': 'bar'}) + + def test_index_info(self): + ix = Index('x', 'a') + eq_(ix.info, {}) + + ix = Index('x', 'a', info={'foo': 'bar'}) + eq_(ix.info, {'foo': 'bar'}) + + def test_column_info(self): + c = Column('x', Integer) + eq_(c.info, {}) + + c = Column('x', Integer, info={'foo': 'bar'}) + eq_(c.info, {'foo': 'bar'}) + + def test_table_info(self): + t = Table('x', MetaData()) + eq_(t.info, {}) + + t = Table('x', MetaData(), info={'foo': 'bar'}) + eq_(t.info, {'foo': 'bar'}) + class TableTest(fixtures.TestBase, AssertsCompiledSQL): @testing.skip_if('mssql', 'different col format') |