From: Mike Bayer Date: Thu, 16 Jun 2011 16:09:45 +0000 (-0400) Subject: - Repaired the examples/versioning test runner X-Git-Tag: rel_0_7_2~63 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e30d02c6259d5a2386fd836e27dee75e3a143bd2;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Repaired the examples/versioning test runner to not rely upon SQLAlchemy test libs, nosetests must be run from within examples/versioning to get around setup.cfg breaking it. - Tweak to examples/versioning to pick the correct foreign key in a multi-level inheritance situation. --- diff --git a/CHANGES b/CHANGES index 8a8c6d04f5..f270e5d632 100644 --- a/CHANGES +++ b/CHANGES @@ -32,6 +32,17 @@ CHANGES if Python unicodes are passed under certain circumstances. +- examples + - Repaired the examples/versioning test runner + to not rely upon SQLAlchemy test libs, + nosetests must be run from within + examples/versioning to get around setup.cfg + breaking it. + + - Tweak to examples/versioning to pick the + correct foreign key in a multi-level + inheritance situation. + 0.7.1 ===== - general @@ -45,7 +56,7 @@ CHANGES self-referential relationships - this since SQLA 0.7 no longer enforces "parent with no child" at the ORM level; this check is left - up to foreign key nullability. + up to foreign key nullability. Related to [ticket:1912] - Repaired new "mutable" extension to propagate @@ -87,10 +98,10 @@ CHANGES - Adjusted the __contains__() method of a RowProxy result row such that no exception - throw is generated internally; + throw is generated internally; NoSuchColumnError() also will generate its message regardless of whether or not the column - construct can be coerced to a string. + construct can be coerced to a string. [ticket:2178]. Also in 0.6.8. - sqlite @@ -106,7 +117,7 @@ CHANGES MATCH operator. A potential floating-point inaccuracy issue was fixed, and certain tests of the MATCH operator only execute within an - EN-oriented locale for now. [ticket:2175]. + EN-oriented locale for now. [ticket:2175]. Also in 0.6.8. - mysql @@ -184,7 +195,7 @@ CHANGES - It is an error to call query.get() when the given entity is not a single, full class entity or mapper (i.e. a column). This is - a deprecation warning in 0.6.8. + a deprecation warning in 0.6.8. [ticket:2144] - Fixed a potential KeyError which under some @@ -310,7 +321,7 @@ CHANGES - The changelog for 0.6.7 and subsequent within the 0.6 series is now listed only in the - CHANGES file within the 0.6 branch. + CHANGES file within the 0.6 branch. In the 0.7 CHANGES file (i.e. this file), all the 0.6 changes are listed inline within the 0.7 section in which they were also applied @@ -529,7 +540,7 @@ CHANGES the given objects don't have any inter-attribute references in memory, which was the behavior in 0.5 and earlier, so a flush of Parent/Child with - only foreign key/primary key set will succeed. + only foreign key/primary key set will succeed. This while still maintaining 0.6 and above's not generating a ton of useless internal dependency structures within the flush that don't correspond @@ -539,7 +550,7 @@ CHANGES - Improvements to the error messages emitted when querying against column-only entities in conjunction with (typically incorrectly) using loader options, - where the parent entity is not fully present. + where the parent entity is not fully present. [ticket:2069] - Fixed bug in query.options() whereby a path @@ -579,7 +590,7 @@ CHANGES a Sequence object as its argument and renders the appropriate "next value" generation string on the target platform, if supported. Also provides - ".next_value()" method on Sequence itself. + ".next_value()" method on Sequence itself. [ticket:2085] - func.next_value() or other SQL expression can @@ -944,7 +955,7 @@ CHANGES - Established consistency when server_default is present on an Integer PK column. SQLA doesn't pre-fetch these, - nor do they come back in cursor.lastrowid (DBAPI). + nor do they come back in cursor.lastrowid (DBAPI). Ensured all backends consistently return None in result.inserted_primary_key for these. Regarding reflection for this case, reflection of an int PK col diff --git a/examples/versioning/__init__.py b/examples/versioning/__init__.py index 72edcca530..61b8f13c65 100644 --- a/examples/versioning/__init__.py +++ b/examples/versioning/__init__.py @@ -8,7 +8,8 @@ represents historical versions of the target object. Usage is illustrated via a unit test module ``test_versioning.py``, which can be run via nose:: - nosetests -w examples/versioning/ + cd examples/versioning + nosetests -v A fragment of example usage, using declarative:: diff --git a/examples/versioning/_lib.py b/examples/versioning/_lib.py new file mode 100644 index 0000000000..d5f2cb0b75 --- /dev/null +++ b/examples/versioning/_lib.py @@ -0,0 +1,96 @@ +"""copy of ComparableEntity and eq_() from test.lib. + +This is just to support running the example outside of +the SQLA testing environment which is no longer part of +SQLAlchemy as of 0.7. + +""" + +import sqlalchemy as sa +from sqlalchemy import exc as sa_exc + + +def eq_(a, b, msg=None): + """Assert a == b, with repr messaging on failure.""" + assert a == b, msg or "%r != %r" % (a, b) + +_repr_stack = set() +class BasicEntity(object): + def __init__(self, **kw): + for key, value in kw.iteritems(): + setattr(self, key, value) + + def __repr__(self): + if id(self) in _repr_stack: + return object.__repr__(self) + _repr_stack.add(id(self)) + try: + return "%s(%s)" % ( + (self.__class__.__name__), + ', '.join(["%s=%r" % (key, getattr(self, key)) + for key in sorted(self.__dict__.keys()) + if not key.startswith('_')])) + finally: + _repr_stack.remove(id(self)) + +_recursion_stack = set() +class ComparableEntity(BasicEntity): + def __hash__(self): + return hash(self.__class__) + + def __ne__(self, other): + return not self.__eq__(other) + + def __eq__(self, other): + """'Deep, sparse compare. + + Deeply compare two entities, following the non-None attributes of the + non-persisted object, if possible. + + """ + if other is self: + return True + elif not self.__class__ == other.__class__: + return False + + if id(self) in _recursion_stack: + return True + _recursion_stack.add(id(self)) + + try: + # pick the entity thats not SA persisted as the source + try: + self_key = sa.orm.attributes.instance_state(self).key + except sa.orm.exc.NO_STATE: + self_key = None + + if other is None: + a = self + b = other + elif self_key is not None: + a = other + b = self + else: + a = self + b = other + + for attr in a.__dict__.keys(): + if attr.startswith('_'): + continue + value = getattr(a, attr) + + try: + # handle lazy loader errors + battr = getattr(b, attr) + except (AttributeError, sa_exc.UnboundExecutionError): + return False + + if hasattr(value, '__iter__'): + if list(value) != list(battr): + return False + else: + if value is not None and value != battr: + return False + return True + finally: + _recursion_stack.remove(id(self)) diff --git a/examples/versioning/history_meta.py b/examples/versioning/history_meta.py index 2983a33e21..5f3820f680 100644 --- a/examples/versioning/history_meta.py +++ b/examples/versioning/history_meta.py @@ -35,7 +35,7 @@ def _history_mapper(local_mapper): col.unique = False if super_mapper and col_references_table(column, super_mapper.local_table): - super_fks.append((col.key, list(super_history_mapper.base_mapper.local_table.primary_key)[0])) + super_fks.append((col.key, list(super_history_mapper.local_table.primary_key)[0])) cols.append(col) diff --git a/examples/versioning/test_versioning.py b/examples/versioning/test_versioning.py index d2d270c432..5baf09530c 100644 --- a/examples/versioning/test_versioning.py +++ b/examples/versioning/test_versioning.py @@ -1,16 +1,16 @@ +from unittest import TestCase from sqlalchemy.ext.declarative import declarative_base from history_meta import VersionedMeta, VersionedListener from sqlalchemy import create_engine, Column, Integer, String, ForeignKey from sqlalchemy.orm import clear_mappers, sessionmaker, deferred, relationship -from test.lib.testing import TestBase, eq_ -from test.lib.entities import ComparableEntity +from _lib import ComparableEntity, eq_ def setup(): global engine engine = create_engine('sqlite://', echo=True) -class TestVersioning(fixtures.TestBase): - def setup(self): +class TestVersioning(TestCase): + def setUp(self): global Base, Session, Versioned Base = declarative_base(bind=engine) class Versioned(object): @@ -18,7 +18,7 @@ class TestVersioning(fixtures.TestBase): _decl_class_registry = Base._decl_class_registry Session = sessionmaker(extension=VersionedListener()) - def teardown(self): + def tearDown(self): clear_mappers() Base.metadata.drop_all()