[Checkins] SVN: Sandbox/faassen/rdbintegration/ Initial import.

Martijn Faassen faassen at infrae.com
Mon Jun 16 14:28:34 EDT 2008


Log message for revision 87431:
  Initial import.
  

Changed:
  A   Sandbox/faassen/rdbintegration/
  A   Sandbox/faassen/rdbintegration/trunk/
  A   Sandbox/faassen/rdbintegration/trunk/buildout.cfg
  A   Sandbox/faassen/rdbintegration/trunk/setup.py
  A   Sandbox/faassen/rdbintegration/trunk/src/
  A   Sandbox/faassen/rdbintegration/trunk/src/rdbintegration/
  A   Sandbox/faassen/rdbintegration/trunk/src/rdbintegration/__init__.py
  A   Sandbox/faassen/rdbintegration/trunk/src/rdbintegration/app.py
  A   Sandbox/faassen/rdbintegration/trunk/src/rdbintegration/app_templates/
  A   Sandbox/faassen/rdbintegration/trunk/src/rdbintegration/configure.zcml
  A   Sandbox/faassen/rdbintegration/trunk/src/rdbintegration/ftesting.zcml
  A   Sandbox/faassen/rdbintegration/trunk/src/rdbintegration/testing.py

-=-
Added: Sandbox/faassen/rdbintegration/trunk/buildout.cfg
===================================================================
--- Sandbox/faassen/rdbintegration/trunk/buildout.cfg	                        (rev 0)
+++ Sandbox/faassen/rdbintegration/trunk/buildout.cfg	2008-06-16 18:28:31 UTC (rev 87431)
@@ -0,0 +1,66 @@
+[buildout]
+develop = .
+parts = app data zopectl i18n test
+find-links = http://download.zope.org/distribution/
+eggs-directory = /home/faassen/buildout-eggs
+newest = false
+extends= http://grok.zope.org/releaseinfo/grok-0.12.1.cfg
+versions = versions
+
+[app]
+recipe = zc.zope3recipes>=0.5.3:application
+eggs = rdbintegration
+site.zcml = <include package="rdbintegration" />
+            <include package="zope.app.twisted" />
+
+            <configure i18n_domain="rdbintegration">
+              <unauthenticatedPrincipal id="zope.anybody"
+                                        title="Unauthenticated User" />
+              <unauthenticatedGroup id="zope.Anybody"
+                                    title="Unauthenticated Users" />
+              <authenticatedGroup id="zope.Authenticated"
+                                title="Authenticated Users" />
+              <everybodyGroup id="zope.Everybody"
+                              title="All Users" />
+              <principal id="zope.manager"
+                         title="Manager"
+                         login="admin"
+                         password_manager="Plain Text"
+                         password="admin"
+                         />
+
+              <!-- Replace the following directive if you don't want
+                   public access -->
+              <grant permission="zope.View"
+                     principal="zope.Anybody" />
+              <grant permission="zope.app.dublincore.view"
+                     principal="zope.Anybody" />
+
+              <role id="zope.Manager" title="Site Manager" />
+              <role id="zope.Member" title="Site Member" />
+              <grantAll role="zope.Manager" />
+              <grant role="zope.Manager"
+                     principal="zope.manager" />
+           </configure>
+
+[data]
+recipe = zc.recipe.filestorage
+
+# this section named so that the start/stop script is called bin/zopectl
+[zopectl]
+recipe = zc.zope3recipes:instance
+application = app
+zope.conf = ${data:zconfig}
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = rdbintegration
+defaults = ['--tests-pattern', '^f?tests$', '-v']
+
+# this section named so that the i18n scripts are called bin/i18n...
+[i18n]
+recipe = lovely.recipe:i18n
+package = rdbintegration
+domain = rdbintegration
+location = src/rdbintegration
+output = locales

Added: Sandbox/faassen/rdbintegration/trunk/setup.py
===================================================================
--- Sandbox/faassen/rdbintegration/trunk/setup.py	                        (rev 0)
+++ Sandbox/faassen/rdbintegration/trunk/setup.py	2008-06-16 18:28:31 UTC (rev 87431)
@@ -0,0 +1,24 @@
+from setuptools import setup, find_packages
+
+setup(name='rdbintegration',
+      version="0.1",
+      description="An SQLAlchemy integration experiment",
+      classifiers=[], 
+      author="Martijn Faassen",
+      author_email="faassen at startifact.com",
+      license="ZPL",
+      package_dir={'': 'src'},
+      packages=find_packages('src'),
+      include_package_data=True,
+      zip_safe=False,
+      install_requires=['setuptools',
+                        'grok',
+                        'SQLAlchemy == 0.5beta1',
+                        'zope.sqlalchemy',
+                        'psycopg2',
+                        # Add extra requirements here
+                        ],
+      entry_points="""
+      # Add entry points here
+      """,
+      )

Added: Sandbox/faassen/rdbintegration/trunk/src/rdbintegration/__init__.py
===================================================================
--- Sandbox/faassen/rdbintegration/trunk/src/rdbintegration/__init__.py	                        (rev 0)
+++ Sandbox/faassen/rdbintegration/trunk/src/rdbintegration/__init__.py	2008-06-16 18:28:31 UTC (rev 87431)
@@ -0,0 +1 @@
+# this directory is a package

Added: Sandbox/faassen/rdbintegration/trunk/src/rdbintegration/app.py
===================================================================
--- Sandbox/faassen/rdbintegration/trunk/src/rdbintegration/app.py	                        (rev 0)
+++ Sandbox/faassen/rdbintegration/trunk/src/rdbintegration/app.py	2008-06-16 18:28:31 UTC (rev 87431)
@@ -0,0 +1,176 @@
+"""An experiment in integration with SQLAlchemy.
+
+The main goal of this experiment is to allow the user to instantiate a
+Session object in the normal SQLAlchemy way. This session object is
+however also fully aware of Zope transactions (thanks to
+zope.sqlalchemy), and moreover, is managed automatically by SQLAlchemy
+per-thread and per-application, using SQLAlchemy's ScopedSession.
+
+The applications to install expose 2 relevant URLs:
+
+* index - edit the name of the engine this app uses. (engine1, engine2)
+
+* db - do a simple db query
+
+The application expects a database with a 'test' table, which has an
+'id' primary key and a 'name' text column. These databases are
+currently Postgresql databases, experiment and experiment2. For the
+experiment, they should each have different information the test table
+(name column).
+
+You can create applications and set their engine using the index
+view. You can then use 'db' to look at the query. The result should be
+different if you set the engine differently. If you change the engine
+while the application is running, it will have no effect as the same
+session will still be reused (with the old engine bound to it). Instead
+you can restart the application to see the effect.
+
+This experiment will hopefully result in a new Zope 3 extension that
+allows the use of this pattern of configuration.
+
+Note that this experiment uses Grok, but the fundamental mechanics are
+not related to Grok. The ORM mapping bits are also just to test whether
+things work, not fundamental to the example.
+"""
+
+import grok
+from zope import schema
+
+from sqlalchemy.orm import scoped_session
+from zope import component
+from zope.interface import Interface
+import thread
+from sqlalchemy.orm import mapper
+from sqlalchemy.orm.session import Session as _Session
+from zope.sqlalchemy import ZopeTransactionExtension
+from sqlalchemy import create_engine
+import sqlalchemy as sa
+
+class IDatabase(Interface):
+    """A utility that specifies the database.
+    """
+    
+    def engine():
+        """Get the engine in use for this database session.
+
+        The engine is not created on the fly. Each time the engine is
+        looked up for a site (application), the same engine should be found.
+        """
+
+    def configuration():
+        """Get all configuration parameters for Session.
+
+        This should give the configuration parameters to be used for
+        a session in this site.
+        """
+
+    def id():
+        """Get unique id for this database configuration.
+
+        This should be unique per site (application).
+        """
+
+class Database(grok.LocalUtility):
+    grok.implements(IDatabase)
+    
+    def engine(self):
+        # we look up the engine with the name defined in the application
+        return component.getUtility(IEngine, self.__parent__.engine_name)
+    
+    def configuration(self):
+        # we return the configuration parameters as for an
+        # SQLAlchemy sessionmaker
+        return dict(
+            bind=self.engine(),
+            autocommit=True,
+            autoflush=True,
+            extension=ZopeTransactionExtension())
+
+    def id(self):
+        # we use the application name as the unique id. Can we use
+        # something more clever and universally working?
+        return self.__parent__.__name__
+
+class IEngine(Interface):
+    """The database engine.
+    """
+
+# we register the available engines as global utilities.
+# we want to be able to configure the engines, preferably also through
+# the UI. This might mean we need to register the engine as a local,
+# non-persistent utility that is somehow recreated on each restart.
+engine1 = create_engine('postgres:///experiment', convert_unicode=True)
+grok.global_utility(engine1, provides=IEngine, direct=True, name='engine1')
+
+engine2 = create_engine('postgres:///experiment2', convert_unicode=True)
+grok.global_utility(engine2, provides=IEngine, direct=True, name='engine2')
+        
+def session_factory():
+    """This is used by scoped session to create a new Session object.
+    """
+    utility = component.getUtility(IDatabase)
+    print "Creating new session"
+    return _Session(**utility.configuration())
+
+def scopefunc():
+    """This is used by scoped session to distinguish between sessions.
+
+    We distinguish between sessions by thread id and IDatabase unique
+    application id.
+    """
+    utility = component.getUtility(IDatabase)
+    result = (thread.get_ident(), utility.id())
+    print "Scope: ", result
+    return result
+
+# this is a frame-work central configuration, we only need to do this
+# once in our integration framework, after which we can just import
+# Session
+Session = scoped_session(session_factory,
+                         scopefunc)
+
+# an application that allows the configuration of the engine name
+class IForm(Interface):
+    engine_name = schema.TextLine(title=u"Engine name")
+
+class App(grok.Application, grok.Container):
+    grok.local_utility(Database, provides=IDatabase, public=True,
+                       name_in_container='database')
+
+    grok.implements(IForm)
+    
+    engine_name = ''
+
+class Index(grok.EditForm):
+    grok.context(App)
+
+    form_fields = grok.Fields(IForm)
+
+    @grok.action("Submit")
+    def submit(self, engine_name):
+        self.context.engine_name = engine_name
+
+# here we define the table information and inform the ORM of the mapping
+# this is not part of the experiment, just so we can test things.
+class Test(object):
+    """An object that is mapped with the ORM"""
+    pass
+
+metadata = sa.MetaData()
+test_table = sa.Table('test', metadata,
+                      sa.Column('id', sa.Integer, primary_key=True),
+                      sa.Column('name', sa.Text))
+mapper(Test, test_table)
+
+# the db view does a query in the Test table using the ORM. It uses
+# an instance of Session to do so. This is to verify that our scoped session
+# is working; it will automatically use a cached session.
+class Db(grok.View):
+    grok.context(App)
+
+    def render(self):
+        # this is the normal SQLAlchemy usage: just instantiate
+        # Session. No special Zope-related lookup logic
+        session = Session()
+        result = session.query(Test).all()
+        return repr([obj.name for obj in result])

Added: Sandbox/faassen/rdbintegration/trunk/src/rdbintegration/configure.zcml
===================================================================
--- Sandbox/faassen/rdbintegration/trunk/src/rdbintegration/configure.zcml	                        (rev 0)
+++ Sandbox/faassen/rdbintegration/trunk/src/rdbintegration/configure.zcml	2008-06-16 18:28:31 UTC (rev 87431)
@@ -0,0 +1,6 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+           xmlns:grok="http://namespaces.zope.org/grok">
+  <include package="grok" />
+  <includeDependencies package="." />
+  <grok:grok package="." />
+</configure>

Added: Sandbox/faassen/rdbintegration/trunk/src/rdbintegration/ftesting.zcml
===================================================================
--- Sandbox/faassen/rdbintegration/trunk/src/rdbintegration/ftesting.zcml	                        (rev 0)
+++ Sandbox/faassen/rdbintegration/trunk/src/rdbintegration/ftesting.zcml	2008-06-16 18:28:31 UTC (rev 87431)
@@ -0,0 +1,35 @@
+<configure
+   xmlns="http://namespaces.zope.org/zope"
+   i18n_domain="rdbintegration"
+   package="rdbintegration"
+   >
+
+  <include package="grok" />
+  <include package="rdbintegration" />
+
+  <!-- Typical functional testing security setup -->
+  <securityPolicy
+      component="zope.app.securitypolicy.zopepolicy.ZopeSecurityPolicy"
+      />
+
+  <unauthenticatedPrincipal
+      id="zope.anybody"
+      title="Unauthenticated User"
+      />
+  <grant
+      permission="zope.View"
+      principal="zope.anybody"
+      />
+
+  <principal
+      id="zope.mgr"
+      title="Manager"
+      login="mgr"
+      password="mgrpw"
+      />
+
+  <role id="zope.Manager" title="Site Manager" />
+  <grantAll role="zope.Manager" />
+  <grant role="zope.Manager" principal="zope.mgr" />
+
+</configure>

Added: Sandbox/faassen/rdbintegration/trunk/src/rdbintegration/testing.py
===================================================================
--- Sandbox/faassen/rdbintegration/trunk/src/rdbintegration/testing.py	                        (rev 0)
+++ Sandbox/faassen/rdbintegration/trunk/src/rdbintegration/testing.py	2008-06-16 18:28:31 UTC (rev 87431)
@@ -0,0 +1,7 @@
+import os.path
+import rdbintegration
+from zope.app.testing.functional import ZCMLLayer
+
+ftesting_zcml = os.path.join(
+    os.path.dirname(rdbintegration.__file__), 'ftesting.zcml')
+FunctionalLayer = ZCMLLayer(ftesting_zcml, __name__, 'FunctionalLayer')



More information about the Checkins mailing list