[Checkins] SVN: lovely.session/trunk/ initial import

Manfred Schwendinger manfred.schwendiger at lovelysystems.com
Mon Dec 10 08:00:00 EST 2007


Log message for revision 82223:
  initial import

Changed:
  A   lovely.session/trunk/
  A   lovely.session/trunk/CHANGES.txt
  A   lovely.session/trunk/LICENSE.txt
  A   lovely.session/trunk/bootstrap.py
  A   lovely.session/trunk/buildout.cfg
  A   lovely.session/trunk/setup.py
  A   lovely.session/trunk/src/
  A   lovely.session/trunk/src/lovely/
  A   lovely.session/trunk/src/lovely/__init__.py
  A   lovely.session/trunk/src/lovely/session/
  A   lovely.session/trunk/src/lovely/session/README.txt
  A   lovely.session/trunk/src/lovely/session/__init__.py
  A   lovely.session/trunk/src/lovely/session/configure.zcml
  A   lovely.session/trunk/src/lovely/session/interfaces.py
  A   lovely.session/trunk/src/lovely/session/lovely.session-configure.zcml
  A   lovely.session/trunk/src/lovely/session/memcached.py
  A   lovely.session/trunk/src/lovely/session/tests.py

-=-
Added: lovely.session/trunk/CHANGES.txt
===================================================================
--- lovely.session/trunk/CHANGES.txt	                        (rev 0)
+++ lovely.session/trunk/CHANGES.txt	2007-12-10 12:59:59 UTC (rev 82223)
@@ -0,0 +1,15 @@
+==============
+lovely.session
+==============
+
+2007/08/13 0.1.2
+----------------
+
+- move source to zope.org
+
+
+2007/08/13 0.1.1
+----------------
+
+- fixed dependency on lovely.memcached
+


Property changes on: lovely.session/trunk/CHANGES.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: lovely.session/trunk/LICENSE.txt
===================================================================
--- lovely.session/trunk/LICENSE.txt	                        (rev 0)
+++ lovely.session/trunk/LICENSE.txt	2007-12-10 12:59:59 UTC (rev 82223)
@@ -0,0 +1,42 @@
+ZPL 2.1
+Zope Public License (ZPL) Version 2.1
+
+A copyright notice accompanies this license document that identifies
+the copyright holders.
+
+This license has been certified as open source. It has also been
+designated as GPL compatible by the Free Software Foundation (FSF).
+
+Redistribution and use in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+Redistributions in source code must retain the accompanying copyright
+notice, this list of conditions, and the following disclaimer.
+Redistributions in binary form must reproduce the accompanying
+copyright notice, this list of conditions, and the following
+disclaimer in the documentation and/or other materials provided
+with the distribution.
+Names of the copyright holders must not be used to endorse or
+promote products derived from this software without prior written
+permission from the copyright holders.
+The right to distribute this software or to use it for any purpose
+does not give you the right to use Servicemarks (sm) or Trademarks (tm)
+of the copyright holders. Use of them is covered by separate
+agreement with the copyright holders.
+If any files are modified, you must cause the modified files to
+carry prominent notices stating that you changed the files and
+the date of any change.
+
+Disclaimer
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


Property changes on: lovely.session/trunk/LICENSE.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: lovely.session/trunk/bootstrap.py
===================================================================
--- lovely.session/trunk/bootstrap.py	                        (rev 0)
+++ lovely.session/trunk/bootstrap.py	2007-12-10 12:59:59 UTC (rev 82223)
@@ -0,0 +1,47 @@
+##############################################################################
+#
+# Copyright (c) 2007 Lovely Systems 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.
+#
+##############################################################################
+"""
+$Id$
+"""
+
+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)


Property changes on: lovely.session/trunk/bootstrap.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: lovely.session/trunk/buildout.cfg
===================================================================
--- lovely.session/trunk/buildout.cfg	                        (rev 0)
+++ lovely.session/trunk/buildout.cfg	2007-12-10 12:59:59 UTC (rev 82223)
@@ -0,0 +1,12 @@
+[buildout]
+develop = .
+parts = test
+extensions = lovely.buildouthttp
+find-links = http://download.lovelysystems.com/eggs/mirror/zope
+             https://download.lovelysystems.com/eggs/tagesspiegel
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = lovely.session [test]
+
+

Added: lovely.session/trunk/setup.py
===================================================================
--- lovely.session/trunk/setup.py	                        (rev 0)
+++ lovely.session/trunk/setup.py	2007-12-10 12:59:59 UTC (rev 82223)
@@ -0,0 +1,50 @@
+import os
+from setuptools import setup, find_packages
+
+def read(*rnames):
+    return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+
+long_description=(
+        read('src', 'lovely', 'session', 'README.txt')
+        + '\n' +
+        read('CHANGES.txt')
+        + '\n' +
+        'Download\n'
+        '========\n'
+        )
+
+name='lovely.session'
+setup(
+    name = name
+    version = '0.1.2',
+    author = "Lovely Systems GmbH",
+    author_email = "office at lovelysystems.com",
+    description = "memcache session",
+    long_description = long_description,
+    license = "ZPL 2.1",
+    keywords = "session zope zope3",
+    url = 'https://launchpad.net/lovely.tal',
+    zip_safe = False,
+    packages = find_packages('src'),
+    include_package_data = True,
+    package_dir = {'':'src'},
+    namespace_packages = ['lovely',],
+    install_requires = ['setuptools',
+                        'lovely.memcached',
+                        'zope.app.container',
+                        'zope.app.session',
+                        'zope.schema',
+                        ],
+    extras_require = dict(
+        test = ['zope.app.testing',
+                'zope.interface',
+                'zope.security',
+                'zope.testing',]),
+    classifiers = [
+       'Development Status :: 4 - Beta',
+       'Intended Audience :: Developers',
+       'License :: OSI Approved :: Zope Public License',
+       'Topic :: Software Development :: Libraries :: Python Modules',
+       ],        
+    )
+


Property changes on: lovely.session/trunk/setup.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: lovely.session/trunk/src/lovely/__init__.py
===================================================================
--- lovely.session/trunk/src/lovely/__init__.py	                        (rev 0)
+++ lovely.session/trunk/src/lovely/__init__.py	2007-12-10 12:59:59 UTC (rev 82223)
@@ -0,0 +1,23 @@
+##############################################################################
+#
+# Copyright (c) 2007 Lovely Systems 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.
+#
+##############################################################################
+"""
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+    pass


Property changes on: lovely.session/trunk/src/lovely/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: lovely.session/trunk/src/lovely/session/README.txt
===================================================================
--- lovely.session/trunk/src/lovely/session/README.txt	                        (rev 0)
+++ lovely.session/trunk/src/lovely/session/README.txt	2007-12-10 12:59:59 UTC (rev 82223)
@@ -0,0 +1,68 @@
+===========================
+Session Data Using memcache
+===========================
+
+This package provides a session data manager which stores it's data in
+memcache. The package uses lovely.memcached to store it's data.
+
+IMPORTANT:
+
+This test expects a memcache server running on local port 11211 which
+is the default port for memcached.
+
+This test runs in level 2 because it needs external resources to work. If you
+want to run this test you need to use --all as parameter to your test.
+
+Start a memcache instance with : memcached <optional options>
+
+  >>> from zope import component
+  >>> from lovely.memcached.interfaces import IMemcachedClient
+  >>> from lovely.memcached.utility import MemcachedClient
+  >>> util = MemcachedClient()
+  >>> component.provideUtility(util, IMemcachedClient, name='session')
+  >>> util.invalidateAll()
+
+Now we create a memcache session and connect it to the memcached client.
+
+  >>> from lovely.session.memcached import MemCachedSessionDataContainer
+  >>> sessionData = MemCachedSessionDataContainer()
+  >>> sessionData.cacheName = u'session'
+
+We need to provide a name for the session data manager because it is used to
+identify the cache entry in memcache.
+
+  >>> sessionData.__name__ = 'MemCacheSession'
+
+  >>> session = sessionData['mySessionId']
+  >>> session
+  {}
+  >>> type(session)
+  <class 'lovely.session.memcached.MemCacheSessionData'>
+
+We can now get data from the session.
+
+  >>> data = session['myData']
+  >>> data
+  {}
+  >>> type(data)
+  <class 'lovely.session.memcached.MemCachePkgData'>
+
+  >>> data['info'] = 'stored in memcache'
+  >>> data
+  {'info': 'stored in memcache'}
+
+Because the MemCacheSession is transaction aware we need to commit the
+transaction to store data in the memcache.
+
+  >>> import transaction
+  >>> transaction.commit()
+
+If we now read session data is is read back from the memcache.
+
+  >>> session = sessionData['mySessionId']
+  >>> session['myData']
+  {'info': 'stored in memcache'}
+
+  >>> sessionData.items()
+  [('mySessionId', <lovely.session.memcached.DataManager object at ...>)]
+


Property changes on: lovely.session/trunk/src/lovely/session/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: lovely.session/trunk/src/lovely/session/__init__.py
===================================================================


Property changes on: lovely.session/trunk/src/lovely/session/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: lovely.session/trunk/src/lovely/session/configure.zcml
===================================================================
--- lovely.session/trunk/src/lovely/session/configure.zcml	                        (rev 0)
+++ lovely.session/trunk/src/lovely/session/configure.zcml	2007-12-10 12:59:59 UTC (rev 82223)
@@ -0,0 +1,49 @@
+<configure 
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser"
+    i18n_domain="zope"
+    >
+
+  <class class=".memcached.MemCachedSessionDataContainer">
+    <implements
+        interface="zope.annotation.interfaces.IAttributeAnnotatable"
+        />
+
+    <require 
+        permission="zope.Public" 
+        interface="zope.app.session.interfaces.ISessionDataContainer"
+        />
+
+    <require 
+        permission="zope.ManageServices" 
+        set_schema="zope.app.session.interfaces.ISessionDataContainer"
+        />
+
+    <require 
+        permission="zope.Public" 
+        interface=".interfaces.IMemCachedSessionDataContainer"
+        />
+
+    <require 
+        permission="zope.ManageServices" 
+        set_schema=".interfaces.IMemCachedSessionDataContainer"
+        />
+  </class>
+
+  <browser:addMenuItem
+      title="Sessiondata in memcache"
+      description="Session data memcache"
+      class=".memcached.MemCachedSessionDataContainer"
+      permission="zope.ManageServices"
+      />
+
+  <browser:editform
+      schema=".interfaces.IMemCachedSessionDataContainer"
+      label="Configure"
+      name="configure.html"
+      menu="zmi_views" title="Configure"
+      permission="zope.ManageServices"
+      />
+
+</configure>
+


Property changes on: lovely.session/trunk/src/lovely/session/configure.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: lovely.session/trunk/src/lovely/session/interfaces.py
===================================================================
--- lovely.session/trunk/src/lovely/session/interfaces.py	                        (rev 0)
+++ lovely.session/trunk/src/lovely/session/interfaces.py	2007-12-10 12:59:59 UTC (rev 82223)
@@ -0,0 +1,29 @@
+##############################################################################
+#
+# Copyright (c) 2007 Lovely Systems 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.
+#
+##############################################################################
+"""
+$Id$
+"""
+__docformat__ = 'restructuredtext'
+
+from zope import interface
+from zope import schema
+
+
+class IMemCachedSessionDataContainer(interface.Interface):
+    """A session container using memcache"""
+
+    cacheName = schema.TextLine(title=u'Cachename',
+                                required=False,
+                                default=u'')
+


Property changes on: lovely.session/trunk/src/lovely/session/interfaces.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: lovely.session/trunk/src/lovely/session/lovely.session-configure.zcml
===================================================================
--- lovely.session/trunk/src/lovely/session/lovely.session-configure.zcml	                        (rev 0)
+++ lovely.session/trunk/src/lovely/session/lovely.session-configure.zcml	2007-12-10 12:59:59 UTC (rev 82223)
@@ -0,0 +1 @@
+<include package="lovely.session" />


Property changes on: lovely.session/trunk/src/lovely/session/lovely.session-configure.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: lovely.session/trunk/src/lovely/session/memcached.py
===================================================================
--- lovely.session/trunk/src/lovely/session/memcached.py	                        (rev 0)
+++ lovely.session/trunk/src/lovely/session/memcached.py	2007-12-10 12:59:59 UTC (rev 82223)
@@ -0,0 +1,197 @@
+##############################################################################
+#
+# Copyright (c) 2007 Lovely Systems 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.
+#
+##############################################################################
+"""
+$Id$
+"""
+__docformat__ = 'restructuredtext'
+
+import md5
+import cPickle
+import memcache
+import time
+from threading import local
+
+import persistent
+import transaction
+from transaction.interfaces import IDataManager
+
+from zope import interface
+from zope import component
+
+from zope.schema.fieldproperty import FieldProperty
+
+from zope.app.container.contained import Contained
+from zope.app.session.interfaces import (
+        ISessionDataContainer,
+        ISessionData,
+        ISessionPkgData,
+        )
+from zope.app.session.session import PersistentSessionDataContainer
+
+from lovely.memcached.interfaces import IMemcachedClient
+
+from interfaces import IMemCachedSessionDataContainer
+
+
+class MemCachedSessionDataContainer(persistent.Persistent, Contained):
+    interface.implements(ISessionDataContainer, IMemCachedSessionDataContainer)
+
+    cacheName = FieldProperty(IMemCachedSessionDataContainer['cacheName'])
+
+    def __init__(self):
+        self.resolution = 5*60
+        self.timeout = 1 * 60 * 60
+
+    def get(self, key, default=None):
+        return self[key]
+
+    def __contains__(self, key):
+        return key in self.dataManagers
+
+    def __getitem__(self, key):
+        # getitem never fails to make sure the session uses our own PkgData
+        # implementation.
+        if key in self.dataManagers:
+            dm = self.dataManagers[key]
+        else:
+            dm = DataManager(self, key)
+            txn = transaction.manager.get()
+            txn.join(dm)
+            self.dataManagers[key] = dm
+            item = self.client.query(self._buildKey(key))
+            if item is None:
+                item = MemCacheSessionData()
+            dm.data = item
+        return dm.data
+
+    def __setitem__(self, key, value):
+        pass
+
+    def items(self):
+        return self.dataManagers.items()
+
+    def _commitManager(self, key):
+        if key in self.dataManagers:
+            dm = self.dataManagers[key]
+            if not dm.changed:
+                del self.dataManagers[key]
+                return
+            if self.client.set(dm.data,
+                               self._buildKey(key),
+                               self.timeout):
+                del self.dataManagers[key]
+
+    def _abortManager(self, key):
+        #TODO: ???
+        if key in self.dataManagers:
+            del self.dataManagers[key]
+
+    def _buildKey(self, key):
+        m = md5.new(key)
+        return 'lovely.session.memcached:%s/%s'%(
+                    self.__name__.encode('utf-8'),
+                    m.hexdigest())
+
+    @property
+    def dataManagers(self):
+        res = getattr(self.storage, 'dataManagers', None)
+        if res is None:
+            res = {}
+            self.storage.dataManagers = res
+        return res
+
+    @property
+    def client(self):
+        return component.getUtility(IMemcachedClient, self.cacheName)
+
+    @property
+    def storage(self):
+        # we use a thread local storage to have a memcache client for every
+        # thread.
+        if not hasattr(self, '_v_storage'):
+            self._v_storage = local()
+        return self._v_storage
+
+
+class MemCacheData(dict):
+    """See zope.app.session.interfaces.ISessionData
+    """
+    interface.implements(ISessionPkgData)
+
+
+class MemCacheSessionData(dict):
+    """See zope.app.session.interfaces.ISessionData"""
+    interface.implements(ISessionData)
+    lastAccessTime = 0
+
+    def __init__(self):
+        self.lastAccessTime = int(time.time())
+
+    def __getitem__(self, key):
+        try:
+            return super(MemCacheSessionData, self).__getitem__(key)
+        except KeyError:
+            data = MemCachePkgData()
+            self[key] = data
+            return data
+
+
+class MemCachePkgData(dict):
+    """"""
+    interface.implements(ISessionPkgData)
+
+
+class DataManager(object):
+    """A data manager for the transaction management of the session data"""
+    interface.implements(IDataManager)
+
+    def __init__(self, sessionData, key):
+        self.sessionData = sessionData
+        self._data = None
+        self.key = key
+
+    @apply
+    def data():
+        def get(self):
+            return self._data
+        def set(self, value):
+            self._data = value
+            self._oldData = cPickle.dumps(value)
+        return property(get, set)
+
+    @property
+    def changed(self):
+        return cPickle.dumps(self.data)!=self._oldData
+
+    def abort(self, trans):
+        self.sessionData._abortManager(self.key)
+
+    def tpc_begin(self, trans):
+        pass
+
+    def commit(self, trans):
+        pass
+
+    def tpc_vote(self, trans):
+        pass
+
+    def tpc_finish(self, trans):
+        self.sessionData._commitManager(self.key)
+
+    def tpc_abort(self, trans):
+        self.sessionData._abortManager(self.key)
+
+    def sortKey(self):
+        return str(id(self))
+


Property changes on: lovely.session/trunk/src/lovely/session/memcached.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: lovely.session/trunk/src/lovely/session/tests.py
===================================================================
--- lovely.session/trunk/src/lovely/session/tests.py	                        (rev 0)
+++ lovely.session/trunk/src/lovely/session/tests.py	2007-12-10 12:59:59 UTC (rev 82223)
@@ -0,0 +1,39 @@
+##############################################################################
+#
+# Copyright (c) 2007 Lovely Systems 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.
+#
+##############################################################################
+"""
+$Id$
+"""
+__docformat__ = 'restructuredtext'
+
+import unittest
+from zope import component
+import zope.interface
+import zope.security
+from zope.testing import doctest
+from zope.testing.doctestunit import DocTestSuite, DocFileSuite
+from zope.app.testing import setup
+
+
+def test_suite():
+    level2Suites = (
+        DocFileSuite('README.txt',
+             optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+             ),
+            )
+    for suite in level2Suites:
+        suite.level = 2
+    return unittest.TestSuite(level2Suites)
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')


Property changes on: lovely.session/trunk/src/lovely/session/tests.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native



More information about the Checkins mailing list