[Checkins] SVN: z3c.saconfig/ Initial import of z3c.saconfig,
a system to configure SQLAlchemy sessions.
Martijn Faassen
faassen at infrae.com
Thu Jun 19 14:41:25 EDT 2008
Log message for revision 87560:
Initial import of z3c.saconfig, a system to configure SQLAlchemy sessions.
Changed:
A z3c.saconfig/
A z3c.saconfig/trunk/
A z3c.saconfig/trunk/CHANGES.txt
A z3c.saconfig/trunk/README.txt
A z3c.saconfig/trunk/bootstrap.py
A z3c.saconfig/trunk/buildout.cfg
A z3c.saconfig/trunk/setup.py
A z3c.saconfig/trunk/src/
A z3c.saconfig/trunk/src/z3c/
A z3c.saconfig/trunk/src/z3c/__init__.py
A z3c.saconfig/trunk/src/z3c/saconfig/
A z3c.saconfig/trunk/src/z3c/saconfig/README.txt
A z3c.saconfig/trunk/src/z3c/saconfig/__init__.py
A z3c.saconfig/trunk/src/z3c/saconfig/interfaces.py
A z3c.saconfig/trunk/src/z3c/saconfig/scopedsession.py
A z3c.saconfig/trunk/src/z3c/saconfig/tests.py
A z3c.saconfig/trunk/src/z3c/saconfig/utility.py
-=-
Added: z3c.saconfig/trunk/CHANGES.txt
===================================================================
--- z3c.saconfig/trunk/CHANGES.txt (rev 0)
+++ z3c.saconfig/trunk/CHANGES.txt 2008-06-19 18:41:24 UTC (rev 87560)
@@ -0,0 +1,7 @@
+z3c.saconfig
+************
+
+0.1 (unreleased)
+================
+
+* Initial public release.
Added: z3c.saconfig/trunk/README.txt
===================================================================
--- z3c.saconfig/trunk/README.txt (rev 0)
+++ z3c.saconfig/trunk/README.txt 2008-06-19 18:41:24 UTC (rev 87560)
@@ -0,0 +1,5 @@
+=================
+ zope.sqlalchemy
+=================
+
+Zope/SQLAlchemy transaction integration. See src/zope/sqlalchemy/README.txt.
Added: z3c.saconfig/trunk/bootstrap.py
===================================================================
--- z3c.saconfig/trunk/bootstrap.py (rev 0)
+++ z3c.saconfig/trunk/bootstrap.py 2008-06-19 18:41:24 UTC (rev 87560)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id: bootstrap.py 71258 2006-11-21 22:22:48Z jim $
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+ez = {}
+exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+ ).read() in ez
+ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+ cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+ os.P_WAIT, sys.executable, sys.executable,
+ '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+ dict(os.environ,
+ PYTHONPATH=
+ ws.find(pkg_resources.Requirement.parse('setuptools')).location
+ ),
+ ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)
Added: z3c.saconfig/trunk/buildout.cfg
===================================================================
--- z3c.saconfig/trunk/buildout.cfg (rev 0)
+++ z3c.saconfig/trunk/buildout.cfg 2008-06-19 18:41:24 UTC (rev 87560)
@@ -0,0 +1,12 @@
+[buildout]
+develop = . zope.sqlalchemy
+parts = test scripts
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = z3c.saconfig
+
+[scripts]
+recipe = zc.recipe.egg
+eggs = z3c.saconfig
+interpreter = py
\ No newline at end of file
Added: z3c.saconfig/trunk/setup.py
===================================================================
--- z3c.saconfig/trunk/setup.py (rev 0)
+++ z3c.saconfig/trunk/setup.py 2008-06-19 18:41:24 UTC (rev 87560)
@@ -0,0 +1,43 @@
+from setuptools import setup, find_packages
+import sys, os
+
+def read(*rnames):
+ return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+
+long_description = ""
+
+setup(name='z3c.saconfig',
+ version='0.1dev',
+ description="Minimal SQLAlchemy ORM session configuration for Zope",
+ long_description=long_description,
+ # Get more strings from http://www.python.org/pypi?%3Aaction=list_classifiers
+ classifiers=[
+ "Framework :: Zope3",
+ "Programming Language :: Python",
+ "License :: OSI Approved :: Zope Public License",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+ ],
+ keywords='',
+ author='Martijn Faassen',
+ author_email='faassen at startifact.com',
+ url='http://pypi.python.org/pypi/z3c.saconfig',
+ license='ZPL 2.1',
+ packages=find_packages('src'),
+ package_dir = {'':'src'},
+ namespace_packages=['z3c'],
+ include_package_data=True,
+ zip_safe=False,
+ install_requires=[
+ # -*- Extra requirements: -*-
+ 'setuptools',
+ 'zope.sqlalchemy',
+ 'zope.interface',
+ 'zope.component',
+ ],
+ entry_points="""
+ # -*- Entry points: -*-
+ """,
+ extras_require = dict(
+ test = ['zope.testing'],
+ ),
+ )
Added: z3c.saconfig/trunk/src/z3c/__init__.py
===================================================================
--- z3c.saconfig/trunk/src/z3c/__init__.py (rev 0)
+++ z3c.saconfig/trunk/src/z3c/__init__.py 2008-06-19 18:41:24 UTC (rev 87560)
@@ -0,0 +1,6 @@
+# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
+try:
+ __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+ from pkgutil import extend_path
+ __path__ = extend_path(__path__, __name__)
Added: z3c.saconfig/trunk/src/z3c/saconfig/README.txt
===================================================================
--- z3c.saconfig/trunk/src/z3c/saconfig/README.txt (rev 0)
+++ z3c.saconfig/trunk/src/z3c/saconfig/README.txt 2008-06-19 18:41:24 UTC (rev 87560)
@@ -0,0 +1,117 @@
+z3c.saconfig
+************
+
+Introduction
+============
+
+This aim of this package is to offer a simple but flexible way to
+configure SQLAlchemy's scoped session support using the Zope 3
+component architecture. This package is based on ``zope.sqlalchemy``, which
+offers transaction integration between Zope and SQLAlchemy.
+
+GloballyScopedSession
+=====================
+
+The simplest way to set up SQLAlchemy for Zope is to have a single
+thread-scoped session that's global to your entire Zope
+instance. Multiple applications will all share this session. The
+engine is set up globally, in code.
+
+We use the SQLAlchemy ``sqlalchemy.ext.declarative`` extension to
+define some tables and classes::
+
+ >>> from sqlalchemy import *
+ >>> from sqlalchemy.ext.declarative import declarative_base
+ >>> from sqlalchemy.orm import relation
+
+ >>> Base = declarative_base()
+ >>> class User(Base):
+ ... __tablename__ = 'test_users'
+ ... id = Column('id', Integer, primary_key=True)
+ ... name = Column('name', String(50))
+ ... addresses = relation("Address", backref="user")
+ >>> class Address(Base):
+ ... __tablename__ = 'test_addresses'
+ ... id = Column('id', Integer, primary_key=True)
+ ... email = Column('email', String(50))
+ ... user_id = Column('user_id', Integer, ForeignKey('test_users.id'))
+
+We now set up an engine globally with our test DSN::
+
+ >>> engine = create_engine(TEST_DSN, convert_unicode=True)
+
+And we create the tables in our test database::
+
+ >>> Base.metadata.create_all(engine)
+
+So far this example doesn't differ any from the way
+``zope.sqlalchemy`` operates. The difference is in how we set up the
+session and use it. We'll use the ``GloballyScopedSession`` utility
+to implement our session creation::
+
+ >>> from z3c.saconfig import GloballyScopedSession
+
+We give the constructor to ``GloballyScopedSession`` the parameters
+you'd normally give to ``sqlalchemy.orm.create_session``, or
+``sqlalchemy.orm.sessionmaker``::
+
+ >>> utility = GloballyScopedSession(
+ ... bind=engine,
+ ... twophase=TEST_TWOPHASE)
+
+``GloballyScopedSession`` automatically sets up the ``autocommit``,
+``autoflush`` and ``extension`` parameters to be the right ones for
+Zope integration, so normally you wouldn't need to supply these, but
+you could pass in your own if you do need it.
+
+We now register this as an ``IScopedSession`` utility with
+``zope.component``. Normally you'd use either ZCML or Grok to do this
+confirmation, but we'll do it manually here::
+
+ >>> from zope import component
+ >>> from z3c.saconfig.interfaces import IScopedSession
+ >>> component.provideUtility(utility, provides=IScopedSession)
+
+We can now use the ``Session`` object create a session which
+will behave according to the utility we provided::
+
+ >>> from z3c.saconfig import Session
+ >>> session = Session()
+
+Now things go the usual ``zope.sqlalchemy`` way, which is like
+``SQLAlchemy`` except you can use Zope's ``transaction`` module::
+
+ >>> session.query(User).all()
+ []
+ >>> import transaction
+ >>> session.save(User(name='bob'))
+ >>> transaction.commit()
+
+ >>> session = Session()
+ >>> bob = session.query(User).all()[0]
+ >>> bob.name
+ u'bob'
+ >>> bob.addresses
+ []
+
+Running the tests
+=================
+
+This package can be checked out from
+svn://svn.zope.org/repos/main. You can then execute the buildout to
+download and install the requirements and install the test
+runner. Using your desired python run:
+
+$ python bootstrap.py
+
+This will download the dependent packages and setup the test script, which may
+be run with:
+
+$ bin/test
+
+To enable testing with your own database set the TEST_DSN environment
+variable to your sqlalchemy database dsn. Two-phase commit behaviour
+may be tested by setting the TEST_TWOPHASE variable to a non empty
+string. e.g:
+
+$ TEST_DSN=postgres://test:test@localhost/test TEST_TWOPHASE=True bin/test
Added: z3c.saconfig/trunk/src/z3c/saconfig/__init__.py
===================================================================
--- z3c.saconfig/trunk/src/z3c/saconfig/__init__.py (rev 0)
+++ z3c.saconfig/trunk/src/z3c/saconfig/__init__.py 2008-06-19 18:41:24 UTC (rev 87560)
@@ -0,0 +1,2 @@
+from z3c.saconfig.scopedsession import Session
+from z3c.saconfig.utility import GloballyScopedSession
Added: z3c.saconfig/trunk/src/z3c/saconfig/interfaces.py
===================================================================
--- z3c.saconfig/trunk/src/z3c/saconfig/interfaces.py (rev 0)
+++ z3c.saconfig/trunk/src/z3c/saconfig/interfaces.py 2008-06-19 18:41:24 UTC (rev 87560)
@@ -0,0 +1,26 @@
+from zope.interface import Interface
+
+class IScopedSession(Interface):
+ """A utility that plugs into SQLAlchemy's scoped session machinery.
+
+ The idea is that you'd either register a IScopedSession utility globally,
+ for simple configurations, or locally, if you want to have the ability
+ to transparently use a different engine and session configuration per
+ database.
+ """
+ def session_factory():
+ """Create a SQLAlchemy session.
+
+ Typically you'd use sqlalchemy.orm.create_session to create
+ the session here.
+ """
+
+ def scopefunc():
+ """Determine the scope of the session.
+
+ This can be used to scope the session per thread, per Zope 3 site,
+ or otherwise. Return an immutable value to scope the session,
+ like a thread id, or a tuple with thread id and application id.
+ """
+
+
Added: z3c.saconfig/trunk/src/z3c/saconfig/scopedsession.py
===================================================================
--- z3c.saconfig/trunk/src/z3c/saconfig/scopedsession.py (rev 0)
+++ z3c.saconfig/trunk/src/z3c/saconfig/scopedsession.py 2008-06-19 18:41:24 UTC (rev 87560)
@@ -0,0 +1,24 @@
+from z3c.saconfig.interfaces import IScopedSession
+
+from sqlalchemy.orm import scoped_session
+from zope import component
+
+def session_factory():
+ """This is used by scoped session to create a new Session object.
+
+ It delegates to a IScopedSession utility.
+ """
+ utility = component.getUtility(IScopedSession)
+ return utility.session_factory()
+
+def scopefunc():
+ """This is used by scoped session to distinguish between sessions.
+
+ It delegates to a IScopedSession utility.
+ """
+ utility = component.getUtility(IScopedSession)
+ return utility.scopefunc()
+
+# this is framework central configuration. Use a IScopedSession utility
+# to define behavior.
+Session = scoped_session(session_factory, scopefunc)
Added: z3c.saconfig/trunk/src/z3c/saconfig/tests.py
===================================================================
--- z3c.saconfig/trunk/src/z3c/saconfig/tests.py (rev 0)
+++ z3c.saconfig/trunk/src/z3c/saconfig/tests.py 2008-06-19 18:41:24 UTC (rev 87560)
@@ -0,0 +1,40 @@
+# You may want to run the tests with your database. To do so set the
+# environment variable TEST_DSN to the connection url. e.g.:
+#
+# export TEST_DSN=postgres://plone:plone@localhost/test
+# export TEST_DSN=mssql://plone:plone@/test?dsn=mydsn
+#
+# To test in twophase commit mode export TEST_TWOPHASE=True
+#
+# NOTE: The sqlite that ships with Mac OS X 10.4 is buggy.
+# Install a newer version (3.5.6) and rebuild pysqlite2 against it.
+
+
+import unittest
+import doctest
+import os
+
+from zope.testing import cleanup
+
+TEST_TWOPHASE = bool(os.environ.get('TEST_TWOPHASE'))
+TEST_DSN = os.environ.get('TEST_DSN', 'sqlite:///:memory:')
+
+def tearDownReadMe(test):
+ # clean up Zope
+ cleanup.cleanUp()
+
+ # clean up SQLAlchemy
+ Base = test.globs['Base']
+ engine = test.globs['engine']
+ Base.metadata.drop_all(engine)
+
+def test_suite():
+ optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
+
+ suite = unittest.TestSuite()
+ suite.addTest(doctest.DocFileSuite(
+ 'README.txt',
+ optionflags=optionflags,
+ tearDown=tearDownReadMe,
+ globs={'TEST_DSN': TEST_DSN, 'TEST_TWOPHASE': TEST_TWOPHASE}))
+ return suite
Added: z3c.saconfig/trunk/src/z3c/saconfig/utility.py
===================================================================
--- z3c.saconfig/trunk/src/z3c/saconfig/utility.py (rev 0)
+++ z3c.saconfig/trunk/src/z3c/saconfig/utility.py 2008-06-19 18:41:24 UTC (rev 87560)
@@ -0,0 +1,51 @@
+"""
+Some reusable, standard implementations of IScopedSession.
+"""
+
+import thread
+from zope.interface import implements
+import sqlalchemy
+from zope.sqlalchemy import ZopeTransactionExtension
+
+from z3c.saconfig.interfaces import IScopedSession
+
+class GloballyScopedSession(object):
+ """A globally scoped session.
+
+ Register this as a global utility to have just one kind of session
+ per Zope instance. All applications in this instance will share the
+ same session.
+
+ To register as a global utility you may need to register it with
+ a custom factory, or alternatively subclass it and override __init__
+ to pass the right arguments to the superclasses __init__.
+ """
+ implements(IScopedSession)
+
+ def __init__(self, **kw):
+ """Pass keywords arguments for sqlalchemy.orm.create_session.
+
+ Note that GloballyScopedSesssion does have different defaults than
+ ``create_session`` for various parameters where it makes sense
+ for Zope integration, namely:
+
+ autocommit = False
+ autoflush = True
+ extension = ZopeTransactionExtension()
+
+ Normally you wouldn't pass these in, but if you have the need
+ to override them, you could.
+ """
+ if 'autocommit' not in kw:
+ kw['autocommit'] = False
+ if 'autoflush' not in kw:
+ kw['autoflush'] = True
+ if 'extension' not in kw:
+ kw['extension'] = ZopeTransactionExtension()
+ self.kw = kw
+
+ def session_factory(self):
+ return sqlalchemy.orm.create_session(**self.kw)
+
+ def scopefunc(self):
+ return thread.get_ident()
More information about the Checkins
mailing list