summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2012-04-03 18:53:39 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2012-04-03 18:53:39 -0400
commitf1bdd4e4bbf8366ff7177ebc3ee6647f32fd414f (patch)
tree3ffef191d48fc13c0595c893ef5ab2bf55db5a9e /test
parent53b4337de3ab4d4abe17ca47903aaaa8664cd50f (diff)
downloadsqlalchemy-f1bdd4e4bbf8366ff7177ebc3ee6647f32fd414f.tar.gz
begin implementing inspection system for #2208
Diffstat (limited to 'test')
-rw-r--r--test/base/test_inspect.py62
-rw-r--r--test/engine/test_reflection.py29
-rw-r--r--test/orm/_fixtures.py45
-rw-r--r--test/orm/test_inspect.py194
-rw-r--r--test/orm/test_mapper.py8
-rw-r--r--test/orm/test_query.py40
6 files changed, 314 insertions, 64 deletions
diff --git a/test/base/test_inspect.py b/test/base/test_inspect.py
new file mode 100644
index 000000000..b95b7d8c5
--- /dev/null
+++ b/test/base/test_inspect.py
@@ -0,0 +1,62 @@
+"""test the inspection registry system."""
+
+from test.lib.testing import eq_, assert_raises
+from sqlalchemy import exc, util
+from sqlalchemy import inspection, inspect
+from test.lib import fixtures
+
+class TestFixture(object):
+ pass
+
+class TestEvents(fixtures.TestBase):
+ """Test class- and instance-level event registration."""
+
+ def tearDown(self):
+ for type_ in list(inspection._registrars):
+ if issubclass(type_, TestFixture):
+ del inspection._registrars[type_]
+
+ def test_def_insp(self):
+ class SomeFoo(TestFixture):
+ pass
+
+ @inspection._inspects(SomeFoo)
+ def insp_somefoo(subject):
+ return {"insp":subject}
+
+ somefoo = SomeFoo()
+ insp = inspect(somefoo)
+ assert insp["insp"] is somefoo
+
+ def test_class_insp(self):
+ class SomeFoo(TestFixture):
+ pass
+
+ @inspection._inspects(SomeFoo)
+ class SomeFooInspect(object):
+ def __init__(self, target):
+ self.target = target
+
+ somefoo = SomeFoo()
+ insp = inspect(somefoo)
+ assert isinstance(insp, SomeFooInspect)
+ assert insp.target is somefoo
+
+ def test_hierarchy_insp(self):
+ class SomeFoo(TestFixture):
+ pass
+
+ class SomeSubFoo(SomeFoo):
+ pass
+
+ @inspection._inspects(SomeFoo)
+ def insp_somefoo(subject):
+ return 1
+
+ @inspection._inspects(SomeSubFoo)
+ def insp_somesubfoo(subject):
+ return 2
+
+ somefoo = SomeFoo()
+ eq_(inspect(SomeFoo()), 1)
+ eq_(inspect(SomeSubFoo()), 2)
diff --git a/test/engine/test_reflection.py b/test/engine/test_reflection.py
index f385a0fa2..e59849d98 100644
--- a/test/engine/test_reflection.py
+++ b/test/engine/test_reflection.py
@@ -1,8 +1,7 @@
from test.lib.testing import eq_, assert_raises, assert_raises_message
import StringIO, unicodedata
from sqlalchemy import types as sql_types
-from sqlalchemy import schema, events, event
-from sqlalchemy.engine.reflection import Inspector
+from sqlalchemy import schema, events, event, inspect
from sqlalchemy import MetaData, Integer, String
from test.lib.schema import Table, Column
import sqlalchemy as sa
@@ -10,8 +9,6 @@ from test.lib import ComparesTables, \
testing, engines, AssertsCompiledSQL
from test.lib import fixtures
-create_inspector = Inspector.from_engine
-
metadata, users = None, None
class ReflectionTest(fixtures.TestBase, ComparesTables):
@@ -773,7 +770,7 @@ class ReflectionTest(fixtures.TestBase, ComparesTables):
def test_inspector_conn_closing(self):
m1 = MetaData()
c = testing.db.connect()
- i = Inspector.from_engine(testing.db)
+ i = inspect(testing.db)
assert not c.closed
@testing.provide_metadata
@@ -1050,7 +1047,7 @@ class UnicodeReflectionTest(fixtures.TestBase):
@testing.requires.unicode_connections
def test_get_names(self):
- inspector = Inspector.from_engine(self.bind)
+ inspector = inspect(self.bind)
names = dict(
(tname, (cname, ixname)) for tname, cname, ixname in self.names
)
@@ -1362,18 +1359,18 @@ class ComponentReflectionTest(fixtures.TestBase):
@testing.requires.schemas
def test_get_schema_names(self):
- insp = Inspector(testing.db)
+ insp = inspect(testing.db)
self.assert_('test_schema' in insp.get_schema_names())
def test_dialect_initialize(self):
engine = engines.testing_engine()
assert not hasattr(engine.dialect, 'default_schema_name')
- insp = Inspector(engine)
+ insp = inspect(engine)
assert hasattr(engine.dialect, 'default_schema_name')
def test_get_default_schema_name(self):
- insp = Inspector(testing.db)
+ insp = inspect(testing.db)
eq_(insp.default_schema_name, testing.db.dialect.default_schema_name)
@testing.provide_metadata
@@ -1384,7 +1381,7 @@ class ComponentReflectionTest(fixtures.TestBase):
meta.create_all()
_create_views(meta.bind, schema)
try:
- insp = Inspector(meta.bind)
+ insp = inspect(meta.bind)
if table_type == 'view':
table_names = insp.get_view_names(schema)
table_names.sort()
@@ -1428,7 +1425,7 @@ class ComponentReflectionTest(fixtures.TestBase):
_create_views(meta.bind, schema)
table_names = ['users_v', 'email_addresses_v']
try:
- insp = Inspector(meta.bind)
+ insp = inspect(meta.bind)
for table_name, table in zip(table_names, (users,
addresses)):
schema_name = schema
@@ -1490,7 +1487,7 @@ class ComponentReflectionTest(fixtures.TestBase):
meta = self.metadata
users, addresses, dingalings = createTables(meta, schema)
meta.create_all()
- insp = Inspector(meta.bind)
+ insp = inspect(meta.bind)
users_pkeys = insp.get_primary_keys(users.name,
schema=schema)
eq_(users_pkeys, ['user_id'])
@@ -1517,7 +1514,7 @@ class ComponentReflectionTest(fixtures.TestBase):
meta = self.metadata
users, addresses, dingalings = createTables(meta, schema)
meta.create_all()
- insp = Inspector(meta.bind)
+ insp = inspect(meta.bind)
expected_schema = schema
# users
users_fkeys = insp.get_foreign_keys(users.name,
@@ -1561,7 +1558,7 @@ class ComponentReflectionTest(fixtures.TestBase):
createIndexes(meta.bind, schema)
# The database may decide to create indexes for foreign keys, etc.
# so there may be more indexes than expected.
- insp = Inspector(meta.bind)
+ insp = inspect(meta.bind)
indexes = insp.get_indexes('users', schema=schema)
expected_indexes = [
{'unique': False,
@@ -1590,7 +1587,7 @@ class ComponentReflectionTest(fixtures.TestBase):
view_name1 = 'users_v'
view_name2 = 'email_addresses_v'
try:
- insp = Inspector(meta.bind)
+ insp = inspect(meta.bind)
v1 = insp.get_view_definition(view_name1, schema=schema)
self.assert_(v1)
v2 = insp.get_view_definition(view_name2, schema=schema)
@@ -1613,7 +1610,7 @@ class ComponentReflectionTest(fixtures.TestBase):
meta = self.metadata
users, addresses, dingalings = createTables(meta, schema)
meta.create_all()
- insp = create_inspector(meta.bind)
+ insp = inspect(meta.bind)
oid = insp.get_table_oid(table_name, schema)
self.assert_(isinstance(oid, (int, long)))
diff --git a/test/orm/_fixtures.py b/test/orm/_fixtures.py
index 5def54e3a..7431a3a83 100644
--- a/test/orm/_fixtures.py
+++ b/test/orm/_fixtures.py
@@ -2,7 +2,8 @@ from sqlalchemy import MetaData, Integer, String, ForeignKey
from sqlalchemy import util
from test.lib.schema import Table
from test.lib.schema import Column
-from sqlalchemy.orm import attributes
+from sqlalchemy.orm import attributes, mapper, relationship, \
+ backref, configure_mappers
from test.lib import fixtures
__all__ = ()
@@ -49,6 +50,48 @@ class FixtureTest(fixtures.MappedTest):
pass
@classmethod
+ def _setup_stock_mapping(cls):
+ Node, composite_pk_table, users, Keyword, items, Dingaling, \
+ order_items, item_keywords, Item, User, dingalings, \
+ Address, keywords, CompositePk, nodes, Order, orders, \
+ addresses = cls.classes.Node, \
+ cls.tables.composite_pk_table, cls.tables.users, \
+ cls.classes.Keyword, cls.tables.items, \
+ cls.classes.Dingaling, cls.tables.order_items, \
+ cls.tables.item_keywords, cls.classes.Item, \
+ cls.classes.User, cls.tables.dingalings, \
+ cls.classes.Address, cls.tables.keywords, \
+ cls.classes.CompositePk, cls.tables.nodes, \
+ cls.classes.Order, cls.tables.orders, cls.tables.addresses
+
+ mapper(User, users, properties={
+ 'addresses':relationship(Address, backref='user', order_by=addresses.c.id),
+ 'orders':relationship(Order, backref='user', order_by=orders.c.id), # o2m, m2o
+ })
+ mapper(Address, addresses, properties={
+ 'dingaling':relationship(Dingaling, uselist=False, backref="address") #o2o
+ })
+ mapper(Dingaling, dingalings)
+ mapper(Order, orders, properties={
+ 'items':relationship(Item, secondary=order_items, order_by=items.c.id), #m2m
+ 'address':relationship(Address), # m2o
+ })
+ mapper(Item, items, properties={
+ 'keywords':relationship(Keyword, secondary=item_keywords) #m2m
+ })
+ mapper(Keyword, keywords)
+
+ mapper(Node, nodes, properties={
+ 'children':relationship(Node,
+ backref=backref('parent', remote_side=[nodes.c.id])
+ )
+ })
+
+ mapper(CompositePk, composite_pk_table)
+
+ configure_mappers()
+
+ @classmethod
def define_tables(cls, metadata):
Table('users', metadata,
Column('id', Integer, primary_key=True, test_needs_autoincrement=True),
diff --git a/test/orm/test_inspect.py b/test/orm/test_inspect.py
new file mode 100644
index 000000000..3c824d849
--- /dev/null
+++ b/test/orm/test_inspect.py
@@ -0,0 +1,194 @@
+"""test the inspection registry system."""
+
+from test.lib.testing import eq_, assert_raises
+from sqlalchemy import exc, util
+from sqlalchemy import inspect
+from test.orm import _fixtures
+from sqlalchemy.orm import class_mapper, synonym
+from sqlalchemy.orm.attributes import instance_state
+
+class TestORMInspection(_fixtures.FixtureTest):
+ @classmethod
+ def setup_mappers(cls):
+ cls._setup_stock_mapping()
+ inspect(cls.classes.User).add_property(
+ "name_syn",synonym("name")
+ )
+
+ def test_class_mapper(self):
+ User = self.classes.User
+
+ assert inspect(User) is class_mapper(User)
+
+ def test_instance_state(self):
+ User = self.classes.User
+ u1 = User()
+
+ assert inspect(u1) is instance_state(u1)
+
+ def test_synonyms(self):
+ User = self.classes.User
+ syn = inspect(User).synonyms
+
+ # TODO: some of the synonym debacle in 0.7
+ # has led User.name_syn.property to be the
+ # ColumnProperty. not sure if we want that
+ # implicit jump in there though, perhaps get Query/etc. to
+ # call upon "effective_property" or something like that
+
+ eq_(inspect(User).synonyms, {
+ "name_syn":class_mapper(User).get_property("name_syn")
+ })
+
+ # TODO: test all these accessors...
+
+"""
+# column collection
+>>> b.columns
+[<id column>, <name column>]
+
+# its a ColumnCollection
+>>> b.columns.id
+<id column>
+
+# i.e. from mapper
+>>> b.primary_key
+(<id column>, )
+
+# i.e. from mapper
+>>> b.local_table
+<user table>
+
+# ColumnProperty
+>>> b.attr.id.columns
+[<id column>]
+
+# but perhaps we use a collection with some helpers
+>>> b.attr.id.columns.first
+<id column>
+
+# and a mapper? its None since this is a column
+>>> b.attr.id.mapper
+None
+
+# attr is basically the _props
+>>> b.attr.keys()
+['id', 'name', 'name_syn', 'addresses']
+
+# b itself is likely just the mapper
+>>> b
+<User mapper>
+
+# get only column attributes
+>>> b.column_attrs
+[<id prop>, <name prop>]
+
+# its a namespace
+>>> b.column_attrs.id
+<id prop>
+
+# get only synonyms
+>>> b.synonyms
+[<name syn prop>]
+
+# get only relationships
+>>> b.relationships
+[<addresses prop>]
+
+# its a namespace
+>>> b.relationships.addresses
+<addresses prop>
+
+# point inspect() at a class level attribute,
+# basically returns ".property"
+>>> b = inspect(User.addresses)
+>>> b
+<addresses prop>
+
+# mapper
+>>> b.mapper
+<Address mapper>
+
+# None columns collection, just like columnprop has empty mapper
+>>> b.columns
+None
+
+# the parent
+>>> b.parent
+<User mapper>
+
+# __clause_element__()
+>>> b.expression
+User.id==Address.user_id
+
+>>> inspect(User.id).expression
+<id column with ORM annotations>
+
+
+# inspect works on instances !
+>>> u1 = User(id=3, name='x')
+>>> b = inspect(u1)
+
+# what's b here ? probably InstanceState
+>>> b
+<InstanceState>
+
+>>> b.attr.keys()
+['id', 'name', 'name_syn', 'addresses']
+
+# this is class level stuff - should this require b.mapper.columns ?
+>>> b.columns
+[<id column>, <name column>]
+
+# does this return '3'? or an object?
+>>> b.attr.id
+<magic attribute inspect thing>
+
+# or does this ?
+>>> b.attr.id.value
+3
+
+>>> b.attr.id.history
+<history object>
+
+>>> b.attr.id.history.unchanged
+3
+
+>>> b.attr.id.history.deleted
+None
+
+# lets assume the object is persistent
+>>> s = Session()
+>>> s.add(u1)
+>>> s.commit()
+
+# big one - the primary key identity ! always
+# works in query.get()
+>>> b.identity
+[3]
+
+# the mapper level key
+>>> b.identity_key
+(User, [3])
+
+>>> b.persistent
+True
+
+>>> b.transient
+False
+
+>>> b.deleted
+False
+
+>>> b.detached
+False
+
+>>> b.session
+<session>
+
+# the object. this navigates obj()
+# of course, would be nice if it was b.obj...
+>>> b.object_
+<User instance u1>
+
+"""
diff --git a/test/orm/test_mapper.py b/test/orm/test_mapper.py
index 1c5f29b71..bec813f39 100644
--- a/test/orm/test_mapper.py
+++ b/test/orm/test_mapper.py
@@ -87,14 +87,6 @@ class MapperTest(_fixtures.FixtureTest, AssertsCompiledSQL):
- def test_prop_accessor(self):
- users, User = self.tables.users, self.classes.User
-
- mapper(User, users)
- assert_raises(NotImplementedError,
- getattr, sa.orm.class_mapper(User), 'properties')
-
-
def test_friendly_attribute_str_on_uncompiled_boom(self):
User, users = self.classes.User, self.tables.users
diff --git a/test/orm/test_query.py b/test/orm/test_query.py
index bcc976816..bd9f10d21 100644
--- a/test/orm/test_query.py
+++ b/test/orm/test_query.py
@@ -29,45 +29,7 @@ class QueryTest(_fixtures.FixtureTest):
@classmethod
def setup_mappers(cls):
- Node, composite_pk_table, users, Keyword, items, Dingaling, \
- order_items, item_keywords, Item, User, dingalings, \
- Address, keywords, CompositePk, nodes, Order, orders, \
- addresses = cls.classes.Node, \
- cls.tables.composite_pk_table, cls.tables.users, \
- cls.classes.Keyword, cls.tables.items, \
- cls.classes.Dingaling, cls.tables.order_items, \
- cls.tables.item_keywords, cls.classes.Item, \
- cls.classes.User, cls.tables.dingalings, \
- cls.classes.Address, cls.tables.keywords, \
- cls.classes.CompositePk, cls.tables.nodes, \
- cls.classes.Order, cls.tables.orders, cls.tables.addresses
-
- mapper(User, users, properties={
- 'addresses':relationship(Address, backref='user', order_by=addresses.c.id),
- 'orders':relationship(Order, backref='user', order_by=orders.c.id), # o2m, m2o
- })
- mapper(Address, addresses, properties={
- 'dingaling':relationship(Dingaling, uselist=False, backref="address") #o2o
- })
- mapper(Dingaling, dingalings)
- mapper(Order, orders, properties={
- 'items':relationship(Item, secondary=order_items, order_by=items.c.id), #m2m
- 'address':relationship(Address), # m2o
- })
- mapper(Item, items, properties={
- 'keywords':relationship(Keyword, secondary=item_keywords) #m2m
- })
- mapper(Keyword, keywords)
-
- mapper(Node, nodes, properties={
- 'children':relationship(Node,
- backref=backref('parent', remote_side=[nodes.c.id])
- )
- })
-
- mapper(CompositePk, composite_pk_table)
-
- configure_mappers()
+ cls._setup_stock_mapping()
class MiscTest(QueryTest):
run_create_tables = None