[Checkins] SVN: z3c.sqlalchemy/trunk/ merging zope.sqlalchemy
integration branch
Andreas Jung
andreas at andreas-jung.com
Mon May 19 01:38:04 EDT 2008
Log message for revision 86825:
merging zope.sqlalchemy integration branch
Changed:
U z3c.sqlalchemy/trunk/CHANGES.txt
U z3c.sqlalchemy/trunk/README.txt
U z3c.sqlalchemy/trunk/buildout.cfg
U z3c.sqlalchemy/trunk/setup.py
U z3c.sqlalchemy/trunk/src/z3c/sqlalchemy/base.py
D z3c.sqlalchemy/trunk/src/z3c/sqlalchemy/doc/
U z3c.sqlalchemy/trunk/src/z3c/sqlalchemy/postgres.py
U z3c.sqlalchemy/trunk/src/z3c/sqlalchemy/tests/testSQLAlchemy.py
U z3c.sqlalchemy/trunk/src/z3c/sqlalchemy/util.py
-=-
Modified: z3c.sqlalchemy/trunk/CHANGES.txt
===================================================================
--- z3c.sqlalchemy/trunk/CHANGES.txt 2008-05-19 05:37:35 UTC (rev 86824)
+++ z3c.sqlalchemy/trunk/CHANGES.txt 2008-05-19 05:38:04 UTC (rev 86825)
@@ -1,3 +1,16 @@
+1.2.0 (unreleased)
+------------------
+
+ - now using zope.sqlalchemy for ZODB transaction integration
+
+ - internal class renaming
+
+ - remove PythonBaseWrapper, there is only *one* ZopeWrapper
+
+ - requires SQLAlchemy 0.4.6 or higher
+
+ - requires zope.sqlalchemy 0.1 or higher
+
1.1.5 (08.05.2008)
------------------
Modified: z3c.sqlalchemy/trunk/README.txt
===================================================================
--- z3c.sqlalchemy/trunk/README.txt 2008-05-19 05:37:35 UTC (rev 86824)
+++ z3c.sqlalchemy/trunk/README.txt 2008-05-19 05:38:04 UTC (rev 86825)
@@ -22,7 +22,6 @@
- no support for Zope 3 schemas
- no support for Archetypes schemas
-
z3c.sqlachemy just tries to provide you with the basic functionalities you need
to write SQLAlchemy-based applications with Zope 2/3. Higher-level
functionalities like integration with Archetypes/Zope 3 schemas are subject to
@@ -33,7 +32,8 @@
=============
- Zope 2.8+, Zope 3.X
-- SQLAlchemy 0.4.0 or higher (no support for SQLAlchemy 0.3)
+- SQLAlchemy 0.4.6 or higher (no support for SQLAlchemy 0.3)
+- zope.sqlalchemy 0.1.0 or higher
- Python 2.4+
Modified: z3c.sqlalchemy/trunk/buildout.cfg
===================================================================
--- z3c.sqlalchemy/trunk/buildout.cfg 2008-05-19 05:37:35 UTC (rev 86824)
+++ z3c.sqlalchemy/trunk/buildout.cfg 2008-05-19 05:38:04 UTC (rev 86825)
@@ -1,29 +1,7 @@
[buildout]
-parts = plone zope2 instance
-eggs =
-develop =
+parts = test
+develop = .
-[plone]
-recipe = plone.recipe.plone
-
-[zope2]
-recipe = plone.recipe.zope2install
-url = ${plone:zope2-url}
-
-[instance]
-recipe = plone.recipe.zope2instance
-zope2-location = ${zope2:location}
-user = admin:admin
-http-port = 8080
-debug-mode = on
-verbose-security = on
-eggs =
- ${buildout:eggs}
- ${plone:eggs}
-zcml =
-
-products =
- ${plone:products}
- ${buildout:directory}/products
-
-
+[test]
+recipe = zc.recipe.testrunner
+eggs = z3c.sqlalchemy [test]
Modified: z3c.sqlalchemy/trunk/setup.py
===================================================================
--- z3c.sqlalchemy/trunk/setup.py 2008-05-19 05:37:35 UTC (rev 86824)
+++ z3c.sqlalchemy/trunk/setup.py 2008-05-19 05:38:04 UTC (rev 86825)
@@ -7,10 +7,8 @@
##########################################################################
-import os
from setuptools import setup, find_packages
-
CLASSIFIERS = [
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
@@ -48,7 +46,8 @@
zip_safe=True,
namespace_packages=['z3c'],
install_requires=['setuptools',
- 'SQLAlchemy>=0.4.0',
+ 'SQLAlchemy>=0.4.6',
+ 'zope.sqlalchemy',
# 'zope.component==3.3',
# 'zope.interface==3.3',
# 'zope.schema==3.3',
Modified: z3c.sqlalchemy/trunk/src/z3c/sqlalchemy/base.py
===================================================================
--- z3c.sqlalchemy/trunk/src/z3c/sqlalchemy/base.py 2008-05-19 05:37:35 UTC (rev 86824)
+++ z3c.sqlalchemy/trunk/src/z3c/sqlalchemy/base.py 2008-05-19 05:38:04 UTC (rev 86825)
@@ -6,52 +6,23 @@
# and ZOPYX Ltd. & Co. KG, Tuebingen, Germany
##########################################################################
-import random
-import threading
-
-import sqlalchemy
-from sqlalchemy.engine.url import make_url
-from sqlalchemy.orm import sessionmaker
-
from zope.interface import implements
from zope.component import getUtility
from zope.component.interfaces import ComponentLookupError
-from z3c.sqlalchemy.interfaces import ISQLAlchemyWrapper, IModelProvider
from z3c.sqlalchemy.model import Model
from z3c.sqlalchemy.mapper import LazyMapperCollection
+from z3c.sqlalchemy.interfaces import ISQLAlchemyWrapper, IModelProvider
-import transaction
-from transaction.interfaces import ISavepointDataManager, IDataManagerSavepoint
+from sqlalchemy import create_engine, MetaData
+from sqlalchemy.engine.url import make_url
+from sqlalchemy.orm import scoped_session, sessionmaker, relation
+from zope.sqlalchemy import ZopeTransactionExtension
-class SynchronizedThreadCache(object):
- def __init__(self):
- self.lock = threading.Lock()
- self.cache = threading.local()
+class ZopeWrapper(object):
- def set(self, id, d):
- self.lock.acquire()
- setattr(self.cache, id, d)
- self.lock.release()
-
- def get(self, id):
- self.lock.acquire()
- result = getattr(self.cache, id, None)
- self.lock.release()
- return result
-
- def remove(self, id):
- self.lock.acquire()
- if hasattr(self.cache, id):
- delattr(self.cache, id)
- self.lock.release()
-
-
-
-class BaseWrapper(object):
-
implements(ISQLAlchemyWrapper)
def __init__(self, dsn, model=None, transactional=True, engine_options={}, session_options={}, **kw):
@@ -82,7 +53,6 @@
self.session_options = session_options
self._model = None
self._createEngine()
- self._id = str(random.random()) # used as unique key for session/connection cache
if model:
@@ -117,13 +87,23 @@
@property
def metadata(self):
if not hasattr(self, '_v_metadata'):
- self._v_metadata = sqlalchemy.MetaData(self._engine)
+ self._v_metadata = MetaData(self._engine)
return self._v_metadata
@property
def session(self):
+ """ Return thread-local session """
return self._sessionmaker()
+ @property
+ def connection(self):
+ """ Return underlying connection """
+ session = self.session
+ # Return the ConnectionFairy
+ return session.connection().connection
+ # instead of the raw connection
+ #return session.connection().connection.connection
+
def registerMapper(self, mapper, name):
self._mappers.registerMapper(mapper, name)
@@ -144,152 +124,10 @@
return self._model
def _createEngine(self):
- self._engine = sqlalchemy.create_engine(self.dsn, **self.engine_options)
- self._sessionmaker = sqlalchemy.orm.sessionmaker(bind=self._engine,
- autoflush=True,
- transactional=True,
- **self.session_options)
-
-
-connection_cache = SynchronizedThreadCache()
-
-
-class SessionDataManager(object):
- """ Wraps session into transaction context of Zope """
-
- implements(ISavepointDataManager)
-
- def __init__(self, connection, session, id, transactional=True):
-
- self.connection = connection
- self.session = session
- self.transactional = True
- self._id = id
- self.transaction = None
- if self.transactional:
- self.transaction = connection.begin()
-
- def abort(self, trans):
-
- try:
- if self.transaction is not None:
- self.transaction.rollback()
- # DM: done in "_cleanup" (similar untidy code at other places as well)
-## self.session.clear()
-## connection_cache.remove(self._id)
- finally:
- # ensure '_cleanup' is called even when 'rollback' causes an exception
- self._cleanup()
-
- def _flush(self):
-
- # check if the session contains something flushable
- if self.session.new or self.session.deleted or self.session.dirty:
-
- # Check if a session-bound transaction has been created so far.
- # If not, create a new transaction
-# if self.transaction is None:
-# self.transaction = connection.begin()
-
- # Flush
- self.session.flush()
-
- def commit(self, trans):
- self._flush()
-
- def tpc_begin(self, trans):
- pass
-
- def tpc_vote(self, trans):
- self._flush()
-
- def tpc_finish(self, trans):
-
- if self.transaction is not None:
- self.transaction.commit()
-
- self.session.clear()
- self._cleanup()
-
-
- # DM: no need to duplicate this code (identical to "abort")
-## def tpc_abort(self, trans):
-## if self.transaction is not None:
-## self.transaction.rollback()
-## self._cleanup()
- tpc_abort = abort
-
- def sortKey(self):
- return 'z3c.sqlalchemy_' + str(id(self))
-
- def _cleanup(self):
- self.session.clear()
- if self.connection:
- self.connection.close()
- self.connection = None
- connection_cache.remove(self._id)
- # DM: maybe, we should set "transaction" to "None"?
-
- def savepoint(self):
- """ return a dummy savepoint """
- return AlchemySavepoint()
-
-
-
-# taken from z3c.zalchemy
-
-class AlchemySavepoint(object):
- """A dummy saveoint """
-
- implements(IDataManagerSavepoint)
-
- def __init__(self):
- pass
-
- def rollback(self):
- pass
-
-
-
-class ZopeBaseWrapper(BaseWrapper):
- """ A wrapper to be used from within Zope. It connects
- the session with the transaction management of Zope.
- """
-
-
- def __getOrCreateConnectionCacheItem(self, cache_id):
-
- cache_item = connection_cache.get(cache_id)
-
- # return cached session if we are within the same transaction
- # and same thread
- if cache_item is not None:
- return cache_item
-
- # no cached session, let's create a new one
- connection = self.engine.connect()
- session = sessionmaker(connection)()
-
- # register a DataManager with the current transaction
- transaction.get().join(SessionDataManager(connection, session, self._id))
-
- # update thread-local cache
- cache_item = dict(connection=connection, session=session)
- connection_cache.set(self._id, cache_item)
- return cache_item
-
-
- @property
- def session(self):
- """ Return a (cached) session object for the current transaction """
- return self.__getOrCreateConnectionCacheItem(self._id)['session']
-
-
- @property
- def connection(self):
- """ This property is _private_ and only intented to be used
- by SQLAlchemyDA and therefore it is not part of the
- public API.
- """
-
- return self.__getOrCreateConnectionCacheItem(self._id)['connection']
+ self._engine = create_engine(self.dsn, **self.engine_options)
+ self._sessionmaker = scoped_session(sessionmaker(bind=self._engine,
+ transactional=True,
+ autoflush=True,
+ extension=ZopeTransactionExtension(),
+ **self.session_options
+ ))
Modified: z3c.sqlalchemy/trunk/src/z3c/sqlalchemy/postgres.py
===================================================================
--- z3c.sqlalchemy/trunk/src/z3c/sqlalchemy/postgres.py 2008-05-19 05:37:35 UTC (rev 86824)
+++ z3c.sqlalchemy/trunk/src/z3c/sqlalchemy/postgres.py 2008-05-19 05:38:04 UTC (rev 86825)
@@ -15,7 +15,7 @@
from zope.interface import implements
from z3c.sqlalchemy.interfaces import ISQLAlchemyWrapper
-from z3c.sqlalchemy.base import BaseWrapper, ZopeBaseWrapper
+from z3c.sqlalchemy.base import ZopeWrapper
_cache = threading.local() # module-level cache
@@ -68,12 +68,7 @@
return _cache.ref_mapping
-class PythonPostgresWrapper(BaseWrapper, PostgresMixin):
- """ Wrapper to be used with Python with extended
- Postgres functionality.
- """
-
-class ZopePostgresWrapper(ZopeBaseWrapper, PostgresMixin):
+class ZopePostgresWrapper(ZopeWrapper, PostgresMixin):
""" A wrapper to be used from within Zope. It connects
the session with the transaction management of Zope.
"""
Modified: z3c.sqlalchemy/trunk/src/z3c/sqlalchemy/tests/testSQLAlchemy.py
===================================================================
--- z3c.sqlalchemy/trunk/src/z3c/sqlalchemy/tests/testSQLAlchemy.py 2008-05-19 05:37:35 UTC (rev 86824)
+++ z3c.sqlalchemy/trunk/src/z3c/sqlalchemy/tests/testSQLAlchemy.py 2008-05-19 05:38:04 UTC (rev 86825)
@@ -14,7 +14,6 @@
"""
import os
-import unittest
import sqlalchemy
from sqlalchemy import MetaData, Integer, String, Column, Table
@@ -22,13 +21,13 @@
from zope.interface.verify import verifyClass
from z3c.sqlalchemy.interfaces import ISQLAlchemyWrapper, IModel
-from z3c.sqlalchemy.postgres import PythonPostgresWrapper, ZopePostgresWrapper
-from z3c.sqlalchemy.base import BaseWrapper
+from z3c.sqlalchemy.postgres import ZopePostgresWrapper
from z3c.sqlalchemy.mapper import MappedClassBase
from z3c.sqlalchemy import createSAWrapper, Model, registerSAWrapper, getSAWrapper
+from Testing.ZopeTestCase import ZopeTestCase
-class WrapperTests(unittest.TestCase):
+class WrapperTests(ZopeTestCase):
def setUp(self):
@@ -38,25 +37,17 @@
users = Table('users', metadata,
Column('id', Integer, primary_key=True),
- Column('firstname', String),
- Column('lastname', String))
+ Column('firstname', String(255)),
+ Column('lastname', String(255)))
skill = Table('skills', metadata,
- Column('id', Integer, primary_key=True),
+ Column('user_id', Integer, primary_key=True),
Column('user_id', Integer),
- Column('name', String))
+ Column('name', String(255)))
+ metadata.drop_all()
metadata.create_all()
-
- def testIFaceBaseWrapper (self):
- verifyClass(ISQLAlchemyWrapper , BaseWrapper)
-
-
- def testIFacePythonPostgres(self):
- verifyClass(ISQLAlchemyWrapper , PythonPostgresWrapper)
-
-
def testIFaceZopePostgres(self):
verifyClass(ISQLAlchemyWrapper , ZopePostgresWrapper)
@@ -77,7 +68,7 @@
session.save(User(id=1, firstname='udo', lastname='juergens'))
session.save(User(id=2, firstname='heino', lastname='n/a'))
session.flush()
-
+
rows = session.query(User).order_by(User.c.id).all()
self.assertEqual(len(rows), 2)
row1 = rows[0]
@@ -173,12 +164,38 @@
User = db.getMapper('users')
session = db.session
session.save(User(id=1,firstname='foo', lastname='bar'))
-
+ session.flush()
user = session.query(User).filter_by(firstname='foo')[0]
Skill = user.getMapper('skills')
user.skills.append(Skill(id=1, name='Zope'))
session.flush()
+ def testCheckConnection(self):
+ """ Check access to low-level connection """
+ db = createSAWrapper(self.dsn)
+ conn = db.connection
+ cursor = conn.cursor()
+ cursor.execute('select * from users')
+ rows = cursor.fetchall()
+ self.assertEqual(len(rows), 0)
+
+ def testConnectionPlusSession(self):
+ """ Check access to low-level connection """
+ db = createSAWrapper(self.dsn)
+
+ User = db.getMapper('users')
+ session = db.session
+ session.save(User(id=1, firstname='udo', lastname='juergens'))
+ session.save(User(id=2, firstname='heino', lastname='n/a'))
+ session.flush()
+
+ conn = db.connection
+ cursor = conn.cursor()
+ cursor.execute('select * from users')
+ rows = cursor.fetchall()
+ self.assertEqual(len(rows), 2)
+
+
def test_suite():
from unittest import TestSuite, makeSuite
suite = TestSuite()
Modified: z3c.sqlalchemy/trunk/src/z3c/sqlalchemy/util.py
===================================================================
--- z3c.sqlalchemy/trunk/src/z3c/sqlalchemy/util.py 2008-05-19 05:37:35 UTC (rev 86824)
+++ z3c.sqlalchemy/trunk/src/z3c/sqlalchemy/util.py 2008-05-19 05:38:04 UTC (rev 86825)
@@ -14,21 +14,19 @@
from sqlalchemy.engine.url import make_url
-from zope.component import getService, getGlobalServices, getUtilitiesFor, getUtility
-from zope.component.utility import GlobalUtilityService
+from zope.component import getUtilitiesFor, getUtility
from zope.component.interfaces import IUtilityService, ComponentLookupError
-from zope.component.servicenames import Utilities
from z3c.sqlalchemy.interfaces import ISQLAlchemyWrapper
-from z3c.sqlalchemy.postgres import ZopePostgresWrapper, PythonPostgresWrapper
-from z3c.sqlalchemy.base import BaseWrapper, ZopeBaseWrapper
+from z3c.sqlalchemy.postgres import ZopePostgresWrapper
+from z3c.sqlalchemy.base import ZopeWrapper
__all__ = ('createSQLAlchemyWrapper', 'registerSQLAlchemyWrapper', 'allRegisteredSQLAlchemyWrappers', 'getSQLAlchemyWrapper',
'createSAWrapper', 'registerSAWrapper', 'allRegisteredSAWrappers', 'getSAWrapper', 'allSAWrapperNames')
registeredWrappers = {}
-def createSAWrapper(dsn, model=None, forZope=False, name=None, transactional=True,
+def createSAWrapper(dsn, model=None, name=None, transactional=True,
engine_options={}, session_options={}, **kw):
""" Convenience method to generate a wrapper for a DSN and a model.
This method hides all database related magic from the user.
@@ -39,9 +37,6 @@
a named utility implementing IModelProvider or a method/callable returning an
instance of model.Model.
- 'forZope' - set this to True in order to obtain a Zope-transaction-aware
- wrapper.
-
'transactional' - True|False, only used for SQLAlchemyDA *don't change it*
'name' can be set to register the wrapper automatically in order
@@ -57,10 +52,10 @@
url = make_url(dsn)
driver = url.drivername
- klass = forZope and ZopeBaseWrapper or BaseWrapper
+ klass = ZopeWrapper
if driver == 'postgres':
- klass = forZope and ZopePostgresWrapper or PythonPostgresWrapper
+ klass = ZopePostgresWrapper
wrapper = klass(dsn, model,
transactional=transactional,
More information about the Checkins
mailing list