Added 'use_pool' and 'echo_pool' attributes to engine interface
authorThierry Florac <tflorac@ulthar.net>
Sun, 23 Oct 2016 16:38:13 +0200
changeset 20 ce081139da77
parent 19 37d28950ea15
child 21 c412c833a9ad
Added 'use_pool' and 'echo_pool' attributes to engine interface
src/pyams_alchemy/engine.py
src/pyams_alchemy/interfaces/__init__.py
src/pyams_alchemy/locales/fr/LC_MESSAGES/pyams_alchemy.mo
src/pyams_alchemy/locales/fr/LC_MESSAGES/pyams_alchemy.po
src/pyams_alchemy/locales/pyams_alchemy.pot
src/pyams_alchemy/metaconfigure.py
src/pyams_alchemy/metadirectives.py
--- a/src/pyams_alchemy/engine.py	Tue Jul 12 09:17:27 2016 +0200
+++ b/src/pyams_alchemy/engine.py	Sun Oct 23 16:38:13 2016 +0200
@@ -23,7 +23,7 @@
 from sqlalchemy.event import listens_for
 from sqlalchemy.orm.scoping import scoped_session
 from sqlalchemy.orm.session import sessionmaker
-from sqlalchemy.pool import Pool
+from sqlalchemy.pool import Pool, NullPool
 from threading import Thread, Lock
 
 # import interfaces
@@ -110,18 +110,22 @@
     name = FieldProperty(IAlchemyEngineUtility['name'])
     dsn = FieldProperty(IAlchemyEngineUtility['dsn'])
     echo = FieldProperty(IAlchemyEngineUtility['echo'])
+    use_pool = FieldProperty(IAlchemyEngineUtility['use_pool'])
     pool_size = FieldProperty(IAlchemyEngineUtility['pool_size'])
     pool_recycle = FieldProperty(IAlchemyEngineUtility['pool_recycle'])
+    echo_pool = FieldProperty(IAlchemyEngineUtility['echo_pool'])
     encoding = FieldProperty(IAlchemyEngineUtility['encoding'])
     convert_unicode = FieldProperty(IAlchemyEngineUtility['convert_unicode'])
 
-    def __init__(self, name='', dsn='', echo=False, pool_size=25, pool_recycle=-1,
+    def __init__(self, name='', dsn='', echo=False, use_pool=True, pool_size=25, pool_recycle=-1, echo_pool=False,
                  encoding='utf-8', convert_unicode=False, **kwargs):
         self.name = name
         self.dsn = dsn
         self.echo = echo
+        self.use_pool = use_pool
         self.pool_size = pool_size
         self.pool_recycle = pool_recycle
+        self.echo_pool = echo_pool
         self.encoding = encoding
         self.convert_unicode = convert_unicode
         self.kw = PersistentDict()
@@ -132,21 +136,32 @@
         if (key != '_v_engine') and hasattr(self, '_v_engine'):
             delattr(self, '_v_engine')
 
-    def get_engine(self):
-        engine = getattr(self, '_v_engine', None)
-        if engine is not None:
-            return engine
-        kw = {}
+    def get_engine(self, use_pool=True):
+        kw = { }
         kw.update(self.kw)
-        self._v_engine = sqlalchemy.create_engine(self.dsn,
-                                                  echo=self.echo,
-                                                  pool_size=self.pool_size,
-                                                  pool_recycle=self.pool_recycle,
-                                                  encoding=self.encoding,
-                                                  convert_unicode=self.convert_unicode,
-                                                  strategy='threadlocal',
-                                                  **kw)
-        return self._v_engine
+        if not (use_pool and self.use_pool):
+            # Always create a new engine when pooling is disabled to help engine disposal
+            return sqlalchemy.create_engine(self.dsn,
+                                            echo=self.echo,
+                                            poolclass=NullPool,
+                                            encoding=self.encoding,
+                                            convert_unicode=self.convert_unicode,
+                                            strategy='threadlocal',
+                                            **kw)
+        else:
+            # Store engine into volatile attributes when pooling is enabled
+            engine = getattr(self, '_v_engine', None)
+            if engine is None:
+                engine = self._v_engine = sqlalchemy.create_engine(self.dsn,
+                                                                   echo=self.echo,
+                                                                   pool_size=self.pool_size,
+                                                                   pool_recycle=self.pool_recycle,
+                                                                   echo_pool=self.echo_pool,
+                                                                   encoding=self.encoding,
+                                                                   convert_unicode=self.convert_unicode,
+                                                                   strategy='threadlocal',
+                                                                   **kw)
+            return engine
 
 
 class PersistentAlchemyEngineUtility(Persistent, AlchemyEngineUtility, Contained):
@@ -167,16 +182,16 @@
     manager.unregisterUtility(event.object, IAlchemyEngineUtility, name=event.object.name or '')
 
 
-def get_engine(engine):
+def get_engine(engine, use_pool=True):
     """Get engine matching given utility name"""
     if isinstance(engine, str):
         engine = query_utility(IAlchemyEngineUtility, name=engine)
         if engine is not None:
-            return engine.get_engine()
+            return engine.get_engine(use_pool)
 
 
 def get_session(engine, join=True, status=STATUS_ACTIVE, request=None, alias=None,
-                twophase=True, use_zope_extension=True):
+                twophase=True, use_zope_extension=True, use_pool=True):
     """Get a new SQLALchemy session
 
     Session is stored in request and in session storage
@@ -188,7 +203,7 @@
     session_data = get_request_data(request, REQUEST_SESSION_KEY, {})
     session = session_data.get(alias)
     if session is None:
-        _engine = get_engine(engine)
+        _engine = get_engine(engine, use_pool)
         if use_zope_extension:
             factory = scoped_session(sessionmaker(bind=_engine,
                                                   twophase=twophase,
@@ -207,14 +222,14 @@
 
 
 def get_user_session(engine, join=True, status=STATUS_ACTIVE, request=None, alias=None,
-                     twophase=True, use_zope_extension=True):
+                     twophase=True, use_zope_extension=True, use_pool=True):
     """Get a new SQLAlchemy session
 
     `engine` can be a session name or an already created session (in which case it's
     returned as-is).
     """
     if isinstance(engine, str):
-        session = get_session(engine, join, status, request, alias, twophase, use_zope_extension)
+        session = get_session(engine, join, status, request, alias, twophase, use_zope_extension, use_pool)
     else:
         session = engine
     return session
--- a/src/pyams_alchemy/interfaces/__init__.py	Tue Jul 12 09:17:27 2016 +0200
+++ b/src/pyams_alchemy/interfaces/__init__.py	Sun Oct 23 16:38:13 2016 +0200
@@ -40,10 +40,16 @@
                    required=True,
                    default=u'sqlite://')
 
-    echo = Bool(title=_('Echo SQL'),
+    echo = Bool(title=_('Echo SQL?'),
+                description=_("Log all SQL statements to system logger"),
                 required=True,
                 default=False)
 
+    use_pool = Bool(title=_("Use connections pool?"),
+                    description=_("If 'no', collections pooling will be disabled"),
+                    required=True,
+                    default=True)
+
     pool_size = Int(title=_("Pool size"),
                     description=_("SQLAlchemy connections pool size"),
                     required=False,
@@ -54,6 +60,11 @@
                        required=False,
                        default=-1)
 
+    echo_pool = Bool(title=_("Echo pool?"),
+                     description=_("Log all pool checkouts/checkins to system logger?"),
+                     required=True,
+                     default=False)
+
     encoding = Choice(title=_('Encoding'),
                       required=True,
                       vocabulary='PyAMS encodings',
@@ -63,5 +74,5 @@
                            required=True,
                            default=False)
 
-    def get_engine(self):
+    def get_engine(self, use_pool=True):
         """Get SQLAlchemy engine"""
Binary file src/pyams_alchemy/locales/fr/LC_MESSAGES/pyams_alchemy.mo has changed
--- a/src/pyams_alchemy/locales/fr/LC_MESSAGES/pyams_alchemy.po	Tue Jul 12 09:17:27 2016 +0200
+++ b/src/pyams_alchemy/locales/fr/LC_MESSAGES/pyams_alchemy.po	Sun Oct 23 16:38:13 2016 +0200
@@ -5,7 +5,7 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE 1.0\n"
-"POT-Creation-Date: 2015-03-04 14:16+0100\n"
+"POT-Creation-Date: 2016-10-23 16:31+0200\n"
 "PO-Revision-Date: 2015-03-03 16:56+0100\n"
 "Last-Translator: Thierry Florac <tflorac@ulthar.net>\n"
 "Language-Team: French <traduc@traduc.org>\n"
@@ -39,86 +39,106 @@
 msgstr "Tracer les instructions SQL"
 
 #: src/pyams_alchemy/metadirectives.py:43
-#: src/pyams_alchemy/interfaces/__init__.py:47
+#: src/pyams_alchemy/interfaces/__init__.py:48
+msgid "Use connections pool?"
+msgstr "Utiliser un pool ?"
+
+#: src/pyams_alchemy/metadirectives.py:44
+#: src/pyams_alchemy/interfaces/__init__.py:49
+msgid "If 'no', collections pooling will be disabled"
+msgstr "Si 'non', l'utilisation d'un pool de connexions sera désactivé"
+
+#: src/pyams_alchemy/metadirectives.py:48
+#: src/pyams_alchemy/interfaces/__init__.py:53
 msgid "Pool size"
 msgstr "Taille du pool"
 
-#: src/pyams_alchemy/metadirectives.py:44
-#: src/pyams_alchemy/interfaces/__init__.py:48
+#: src/pyams_alchemy/metadirectives.py:49
+#: src/pyams_alchemy/interfaces/__init__.py:54
 msgid "SQLAlchemy connections pool size"
 msgstr "Taille du pool de connexions SQLALchemy"
 
-#: src/pyams_alchemy/metadirectives.py:48
-#: src/pyams_alchemy/interfaces/__init__.py:52
+#: src/pyams_alchemy/metadirectives.py:53
+#: src/pyams_alchemy/interfaces/__init__.py:58
 msgid "Pool recycle time"
 msgstr "Durée de recyclage"
 
-#: src/pyams_alchemy/metadirectives.py:49
-#: src/pyams_alchemy/interfaces/__init__.py:53
+#: src/pyams_alchemy/metadirectives.py:54
+#: src/pyams_alchemy/interfaces/__init__.py:59
 msgid "SQLAlchemy connection recycle time (-1 for none)"
 msgstr ""
-"Durée de vie des connexions avant leur recyclage ; indiquer -1 pour "
+"Durée de vie (en secondes) des connexions avant leur recyclage ; indiquer -1 pour "
 "conserver les connexions"
 
-#: src/pyams_alchemy/metadirectives.py:53
-#: src/pyams_alchemy/interfaces/__init__.py:57
+#: src/pyams_alchemy/metadirectives.py:58
+#: src/pyams_alchemy/interfaces/__init__.py:63
+msgid "Echo pool?"
+msgstr "Traces du pool ?"
+
+#: src/pyams_alchemy/metadirectives.py:59
+#: src/pyams_alchemy/interfaces/__init__.py:64
+msgid "Log all pool checkouts/checkins to system logger?"
+msgstr "Tracer toutes les entrées/sorties du pool de connexions dans le système de logs de l'application"
+
+#: src/pyams_alchemy/metadirectives.py:63
+#: src/pyams_alchemy/interfaces/__init__.py:68
 msgid "Encoding"
 msgstr "Encodage"
 
-#: src/pyams_alchemy/metadirectives.py:58
-#: src/pyams_alchemy/interfaces/__init__.py:62
+#: src/pyams_alchemy/metadirectives.py:68
+#: src/pyams_alchemy/interfaces/__init__.py:73
 msgid "Convert Unicode"
 msgstr "Conversion Unicode"
 
-#: src/pyams_alchemy/zmi/engine.py:55
+#: src/pyams_alchemy/zmi/engine.py:56
 msgid "Add SQLAlchemy engine..."
 msgstr "Ajouter un moteur SQLAlchemy..."
 
-#: src/pyams_alchemy/zmi/engine.py:65
+#: src/pyams_alchemy/zmi/engine.py:66
 msgid "Utilities"
 msgstr "Utilitaires"
 
-#: src/pyams_alchemy/zmi/engine.py:66
+#: src/pyams_alchemy/zmi/engine.py:67
 msgid "Add SQLAlchemy engine"
 msgstr "Ajout d'un moteur SQLAlchemy"
 
-#: src/pyams_alchemy/zmi/engine.py:112
+#: src/pyams_alchemy/zmi/engine.py:114
 msgid "Update SQLAlchemy engine properties"
 msgstr "Modification des propriétés d'un moteur SQLAlchemy"
 
-#: src/pyams_alchemy/zmi/engine.py:136
+#: src/pyams_alchemy/zmi/engine.py:138
 msgid "Test connection..."
 msgstr "Tester la connexion..."
 
-#: src/pyams_alchemy/zmi/engine.py:167
+#: src/pyams_alchemy/zmi/engine.py:169
 msgid "Test SQLAlchemy engine"
 msgstr "Test d'un moteur SQLAlchemy"
 
-#: src/pyams_alchemy/zmi/engine.py:203
+#: src/pyams_alchemy/zmi/engine.py:205
 msgid "Query results"
 msgstr "Résultats de la requête"
 
-#: src/pyams_alchemy/zmi/engine.py:146
+#: src/pyams_alchemy/zmi/engine.py:148
 msgid "SQL query"
 msgstr "Requête SQL"
 
-#: src/pyams_alchemy/zmi/engine.py:153
+#: src/pyams_alchemy/zmi/engine.py:155
 msgid "Close"
 msgstr "Fermer"
 
-#: src/pyams_alchemy/zmi/engine.py:154
+#: src/pyams_alchemy/zmi/engine.py:156
 msgid "Execute SQL"
 msgstr "Exécuter"
 
-#: src/pyams_alchemy/zmi/engine.py:91
+#: src/pyams_alchemy/zmi/engine.py:92
 msgid "Specified engine name is already used!"
 msgstr "Le nom indiqué pour ce moteur est déjà utilisé !"
 
-#: src/pyams_alchemy/zmi/engine.py:94
+#: src/pyams_alchemy/zmi/engine.py:95
 msgid "An SQLAlchemy engine is already registered with this name!"
 msgstr "Un moteur SQLAlchemy est déjà enregistré avec ce nom !"
 
-#: src/pyams_alchemy/zmi/engine.py:110 src/pyams_alchemy/zmi/engine.py:165
+#: src/pyams_alchemy/zmi/engine.py:112 src/pyams_alchemy/zmi/engine.py:167
 #, python-format
 msgid "SQLAlchemy engine: {0}"
 msgstr "Moteur SQLAlchemy : {0}"
@@ -132,5 +152,9 @@
 msgstr "DSN"
 
 #: src/pyams_alchemy/interfaces/__init__.py:43
-msgid "Echo SQL"
-msgstr "Écho SQL"
+msgid "Echo SQL?"
+msgstr "Traces SQL ?"
+
+#: src/pyams_alchemy/interfaces/__init__.py:44
+msgid "Log all SQL statements to system logger"
+msgstr "Tracer toutes les instructions SQL dans le système de logs de l'application"
--- a/src/pyams_alchemy/locales/pyams_alchemy.pot	Tue Jul 12 09:17:27 2016 +0200
+++ b/src/pyams_alchemy/locales/pyams_alchemy.pot	Sun Oct 23 16:38:13 2016 +0200
@@ -1,12 +1,12 @@
 # 
 # SOME DESCRIPTIVE TITLE
 # This file is distributed under the same license as the PACKAGE package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, 2015.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2016.
 #, fuzzy
 msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE 1.0\n"
-"POT-Creation-Date: 2015-03-04 14:16+0100\n"
+"POT-Creation-Date: 2016-10-23 16:31+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -39,84 +39,104 @@
 msgstr ""
 
 #: ./src/pyams_alchemy/metadirectives.py:43
-#: ./src/pyams_alchemy/interfaces/__init__.py:47
-msgid "Pool size"
+#: ./src/pyams_alchemy/interfaces/__init__.py:48
+msgid "Use connections pool?"
 msgstr ""
 
 #: ./src/pyams_alchemy/metadirectives.py:44
-#: ./src/pyams_alchemy/interfaces/__init__.py:48
-msgid "SQLAlchemy connections pool size"
+#: ./src/pyams_alchemy/interfaces/__init__.py:49
+msgid "If 'no', collections pooling will be disabled"
 msgstr ""
 
 #: ./src/pyams_alchemy/metadirectives.py:48
-#: ./src/pyams_alchemy/interfaces/__init__.py:52
-msgid "Pool recycle time"
+#: ./src/pyams_alchemy/interfaces/__init__.py:53
+msgid "Pool size"
 msgstr ""
 
 #: ./src/pyams_alchemy/metadirectives.py:49
-#: ./src/pyams_alchemy/interfaces/__init__.py:53
-msgid "SQLAlchemy connection recycle time (-1 for none)"
+#: ./src/pyams_alchemy/interfaces/__init__.py:54
+msgid "SQLAlchemy connections pool size"
 msgstr ""
 
 #: ./src/pyams_alchemy/metadirectives.py:53
-#: ./src/pyams_alchemy/interfaces/__init__.py:57
-msgid "Encoding"
+#: ./src/pyams_alchemy/interfaces/__init__.py:58
+msgid "Pool recycle time"
+msgstr ""
+
+#: ./src/pyams_alchemy/metadirectives.py:54
+#: ./src/pyams_alchemy/interfaces/__init__.py:59
+msgid "SQLAlchemy connection recycle time (-1 for none)"
 msgstr ""
 
 #: ./src/pyams_alchemy/metadirectives.py:58
-#: ./src/pyams_alchemy/interfaces/__init__.py:62
+#: ./src/pyams_alchemy/interfaces/__init__.py:63
+msgid "Echo pool?"
+msgstr ""
+
+#: ./src/pyams_alchemy/metadirectives.py:59
+#: ./src/pyams_alchemy/interfaces/__init__.py:64
+msgid "Log all pool checkouts/checkins to system logger?"
+msgstr ""
+
+#: ./src/pyams_alchemy/metadirectives.py:63
+#: ./src/pyams_alchemy/interfaces/__init__.py:68
+msgid "Encoding"
+msgstr ""
+
+#: ./src/pyams_alchemy/metadirectives.py:68
+#: ./src/pyams_alchemy/interfaces/__init__.py:73
 msgid "Convert Unicode"
 msgstr ""
 
-#: ./src/pyams_alchemy/zmi/engine.py:55
+#: ./src/pyams_alchemy/zmi/engine.py:56
 msgid "Add SQLAlchemy engine..."
 msgstr ""
 
-#: ./src/pyams_alchemy/zmi/engine.py:65
-msgid "Utilities"
-msgstr ""
-
 #: ./src/pyams_alchemy/zmi/engine.py:66
+msgid "Utilities"
+msgstr ""
+
+#: ./src/pyams_alchemy/zmi/engine.py:67
 msgid "Add SQLAlchemy engine"
 msgstr ""
 
-#: ./src/pyams_alchemy/zmi/engine.py:112
+#: ./src/pyams_alchemy/zmi/engine.py:114
 msgid "Update SQLAlchemy engine properties"
 msgstr ""
 
-#: ./src/pyams_alchemy/zmi/engine.py:136
+#: ./src/pyams_alchemy/zmi/engine.py:138
 msgid "Test connection..."
 msgstr ""
 
-#: ./src/pyams_alchemy/zmi/engine.py:167
+#: ./src/pyams_alchemy/zmi/engine.py:169
 msgid "Test SQLAlchemy engine"
 msgstr ""
 
-#: ./src/pyams_alchemy/zmi/engine.py:203
+#: ./src/pyams_alchemy/zmi/engine.py:205
 msgid "Query results"
 msgstr ""
 
-#: ./src/pyams_alchemy/zmi/engine.py:146
+#: ./src/pyams_alchemy/zmi/engine.py:148
 msgid "SQL query"
 msgstr ""
 
-#: ./src/pyams_alchemy/zmi/engine.py:153
+#: ./src/pyams_alchemy/zmi/engine.py:155
 msgid "Close"
 msgstr ""
 
-#: ./src/pyams_alchemy/zmi/engine.py:154
+#: ./src/pyams_alchemy/zmi/engine.py:156
 msgid "Execute SQL"
 msgstr ""
 
-#: ./src/pyams_alchemy/zmi/engine.py:91
+#: ./src/pyams_alchemy/zmi/engine.py:92
 msgid "Specified engine name is already used!"
 msgstr ""
 
-#: ./src/pyams_alchemy/zmi/engine.py:94
+#: ./src/pyams_alchemy/zmi/engine.py:95
 msgid "An SQLAlchemy engine is already registered with this name!"
 msgstr ""
 
-#: ./src/pyams_alchemy/zmi/engine.py:110 ./src/pyams_alchemy/zmi/engine.py:165
+#: ./src/pyams_alchemy/zmi/engine.py:112 ./src/pyams_alchemy/zmi/engine.py:167
 #, python-format
 msgid "SQLAlchemy engine: {0}"
 msgstr ""
@@ -130,5 +150,9 @@
 msgstr ""
 
 #: ./src/pyams_alchemy/interfaces/__init__.py:43
-msgid "Echo SQL"
+msgid "Echo SQL?"
 msgstr ""
+
+#: ./src/pyams_alchemy/interfaces/__init__.py:44
+msgid "Log all SQL statements to system logger"
+msgstr ""
--- a/src/pyams_alchemy/metaconfigure.py	Tue Jul 12 09:17:27 2016 +0200
+++ b/src/pyams_alchemy/metaconfigure.py	Sun Oct 23 16:38:13 2016 +0200
@@ -23,7 +23,8 @@
 from zope.component import zcml
 
 
-def engine_directive(context, name='', dsn='', echo=False, pool_size=25, pool_recycle=-2, encoding='utf-8',
-                     convert_unicode=False, **kwargs):
-    engine = AlchemyEngineUtility(name, dsn, echo, pool_size, pool_recycle, encoding, convert_unicode, **kwargs)
+def engine_directive(context, name='', dsn='', echo=False, use_pool=True, pool_size=25, pool_recycle=-2,
+                     echo_pool=False, encoding='utf-8', convert_unicode=False, **kwargs):
+    engine = AlchemyEngineUtility(name, dsn, echo, use_pool, pool_size, pool_recycle, echo_pool, encoding,
+                                  convert_unicode, **kwargs)
     zcml.utility(context, IAlchemyEngineUtility, engine, name=name)
--- a/src/pyams_alchemy/metadirectives.py	Tue Jul 12 09:17:27 2016 +0200
+++ b/src/pyams_alchemy/metadirectives.py	Sun Oct 23 16:38:13 2016 +0200
@@ -40,6 +40,11 @@
                 required=False,
                 default=False)
 
+    use_pool = Bool(title=_("Use connections pool?"),
+                    description=_("If 'no', collections pooling will be disabled"),
+                    required=True,
+                    default=True)
+
     pool_size = Int(title=_("Pool size"),
                     description=_("SQLAlchemy connections pool size"),
                     required=False,
@@ -50,6 +55,11 @@
                        required=False,
                        default=-1)
 
+    echo_pool = Bool(title=_("Echo pool?"),
+                     description=_("Log all pool checkouts/checkins to system logger?"),
+                     required=True,
+                     default=False)
+
     encoding = Choice(title=_('Encoding'),
                       required=True,
                       vocabulary='PyAMS encodings',