Re: [sqlalchemy] [Q] Lack of isolation in unit tests

2012-09-19 Thread Ladislav Lenart
Hello.

 OK, that's unusual but that is actually the case where clear_mappers() is fine
to use.  SQLAlchemy's own unit tests create new classes for each test because
we're testing SQLAlchemy itself.

 OK, yes this is correct, as the compilation step tries to get at all mappers.
 Sorry I didn't realize this, it's very strange for end-user unit tests to be
testing the creation of ad-hoc mapper configurations.

Thank you for your clarification. At this time I still play with/learn
SQLAlchemy and unit tests are the fastest way to learn it, at least for me. I
don't want to write new module file with a single test case to play with, hence
I define ORM classes locally in each test and test my assumptions about how
SQLAlchemy works :-)

Ladislav Lenart


On 18.9.2012 17:40, Michael Bayer wrote:
 
 On Sep 18, 2012, at 11:36 AM, Ladislav Lenart wrote:
 
 Hello.

 Adding call to clear_mappers() to tearDown fixed my problem.

 For the rest of the discussion I am not sure I completely follow.

 I use declarative exclusively but each test defines its own Base and its own 
 set
 of ORM classes.
 
 
 OK, that's unusual but that is actually the case where clear_mappers() is 
 fine to use.  SQLAlchemy's own unit tests create new classes for each test 
 because we're testing SQLAlchemy itself.
 
 However when one test has a bug in an ORM class definition, ALL
 the following tests fail because of this.
 
 OK, yes this is correct, as the compilation step tries to get at all mappers. 
  Sorry I didn't realize this, it's very strange for end-user unit tests to be 
 testing the creation of ad-hoc mapper configurations.

-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.



[sqlalchemy] [Q] Lack of isolation in unit tests

2012-09-18 Thread Ladislav Lenart
Hello.

I have isolation issues with unit testing with SQLAlchemy. My DbTestCase looks
like this (I use nose test framework):


class DbTestCase(object):
Db-aware test case superclass.
__engine_name__ = 'postgres'
__db_name__ = 'unit_tests'
__echo__ = True

@property
def engine(self):
return self.Base.metadata.bind

def setUp(self):
self.Base = create_base()
self._create_db()
self.connection = self.engine.connect()
self.trans = self.connection.begin()
self.session = Session(bind=self.connection)

def _create_db(self):
# construct conn_string from __engine_name__ and __db_name__
engine = create_engine(conn_string, echo=echo)
self.Base.metadata.bind = engine

def tearDown(self):
# Rollback: Everything that happened with the Session above (including
# calls to commit()) is rolled back.
self.trans.rollback()
self.session.close()

# Return connection to the engine.
self.connection.close()

# Remove all tables while we know what we have defined.
self._drop_all()

def _recreate_all(self):
self._drop_all()
self._create_all()

def _drop_all(self):
self.Base.metadata.drop_all()

def _create_all(self):
self.Base.metadata.create_all()


A complete usage looks like this:

import re
from sqlalchemy import Column, Integer, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base, declared_attr


class _Base(object):
@declared_attr
def __tablename__(cls): #@NoSelf
return camel_case_to_underscore(cls.__name__)


def camel_case_to_underscore(name):
Convert CamelCaseForm to camel_case_form.
Taken from:
http://stackoverflow.com/questions/1175208/elegant-python-function-to-convert-camelcase-to-camel-case

s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()


def create_base():
return declarative_base(cls=_Base)


class Id(object):
Add id column as primary key.

@declared_attr
def id(cls): #@NoSelf
return Column(Integer(), primary_key=True)


class TestFooBar(DbTestCase):
def test_foo(self):
class Foo(self.Base, Id):
qux = relationship('Qux') # -- intentional bug
pass

self._recreate_all()

foo = Foo()
self.session.add(foo)
self.session.flush()
# ...

def test_foo_bar(self):
class Foo(self.Base, Id):
bars = relationship('Bar', back_populates='foo')

class Bar(self.Base, Id):
foo_id = Column(Integer(), ForeignKey('foo.id'), nullable=False)
foo = relationship('Foo', back_populates='bars')


self._recreate_all()

foo = Foo()
bar = Bar(foo=foo)
self.session.add(foo)
self.session.add(bar)
self.session.flush()
# ...

When I run the above two tests, BOTH fail because of the bug in test_foo. The
test test_foo_bar fails with:

InvalidRequestError: One or more mappers failed to initialize - can't proceed
with initialization of other mappers.  Original exception was: When initializing
mapper Mapper|Foo|foo, expression 'Qux' failed to locate a name (name 'Qux' is
not defined). If this is a class name, consider adding this relationship() to
the class 'zfp.tests.model.test_foo.Foo' class after both dependent classes
have been defined.

If I comment the 'qux = ...' line, BOTH tests pass.

I thought that using separate declarative bases, each with its own metadata, is
enough to ensure isolation among tests. Was my assumption wrong? Can I achieve
proper isolation of unit tests somehow?


Thank you,

Ladislav Lenart


-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.



Re: [sqlalchemy] [Q] Lack of isolation in unit tests

2012-09-18 Thread Ladislav Lenart
Hello again.

The problem is in sqlalchemy.orm.mapper. There are globals _mapper_registry and
a boolean _new_mappers that triggers recompilation of the mappers. Is there a
safe way to clear them in each test's case tearDown?

Thank you in advance,

Ladislav Lenart


On 18.9.2012 16:07, Ladislav Lenart wrote:
 Hello.
 
 I have isolation issues with unit testing with SQLAlchemy. My DbTestCase looks
 like this (I use nose test framework):
 
 
 class DbTestCase(object):
 Db-aware test case superclass.
 __engine_name__ = 'postgres'
 __db_name__ = 'unit_tests'
 __echo__ = True
 
 @property
 def engine(self):
 return self.Base.metadata.bind
 
 def setUp(self):
 self.Base = create_base()
 self._create_db()
 self.connection = self.engine.connect()
 self.trans = self.connection.begin()
 self.session = Session(bind=self.connection)
 
 def _create_db(self):
   # construct conn_string from __engine_name__ and __db_name__
 engine = create_engine(conn_string, echo=echo)
 self.Base.metadata.bind = engine
 
 def tearDown(self):
 # Rollback: Everything that happened with the Session above (including
 # calls to commit()) is rolled back.
 self.trans.rollback()
 self.session.close()
 
 # Return connection to the engine.
 self.connection.close()
 
 # Remove all tables while we know what we have defined.
 self._drop_all()
 
 def _recreate_all(self):
 self._drop_all()
 self._create_all()
 
 def _drop_all(self):
 self.Base.metadata.drop_all()
 
 def _create_all(self):
 self.Base.metadata.create_all()
 
 
 A complete usage looks like this:
 
 import re
 from sqlalchemy import Column, Integer, ForeignKey
 from sqlalchemy.orm import relationship
 from sqlalchemy.ext.declarative import declarative_base, declared_attr
 
 
 class _Base(object):
 @declared_attr
 def __tablename__(cls): #@NoSelf
 return camel_case_to_underscore(cls.__name__)
 
 
 def camel_case_to_underscore(name):
 Convert CamelCaseForm to camel_case_form.
 Taken from:
 http://stackoverflow.com/questions/1175208/elegant-python-function-to-convert-camelcase-to-camel-case
 
 s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
 return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
 
 
 def create_base():
 return declarative_base(cls=_Base)
 
 
 class Id(object):
 Add id column as primary key.
 
 @declared_attr
 def id(cls): #@NoSelf
 return Column(Integer(), primary_key=True)
 
 
 class TestFooBar(DbTestCase):
 def test_foo(self):
 class Foo(self.Base, Id):
 qux = relationship('Qux') # -- intentional bug
 pass
 
 self._recreate_all()
 
 foo = Foo()
 self.session.add(foo)
 self.session.flush()
 # ...
 
 def test_foo_bar(self):
 class Foo(self.Base, Id):
 bars = relationship('Bar', back_populates='foo')
 
 class Bar(self.Base, Id):
 foo_id = Column(Integer(), ForeignKey('foo.id'), nullable=False)
 foo = relationship('Foo', back_populates='bars')
 
 
 self._recreate_all()
 
 foo = Foo()
 bar = Bar(foo=foo)
 self.session.add(foo)
 self.session.add(bar)
 self.session.flush()
 # ...
 
 When I run the above two tests, BOTH fail because of the bug in test_foo. The
 test test_foo_bar fails with:
 
 InvalidRequestError: One or more mappers failed to initialize - can't proceed
 with initialization of other mappers.  Original exception was: When 
 initializing
 mapper Mapper|Foo|foo, expression 'Qux' failed to locate a name (name 'Qux' 
 is
 not defined). If this is a class name, consider adding this relationship() to
 the class 'zfp.tests.model.test_foo.Foo' class after both dependent classes
 have been defined.
 
 If I comment the 'qux = ...' line, BOTH tests pass.
 
 I thought that using separate declarative bases, each with its own metadata, 
 is
 enough to ensure isolation among tests. Was my assumption wrong? Can I achieve
 proper isolation of unit tests somehow?
 
 
 Thank you,
 
 Ladislav Lenart
 
 


-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.



Re: [sqlalchemy] [Q] Lack of isolation in unit tests

2012-09-18 Thread Michael Bayer

the _mapper_registry is weak referencing, so doesn't have any impact on mappers 
hanging around or not.   Ultimately, the mapper is associated with your mapped 
class.   The clear_mappers() API call will de-associate mappers from classes 
and remove instrumentation that was affixed by the mapper() call.

However, the vast majority of applications have no need for such a phase, and 
there is no benefit to calling clear_mappers().   This is discussed in the docs 
for clear_mappers(): 
http://docs.sqlalchemy.org/en/rel_0_7/orm/mapper_config.html?highlight=clear_mappers#sqlalchemy.orm.clear_mappers

If you're using declarative, it is especially useless, since the directives 
placed on a declarative class are destroyed by the mapping process in the first 
place, and those classes can't be re-mapped unless you defined the mappings 
again non-declaratively, thus making the usage of declarative in the first 
place completely redundant.

The mapped class is best considered as a single, composed unit.Just like 
when you create a class in your application that has, for example, an 
__init__() method and a get_stuff() method, there's no need when tearing down 
tests to remove the __init__() and get_stuff() methods from those classes; 
these are part of that structure.

A frequent misunderstanding is that mappers have some connection to the 
database, either through engines or connections, and that as long as mappers 
exist, this means we are connected to the database.  This is not at all true, 
any more than a SQL string you might pass to cursor.execute() in DBAPI has any 
relationship to database connections.The mapper() only represents in-memory 
information about how your classes are structured in terms of hypothetical 
database tables.   It has no connection whatsoever to actual database 
connections.

When unit testing, the thing that needs to be torn down is the transactional, 
connection, and Session state, that is, those things which represent per-use 
resources that need to be closed down.An example of how to associate a 
Session with connection resources for the lifespan of a test is here: 
http://docs.sqlalchemy.org/en/rel_0_7/orm/session.html#joining-a-session-into-an-external-transaction
 .It seems like you're already working with this code based on what I see 
below.   



On Sep 18, 2012, at 10:20 AM, Ladislav Lenart wrote:

 Hello again.
 
 The problem is in sqlalchemy.orm.mapper. There are globals _mapper_registry 
 and
 a boolean _new_mappers that triggers recompilation of the mappers. Is there a
 safe way to clear them in each test's case tearDown?
 
 Thank you in advance,
 
 Ladislav Lenart
 
 
 On 18.9.2012 16:07, Ladislav Lenart wrote:
 Hello.
 
 I have isolation issues with unit testing with SQLAlchemy. My DbTestCase 
 looks
 like this (I use nose test framework):
 
 
 class DbTestCase(object):
Db-aware test case superclass.
__engine_name__ = 'postgres'
__db_name__ = 'unit_tests'
__echo__ = True
 
@property
def engine(self):
return self.Base.metadata.bind
 
def setUp(self):
self.Base = create_base()
self._create_db()
self.connection = self.engine.connect()
self.trans = self.connection.begin()
self.session = Session(bind=self.connection)
 
def _create_db(self):
  # construct conn_string from __engine_name__ and __db_name__
engine = create_engine(conn_string, echo=echo)
self.Base.metadata.bind = engine
 
def tearDown(self):
# Rollback: Everything that happened with the Session above (including
# calls to commit()) is rolled back.
self.trans.rollback()
self.session.close()
 
# Return connection to the engine.
self.connection.close()
 
# Remove all tables while we know what we have defined.
self._drop_all()
 
def _recreate_all(self):
self._drop_all()
self._create_all()
 
def _drop_all(self):
self.Base.metadata.drop_all()
 
def _create_all(self):
self.Base.metadata.create_all()
 
 
 A complete usage looks like this:
 
 import re
 from sqlalchemy import Column, Integer, ForeignKey
 from sqlalchemy.orm import relationship
 from sqlalchemy.ext.declarative import declarative_base, declared_attr
 
 
 class _Base(object):
@declared_attr
def __tablename__(cls): #@NoSelf
return camel_case_to_underscore(cls.__name__)
 
 
 def camel_case_to_underscore(name):
Convert CamelCaseForm to camel_case_form.
Taken from:
 http://stackoverflow.com/questions/1175208/elegant-python-function-to-convert-camelcase-to-camel-case

s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
 
 
 def create_base():
return declarative_base(cls=_Base)
 
 
 class Id(object):
Add id column as primary key.
 
@declared_attr
def id(cls): #@NoSelf
return Column(Integer(), primary_key=True)
 
 
 class 

Re: [sqlalchemy] [Q] Lack of isolation in unit tests

2012-09-18 Thread Claudio Freire
On Tue, Sep 18, 2012 at 11:07 AM, Ladislav Lenart lenart...@volny.cz wrote:
 def create_base():
 return declarative_base(cls=_Base)

Move the declaration of _Base to within create_base, and I think that
should fix your problem.

(I've had a similar one, not with test cases, but with a replica schema)

-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.



Re: [sqlalchemy] [Q] Lack of isolation in unit tests

2012-09-18 Thread Ladislav Lenart
Hello.

Adding call to clear_mappers() to tearDown fixed my problem.

For the rest of the discussion I am not sure I completely follow.

I use declarative exclusively but each test defines its own Base and its own set
of ORM classes. However when one test has a bug in an ORM class definition, ALL
the following tests fail because of this. I tried to delete all ORM classes
defined in the test but this did not help. Perhaps there are lingering
references to them somewhere. I am not sure how Python's GC works.

Anyway, thank you again for your quick help,

Ladislav Lenart


On 18.9.2012 16:39, Michael Bayer wrote:
 
 the _mapper_registry is weak referencing, so doesn't have any impact on 
 mappers hanging around or not.   Ultimately, the mapper is associated with 
 your mapped class.   The clear_mappers() API call will de-associate mappers 
 from classes and remove instrumentation that was affixed by the mapper() call.
 
 However, the vast majority of applications have no need for such a phase, and 
 there is no benefit to calling clear_mappers().   This is discussed in the 
 docs for clear_mappers(): 
 http://docs.sqlalchemy.org/en/rel_0_7/orm/mapper_config.html?highlight=clear_mappers#sqlalchemy.orm.clear_mappers
 
 If you're using declarative, it is especially useless, since the directives 
 placed on a declarative class are destroyed by the mapping process in the 
 first place, and those classes can't be re-mapped unless you defined the 
 mappings again non-declaratively, thus making the usage of declarative in the 
 first place completely redundant.
 
 The mapped class is best considered as a single, composed unit.Just like 
 when you create a class in your application that has, for example, an 
 __init__() method and a get_stuff() method, there's no need when tearing down 
 tests to remove the __init__() and get_stuff() methods from those classes; 
 these are part of that structure.
 
 A frequent misunderstanding is that mappers have some connection to the 
 database, either through engines or connections, and that as long as mappers 
 exist, this means we are connected to the database.  This is not at all 
 true, any more than a SQL string you might pass to cursor.execute() in DBAPI 
 has any relationship to database connections.The mapper() only represents 
 in-memory information about how your classes are structured in terms of 
 hypothetical database tables.   It has no connection whatsoever to actual 
 database connections.
 
 When unit testing, the thing that needs to be torn down is the transactional, 
 connection, and Session state, that is, those things which represent per-use 
 resources that need to be closed down.An example of how to associate a 
 Session with connection resources for the lifespan of a test is here: 
 http://docs.sqlalchemy.org/en/rel_0_7/orm/session.html#joining-a-session-into-an-external-transaction
  .It seems like you're already working with this code based on what I see 
 below.   
 
 
 
 On Sep 18, 2012, at 10:20 AM, Ladislav Lenart wrote:
 
 Hello again.

 The problem is in sqlalchemy.orm.mapper. There are globals _mapper_registry 
 and
 a boolean _new_mappers that triggers recompilation of the mappers. Is there a
 safe way to clear them in each test's case tearDown?

 Thank you in advance,

 Ladislav Lenart


 On 18.9.2012 16:07, Ladislav Lenart wrote:
 Hello.

 I have isolation issues with unit testing with SQLAlchemy. My DbTestCase 
 looks
 like this (I use nose test framework):


 class DbTestCase(object):
Db-aware test case superclass.
__engine_name__ = 'postgres'
__db_name__ = 'unit_tests'
__echo__ = True

@property
def engine(self):
return self.Base.metadata.bind

def setUp(self):
self.Base = create_base()
self._create_db()
self.connection = self.engine.connect()
self.trans = self.connection.begin()
self.session = Session(bind=self.connection)

def _create_db(self):
 # construct conn_string from __engine_name__ and __db_name__
engine = create_engine(conn_string, echo=echo)
self.Base.metadata.bind = engine

def tearDown(self):
# Rollback: Everything that happened with the Session above 
 (including
# calls to commit()) is rolled back.
self.trans.rollback()
self.session.close()

# Return connection to the engine.
self.connection.close()

# Remove all tables while we know what we have defined.
self._drop_all()

def _recreate_all(self):
self._drop_all()
self._create_all()

def _drop_all(self):
self.Base.metadata.drop_all()

def _create_all(self):
self.Base.metadata.create_all()


 A complete usage looks like this:

 import re
 from sqlalchemy import Column, Integer, ForeignKey
 from sqlalchemy.orm import relationship
 from sqlalchemy.ext.declarative import declarative_base, declared_attr


 class _Base(object):
@declared_attr
def 

Re: [sqlalchemy] [Q] Lack of isolation in unit tests

2012-09-18 Thread Michael Bayer

On Sep 18, 2012, at 11:36 AM, Ladislav Lenart wrote:

 Hello.
 
 Adding call to clear_mappers() to tearDown fixed my problem.
 
 For the rest of the discussion I am not sure I completely follow.
 
 I use declarative exclusively but each test defines its own Base and its own 
 set
 of ORM classes.


OK, that's unusual but that is actually the case where clear_mappers() is fine 
to use.  SQLAlchemy's own unit tests create new classes for each test because 
we're testing SQLAlchemy itself.

 However when one test has a bug in an ORM class definition, ALL
 the following tests fail because of this.

OK, yes this is correct, as the compilation step tries to get at all mappers.  
Sorry I didn't realize this, it's very strange for end-user unit tests to be 
testing the creation of ad-hoc mapper configurations.


-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.