[Checkins] SVN: zope.app.generations/trunk/ merged icemac-zope.generations branch to trunk, using new zope.generations
Michael Howitz
mh at gocept.com
Sat Sep 18 08:06:31 EDT 2010
Log message for revision 116570:
merged icemac-zope.generations branch to trunk, using new zope.generations
Changed:
U zope.app.generations/trunk/CHANGES.txt
U zope.app.generations/trunk/README.txt
U zope.app.generations/trunk/buildout.cfg
U zope.app.generations/trunk/setup.py
D zope.app.generations/trunk/src/zope/app/generations/README.txt
U zope.app.generations/trunk/src/zope/app/generations/__init__.py
U zope.app.generations/trunk/src/zope/app/generations/browser/managerdetails.py
U zope.app.generations/trunk/src/zope/app/generations/browser/managers.py
U zope.app.generations/trunk/src/zope/app/generations/browser/tests.py
U zope.app.generations/trunk/src/zope/app/generations/configure.zcml
D zope.app.generations/trunk/src/zope/app/generations/demo/
D zope.app.generations/trunk/src/zope/app/generations/demo2/
D zope.app.generations/trunk/src/zope/app/generations/demo3/
U zope.app.generations/trunk/src/zope/app/generations/generations.py
U zope.app.generations/trunk/src/zope/app/generations/interfaces.py
U zope.app.generations/trunk/src/zope/app/generations/subscriber.zcml
D zope.app.generations/trunk/src/zope/app/generations/tests.py
U zope.app.generations/trunk/src/zope/app/generations/utility.py
-=-
Modified: zope.app.generations/trunk/CHANGES.txt
===================================================================
--- zope.app.generations/trunk/CHANGES.txt 2010-09-18 11:31:37 UTC (rev 116569)
+++ zope.app.generations/trunk/CHANGES.txt 2010-09-18 12:06:31 UTC (rev 116570)
@@ -5,7 +5,7 @@
3.6.1 (unreleased)
------------------
-- Nothing changed yet.
+- Depends now on the extracted ``zope.generations``.
3.6.0 (2010-09-17)
Modified: zope.app.generations/trunk/README.txt
===================================================================
--- zope.app.generations/trunk/README.txt 2010-09-18 11:31:37 UTC (rev 116569)
+++ zope.app.generations/trunk/README.txt 2010-09-18 12:06:31 UTC (rev 116570)
@@ -2,3 +2,7 @@
schema changes. An application schema is essentially the structure of data,
the structure of classes in the case of ZODB or the table descriptions in the
case of a relational database.
+
+This package only contains the ZMI user interface for `zope.generations`_
+
+.. _zope.generations: http://pypi.python.org/pypi/zope.generations
\ No newline at end of file
Modified: zope.app.generations/trunk/buildout.cfg
===================================================================
--- zope.app.generations/trunk/buildout.cfg 2010-09-18 11:31:37 UTC (rev 116569)
+++ zope.app.generations/trunk/buildout.cfg 2010-09-18 12:06:31 UTC (rev 116570)
@@ -1,5 +1,7 @@
[buildout]
develop = .
+ ../zope.generations
+ ../zope.app.zopeappgenerations
parts = test coverage-test coverage-report
[test]
Modified: zope.app.generations/trunk/setup.py
===================================================================
--- zope.app.generations/trunk/setup.py 2010-09-18 11:31:37 UTC (rev 116569)
+++ zope.app.generations/trunk/setup.py 2010-09-18 12:06:31 UTC (rev 116570)
@@ -27,22 +27,16 @@
return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
setup(name='zope.app.generations',
- version='3.6.1dev',
+ version='3.7.0dev',
author='Zope Corporation and Contributors',
author_email='zope-dev at zope.org',
- description='Zope Application Schema Generations',
+ description='ZMI UI for zope.generations',
long_description=(
read('README.txt')
+ '\n\n.. contents::\n\n' +
- '======================\n'
- 'Detailed Documentation\n'
- '======================\n'
- + '\n\n' +
- read('src', 'zope', 'app', 'generations', 'README.txt')
- + '\n\n' +
read('CHANGES.txt')
),
- keywords = "zope3 zodb schema generation",
+ keywords = "zope zmi zodb schema generation",
classifiers = [
'Development Status :: 5 - Production/Stable',
'Environment :: Web Environment',
@@ -64,15 +58,18 @@
'zope.login',
'zope.publisher >= 3.12',
'zope.securitypolicy',
+ 'zope.testing >= 3.8',
]),
- install_requires=['setuptools',
- 'zope.app.renderer',
- 'zope.interface',
- 'zope.app.publication',
- 'ZODB3',
- 'zope.processlifetime',
- 'zope.applicationcontrol',
- ],
+ install_requires=[
+ 'ZODB3',
+ 'setuptools',
+ 'zope.app.publication',
+ 'zope.app.renderer',
+ 'zope.applicationcontrol',
+ 'zope.generations',
+ 'zope.interface',
+ 'zope.processlifetime',
+ ],
include_package_data = True,
zip_safe = False,
)
Deleted: zope.app.generations/trunk/src/zope/app/generations/README.txt
===================================================================
--- zope.app.generations/trunk/src/zope/app/generations/README.txt 2010-09-18 11:31:37 UTC (rev 116569)
+++ zope.app.generations/trunk/src/zope/app/generations/README.txt 2010-09-18 12:06:31 UTC (rev 116570)
@@ -1,354 +0,0 @@
-Generations are a way of updating objects in the database when the application
-schema changes. An application schema is essentially the structure of data,
-the structure of classes in the case of ZODB or the table descriptions in the
-case of a relational database.
-
-When you change your application's data structures, for example,
-you change the semantic meaning of an existing field in a class, you will
-have a problem with databases that were created before your change. For a
-more thorough discussion and possible solutions, see
-http://wiki.zope.org/zope3/DatabaseGenerations
-
-We will be using the component architecture, and we will need a database and a
-connection:
-
- >>> import cgi
- >>> from pprint import pprint
- >>> from zope.interface import implements
- >>> from zope.app.testing import ztapi
-
- >>> from ZODB.tests.util import DB
- >>> db = DB()
- >>> conn = db.open()
- >>> root = conn.root()
-
-Imagine that our application is an oracle: you can teach it to react to
-phrases. Let's keep it simple and store the data in a dict:
-
- >>> root['answers'] = {'Hello': 'Hi & how do you do?',
- ... 'Meaning of life?': '42',
- ... 'four < ?': 'four < five'}
- >>> import transaction
- >>> transaction.commit()
-
-
-Initial setup
--------------
-
-Here's some generations-specific code. We will create and register a
-SchemaManager. SchemaManagers are responsible for the actual updates of the
-database. This one will be just a dummy. The point here is to make the
-generations module aware that our application supports generations.
-
-The default implementation of SchemaManager is not suitable for this test
-because it uses Python modules to manage generations. For now, it
-will be just fine, since we don't want it to do anything just yet.
-
- >>> from zope.app.generations.interfaces import ISchemaManager
- >>> from zope.app.generations.generations import SchemaManager
- >>> dummy_manager = SchemaManager(minimum_generation=0, generation=0)
- >>> ztapi.provideUtility(ISchemaManager, dummy_manager, name='some.app')
-
-'some.app' is a unique identifier. You should use a URI or the dotted name
-of your package.
-
-When you start Zope and a database is opened, an event
-IDatabaseOpenedWithRoot is sent. Zope registers
-evolveMinimumSubscriber by default as a handler for this event. Let's
-simulate this:
-
- >>> class DatabaseOpenedEventStub(object):
- ... def __init__(self, database):
- ... self.database = database
- >>> event = DatabaseOpenedEventStub(db)
-
- >>> from zope.app.generations.generations import evolveMinimumSubscriber
- >>> evolveMinimumSubscriber(event)
-
-The consequence of this action is that now the database contains the fact
-that our current schema number is 0. When we update the schema, Zope3 will
-have an idea of what the starting point was. Here, see?
-
- >>> from zope.app.generations.generations import generations_key
- >>> root[generations_key]['some.app']
- 0
-
-In real life you should never have to bother with this key directly,
-but you should be aware that it exists.
-
-
-Upgrade scenario
-----------------
-
-Back to the story. Some time passes and one of our clients gets hacked because
-we forgot to escape HTML special characters! The horror! We must fix this
-problem ASAP without losing any data. We decide to use generations to impress
-our peers.
-
-Let's update the schema manager (drop the old one and install a new custom
-one):
-
- >>> ztapi.unprovideUtility(ISchemaManager, name='some.app')
-
- >>> class MySchemaManager(object):
- ... implements(ISchemaManager)
- ...
- ... minimum_generation = 1
- ... generation = 2
- ...
- ... def evolve(self, context, generation):
- ... root = context.connection.root()
- ... answers = root['answers']
- ... if generation == 1:
- ... for question, answer in answers.items():
- ... answers[question] = cgi.escape(answer)
- ... elif generation == 2:
- ... for question, answer in answers.items():
- ... del answers[question]
- ... answers[cgi.escape(question)] = answer
- ... else:
- ... raise ValueError("Bummer")
- ... root['answers'] = answers # ping persistence
- ... transaction.commit()
-
- >>> manager = MySchemaManager()
- >>> ztapi.provideUtility(ISchemaManager, manager, name='some.app')
-
-We have set `minimum_generation` to 1. That means that our application
-will refuse to run with a database older than generation 1. The `generation`
-attribute is set to 2, which means that the latest generation that this
-SchemaManager knows about is 2.
-
-evolve() is the workhorse here. Its job is to get the database from
-`generation`-1 to `generation`. It gets a context which has the attribute
-'connection', which is a connection to the ZODB. You can use that to change
-objects like in this example.
-
-In this particular implementation generation 1 escapes the answers (say,
-critical, because they can be entered by anyone!), generation 2 escapes the
-questions (say, less important, because these can be entered by authorized
-personell only).
-
-In fact, you don't really need a custom implementation of ISchemaManager. One
-is available, we have used it for a dummy previously. It uses Python modules
-for organization of evolver functions. See its docstring for more information.
-
-In real life you will have much more complex object structures than the one
-here. To make your life easier, there are two very useful functions available
-in zope.app.generations.utility: findObjectsMatching() and
-findObjectsProviding(). They will dig through containers recursively to help
-you seek out old objects that you wish to update, by interface or by some other
-criteria. They are easy to understand, check their docstrings.
-
-
-Generations in action
----------------------
-
-So, our furious client downloads our latest code and restarts Zope. The event
-is automatically sent again:
-
- >>> event = DatabaseOpenedEventStub(db)
- >>> evolveMinimumSubscriber(event)
-
-Shazam! The client is happy again!
-
- >>> pprint(root['answers'])
- {'Hello': 'Hi & how do you do?',
- 'Meaning of life?': '42',
- 'four < ?': 'four < five'}
-
-Because evolveMinimumSubscriber is very lazy, it only updates the database just
-enough so that your application can use it (to the `minimum_generation`, that
-is). Indeed, the marker indicates that the database generation has been bumped
-to 1:
-
- >>> root[generations_key]['some.app']
- 1
-
-We see that generations are working, so we decide to take the next step
-and evolve to generation 2. Let's see how this can be done manually:
-
- >>> from zope.app.generations.generations import evolve
- >>> evolve(db)
-
- >>> pprint(root['answers'])
- {'Hello': 'Hi & how do you do?',
- 'Meaning of life?': '42',
- 'four < ?': 'four < five'}
- >>> root[generations_key]['some.app']
- 2
-
-Default behaviour of `evolve` upgrades to the latest generation provided by
-the SchemaManager. You can use the `how` argument to evolve() when you want
-just to check if you need to update or if you want to be lazy like the
-subscriber which we have called previously.
-
-
-Ordering of schema managers
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Frequently subsystems used to compose an application rely on other
-subsystems to operate properly. If both subsystems provide schema
-managers, it is often helpful to know the order in which the evolvers
-will be invoked. This allows a framework and it's clients to be able
-to evolve in concert, and the clients can know that the framework will
-be evolved before or after itself.
-
-This can be accomplished by controlling the names of the schema
-manager utilities. The schema managers are run in the order
-determined by sorting their names.
-
- >>> manager1 = SchemaManager(minimum_generation=0, generation=0)
- >>> manager2 = SchemaManager(minimum_generation=0, generation=0)
-
- >>> ztapi.provideUtility(
- ... ISchemaManager, manager1, name='another.app')
- >>> ztapi.provideUtility(
- ... ISchemaManager, manager2, name='another.app-extension')
-
-Notice how the name of the first package is used to create a namespace
-for dependent packages. This is not a requirement of the framework,
-but a convenient pattern for this usage.
-
-Let's evolve the database to establish these generations:
-
- >>> event = DatabaseOpenedEventStub(db)
- >>> evolveMinimumSubscriber(event)
-
- >>> root[generations_key]['another.app']
- 0
- >>> root[generations_key]['another.app-extension']
- 0
-
-Let's assume that for some reason each of these subsystems needs to
-add a generation, and that generation 1 of 'another.app-extension'
-depends on generation 1 of 'another.app'. We'll need to provide
-schema managers for each that record that they've been run so we can
-verify the result:
-
- >>> ztapi.unprovideUtility(ISchemaManager, name='another.app')
- >>> ztapi.unprovideUtility(ISchemaManager, name='another.app-extension')
-
- >>> class FoundationSchemaManager(object):
- ... implements(ISchemaManager)
- ...
- ... minimum_generation = 1
- ... generation = 1
- ...
- ... def evolve(self, context, generation):
- ... root = context.connection.root()
- ... ordering = root.get('ordering', [])
- ... if generation == 1:
- ... ordering.append('foundation 1')
- ... print 'foundation generation 1'
- ... else:
- ... raise ValueError("Bummer")
- ... root['ordering'] = ordering # ping persistence
- ... transaction.commit()
-
- >>> class DependentSchemaManager(object):
- ... implements(ISchemaManager)
- ...
- ... minimum_generation = 1
- ... generation = 1
- ...
- ... def evolve(self, context, generation):
- ... root = context.connection.root()
- ... ordering = root.get('ordering', [])
- ... if generation == 1:
- ... ordering.append('dependent 1')
- ... print 'dependent generation 1'
- ... else:
- ... raise ValueError("Bummer")
- ... root['ordering'] = ordering # ping persistence
- ... transaction.commit()
-
- >>> manager1 = FoundationSchemaManager()
- >>> manager2 = DependentSchemaManager()
-
- >>> ztapi.provideUtility(
- ... ISchemaManager, manager1, name='another.app')
- >>> ztapi.provideUtility(
- ... ISchemaManager, manager2, name='another.app-extension')
-
-Evolving the database now will always run the 'another.app' evolver
-before the 'another.app-extension' evolver:
-
- >>> event = DatabaseOpenedEventStub(db)
- >>> evolveMinimumSubscriber(event)
- foundation generation 1
- dependent generation 1
-
- >>> root['ordering']
- ['foundation 1', 'dependent 1']
-
-
-Installation
-------------
-
-In the the example above, we manually initialized the answers. We
-shouldn't have to do that manually. The application should be able to
-do that automatically.
-
-IInstallableSchemaManager extends ISchemaManager, providing an install
-method for performing an intial installation of an application. This
-is a better alternative than registering database-opened subscribers.
-
-Let's define a new schema manager that includes installation:
-
-
- >>> ztapi.unprovideUtility(ISchemaManager, name='some.app')
-
- >>> from zope.app.generations.interfaces import IInstallableSchemaManager
- >>> class MySchemaManager(object):
- ... implements(IInstallableSchemaManager)
- ...
- ... minimum_generation = 1
- ... generation = 2
- ...
- ... def install(self, context):
- ... root = context.connection.root()
- ... root['answers'] = {'Hello': 'Hi & how do you do?',
- ... 'Meaning of life?': '42',
- ... 'four < ?': 'four < five'}
- ... transaction.commit()
- ...
- ... def evolve(self, context, generation):
- ... root = context.connection.root()
- ... answers = root['answers']
- ... if generation == 1:
- ... for question, answer in answers.items():
- ... answers[question] = cgi.escape(answer)
- ... elif generation == 2:
- ... for question, answer in answers.items():
- ... del answers[question]
- ... answers[cgi.escape(question)] = answer
- ... else:
- ... raise ValueError("Bummer")
- ... root['answers'] = answers # ping persistence
- ... transaction.commit()
-
- >>> manager = MySchemaManager()
- >>> ztapi.provideUtility(ISchemaManager, manager, name='some.app')
-
-Now, lets open a new database:
-
- >>> db.close()
- >>> db = DB()
- >>> conn = db.open()
- >>> 'answers' in conn.root()
- False
-
-
- >>> event = DatabaseOpenedEventStub(db)
- >>> evolveMinimumSubscriber(event)
-
- >>> conn.sync()
- >>> root = conn.root()
-
- >>> pprint(root['answers'])
- {'Hello': 'Hi & how do you do?',
- 'Meaning of life?': '42',
- 'four < ?': 'four < five'}
- >>> root[generations_key]['some.app']
- 2
Modified: zope.app.generations/trunk/src/zope/app/generations/__init__.py
===================================================================
--- zope.app.generations/trunk/src/zope/app/generations/__init__.py 2010-09-18 11:31:37 UTC (rev 116569)
+++ zope.app.generations/trunk/src/zope/app/generations/__init__.py 2010-09-18 12:06:31 UTC (rev 116570)
@@ -11,7 +11,4 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-"""Experimental support for application database generations
-
-$Id$
-"""
+"""Support for application database generations."""
Modified: zope.app.generations/trunk/src/zope/app/generations/browser/managerdetails.py
===================================================================
--- zope.app.generations/trunk/src/zope/app/generations/browser/managerdetails.py 2010-09-18 11:31:37 UTC (rev 116569)
+++ zope.app.generations/trunk/src/zope/app/generations/browser/managerdetails.py 2010-09-18 12:06:31 UTC (rev 116570)
@@ -18,7 +18,7 @@
__docformat__ = "reStructuredText"
import zope.component
-from zope.app.generations.interfaces import ISchemaManager
+from zope.generations.interfaces import ISchemaManager
from zope.app.renderer.rest import ReStructuredTextToHTMLRenderer
class ManagerDetails(object):
@@ -32,9 +32,9 @@
We need to define some schema managers. We'll define just one:
- >>> from zope.app.generations.generations import SchemaManager
+ >>> from zope.generations.generations import SchemaManager
>>> from zope.app.testing import ztapi
- >>> app1 = SchemaManager(0, 3, 'zope.app.generations.demo')
+ >>> app1 = SchemaManager(0, 3, 'zope.generations.demo')
>>> ztapi.provideUtility(ISchemaManager, app1, 'foo.app1')
Now let's create the view:
Modified: zope.app.generations/trunk/src/zope/app/generations/browser/managers.py
===================================================================
--- zope.app.generations/trunk/src/zope/app/generations/browser/managers.py 2010-09-18 11:31:37 UTC (rev 116569)
+++ zope.app.generations/trunk/src/zope/app/generations/browser/managers.py 2010-09-18 12:06:31 UTC (rev 116570)
@@ -21,8 +21,8 @@
import zope.component
-from zope.app.generations.interfaces import ISchemaManager
-from zope.app.generations.generations import generations_key, Context
+from zope.generations.interfaces import ISchemaManager
+from zope.generations.generations import generations_key, Context
request_key_format = "evolve-app-%s"
@@ -63,16 +63,16 @@
We need to define some schema managers. We'll define two
using the demo package:
- >>> from zope.app.generations.generations import SchemaManager
+ >>> from zope.generations.generations import SchemaManager
>>> from zope.app.testing import ztapi
- >>> app1 = SchemaManager(0, 1, 'zope.app.generations.demo')
+ >>> app1 = SchemaManager(0, 1, 'zope.generations.demo')
>>> ztapi.provideUtility(ISchemaManager, app1, 'foo.app1')
- >>> app2 = SchemaManager(0, 0, 'zope.app.generations.demo')
+ >>> app2 = SchemaManager(0, 0, 'zope.generations.demo')
>>> ztapi.provideUtility(ISchemaManager, app2, 'foo.app2')
And we need to record some data for them in the database.
- >>> from zope.app.generations.generations import evolve
+ >>> from zope.generations.generations import evolve
>>> evolve(db)
This sets up the data and actually evolves app1:
@@ -108,13 +108,13 @@
The demo evolver just writes the generation to a database key:
- >>> from zope.app.generations.demo import key
+ >>> from zope.generations.demo import key
>>> conn.root()[key]
('installed', 'installed', 2)
Note that, because the demo package has an install script,
we have entries for that script.
-
+
Which the returned status should indicate:
>>> status['app']
@@ -132,7 +132,7 @@
2
>>> conn.root()[key]
('installed', 'installed', 2)
-
+
as the status will indicate by returning a 'to' generation
of 0:
@@ -214,16 +214,16 @@
We need to define some schema managers. We'll define two
using the demo package:
- >>> from zope.app.generations.generations import SchemaManager
+ >>> from zope.generations.generations import SchemaManager
>>> from zope.app.testing import ztapi
- >>> app1 = SchemaManager(0, 1, 'zope.app.generations.demo')
+ >>> app1 = SchemaManager(0, 1, 'zope.generations.demo')
>>> ztapi.provideUtility(ISchemaManager, app1, 'foo.app1')
- >>> app2 = SchemaManager(0, 0, 'zope.app.generations.demo')
+ >>> app2 = SchemaManager(0, 0, 'zope.generations.demo')
>>> ztapi.provideUtility(ISchemaManager, app2, 'foo.app2')
And we need to record some data for them in the database.
- >>> from zope.app.generations.generations import evolve
+ >>> from zope.generations.generations import evolve
>>> evolve(db)
This sets up the data and actually evolves app1:
Modified: zope.app.generations/trunk/src/zope/app/generations/browser/tests.py
===================================================================
--- zope.app.generations/trunk/src/zope/app/generations/browser/tests.py 2010-09-18 11:31:37 UTC (rev 116569)
+++ zope.app.generations/trunk/src/zope/app/generations/browser/tests.py 2010-09-18 12:06:31 UTC (rev 116570)
@@ -19,17 +19,17 @@
import doctest
from zope.app.generations.testing import GenerationsLayer
from zope.app.testing import ztapi, functional
-from zope.app.generations.generations import SchemaManager, generations_key
-from zope.app.generations.interfaces import ISchemaManager
+from zope.generations.generations import SchemaManager, generations_key
+from zope.generations.interfaces import ISchemaManager
class TestDatabaseSchema(functional.BrowserTestCase):
def test(self):
root = self.getRootFolder()._p_jar.root()
- appkey = 'zope.app.generations.demo'
+ appkey = 'zope.generations.demo'
root[generations_key][appkey] = 0
self.commit()
- manager = SchemaManager(0, 3, 'zope.app.generations.demo')
+ manager = SchemaManager(0, 3, 'zope.generations.demo')
ztapi.provideUtility(ISchemaManager, manager, appkey)
@@ -37,40 +37,40 @@
basic='globalmgr:globalmgrpw')
body = response.getBody()
body = ' '.join(body.split())
- expect = ('zope.app.generations.demo</a> </td> '
+ expect = ('zope.generations.demo</a> </td> '
'<td>0</td> <td>3</td> <td>0</td> '
'<td> <input type="submit" value=" evolve " '
- 'name="evolve-app-zope.app.generations.demo"> </td>')
+ 'name="evolve-app-zope.generations.demo"> </td>')
self.assert_(body.find(expect) > 0)
response = self.publish('/++etc++process/@@generations.html'
- '?evolve-app-zope.app.generations.demo=evolve',
+ '?evolve-app-zope.generations.demo=evolve',
basic='globalmgr:globalmgrpw')
body = response.getBody()
body = ' '.join(body.split())
- expect = ('zope.app.generations.demo</a> </td> '
+ expect = ('zope.generations.demo</a> </td> '
'<td>0</td> <td>3</td> <td>1</td> '
'<td> <input type="submit" value=" evolve " '
- 'name="evolve-app-zope.app.generations.demo"> </td>')
+ 'name="evolve-app-zope.generations.demo"> </td>')
self.assert_(body.find(expect) > 0)
response = self.publish('/++etc++process/@@generations.html'
- '?evolve-app-zope.app.generations.demo=evolve',
+ '?evolve-app-zope.generations.demo=evolve',
basic='globalmgr:globalmgrpw')
body = response.getBody()
body = ' '.join(body.split())
- expect = ('zope.app.generations.demo</a> </td> '
+ expect = ('zope.generations.demo</a> </td> '
'<td>0</td> <td>3</td> <td>2</td> '
'<td> <input type="submit" value=" evolve " '
- 'name="evolve-app-zope.app.generations.demo"> </td>')
+ 'name="evolve-app-zope.generations.demo"> </td>')
self.assert_(body.find(expect) > 0)
response = self.publish('/++etc++process/@@generations.html'
- '?evolve-app-zope.app.generations.demo=evolve',
+ '?evolve-app-zope.generations.demo=evolve',
basic='globalmgr:globalmgrpw')
body = response.getBody()
body = ' '.join(body.split())
- expect = ('zope.app.generations.demo</a> </td> '
+ expect = ('zope.generations.demo</a> </td> '
'<td>0</td> <td>3</td> <td>3</td> '
'<td> <span>')
self.assert_(body.find(expect) > 0)
@@ -86,7 +86,3 @@
doctest.DocTestSuite('zope.app.generations.browser.managerdetails'),
unittest.makeSuite(TestDatabaseSchema),
))
-
-if __name__ == '__main__':
- unittest.main(defaultTest='test_suite')
-
Modified: zope.app.generations/trunk/src/zope/app/generations/configure.zcml
===================================================================
--- zope.app.generations/trunk/src/zope/app/generations/configure.zcml 2010-09-18 11:31:37 UTC (rev 116569)
+++ zope.app.generations/trunk/src/zope/app/generations/configure.zcml 2010-09-18 12:06:31 UTC (rev 116570)
@@ -1,36 +1,6 @@
<configure xmlns="http://namespaces.zope.org/zope">
- <!--
- <utility
- name="zope.app"
- provides=".interfaces.ISchemaManager"
- factory=".generations.SchemaManager"
- >
-
- Provide an *initial* schema manager for zope.
-
- We can use a factory here, because the generation is 0.
-
- When we get to generation 1, we'll have to actually create
- a manager instance with the necessary parameters and a package of
- evolution scripts.
- </utility>
- -->
-
+ <include package="zope.generations" />
<include package=".browser" />
- <!-- Registering documentation with API doc -->
- <configure
- xmlns:apidoc="http://namespaces.zope.org/apidoc"
- xmlns:zcml="http://namespaces.zope.org/zcml"
- zcml:condition="have apidoc">
-
- <apidoc:bookchapter
- id="generations"
- title="Generations"
- doc_path="README.txt"
- />
-
- </configure>
-
</configure>
Modified: zope.app.generations/trunk/src/zope/app/generations/generations.py
===================================================================
--- zope.app.generations/trunk/src/zope/app/generations/generations.py 2010-09-18 11:31:37 UTC (rev 116569)
+++ zope.app.generations/trunk/src/zope/app/generations/generations.py 2010-09-18 12:06:31 UTC (rev 116570)
@@ -11,511 +11,13 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-"""Experimental support for application database generations
+"""Support for application database generations."""
-$Id$
-"""
-__docformat__ = 'restructuredtext'
-
-import logging
-
-import transaction
-
-import zope.component
-import zope.interface
-
-from interfaces import GenerationTooHigh, GenerationTooLow, UnableToEvolve
-from interfaces import ISchemaManager, IInstallableSchemaManager
-
-
-logger = logging.getLogger('zope.app.generations')
-generations_key = 'zope.app.generations'
-
-
-class SchemaManager(object):
- """Schema manager
-
- Schema managers implement `IInstallableSchemaManager` using
- scripts provided as module methods. You create a schema
- manager by providing mimumum and maximum generations and a
- package providing modules named ``evolveN``, where ``N`` is a
- generation number. Each module provides a function, `evolve`
- that evolves a database from the previous generation.
-
- For the sake of the example, we'll use the demo package defined
- in here. See the modules there for simple examples of evolution
- scripts.
-
- So, if we'll create a SchemaManager:
-
- >>> manager = SchemaManager(1, 3, 'zope.app.generations.demo')
-
- and we'll create a test database and context:
-
- >>> from ZODB.tests.util import DB
- >>> db = DB()
- >>> context = Context()
- >>> context.connection = db.open()
-
- Then we'll evolve the database from generation 1 to 3:
-
- >>> manager.evolve(context, 2)
- >>> manager.evolve(context, 3)
- >>> transaction.commit()
-
- The demo evolvers simply record their data in a root key:
-
- >>> from zope.app.generations.demo import key
- >>> conn = db.open()
- >>> conn.root()[key]
- (2, 3)
-
- You can get the information for each evolver by specifying the
- destination generation of the evolver as argument to the `getInfo()`
- method:
-
- >>> manager.getInfo(1)
- 'Evolver 1'
- >>> manager.getInfo(2)
- 'Evolver 2'
- >>> manager.getInfo(3) is None
- True
-
- If a package provides an install script, then it will be called
- when the manager's intall method is called:
-
- >>> conn.sync()
- >>> del conn.root()[key]
- >>> transaction.commit()
- >>> conn.root().get(key)
-
- >>> manager.install(context)
- >>> transaction.commit()
- >>> conn.sync()
- >>> conn.root()[key]
- ('installed',)
-
- If there is not install script, the manager will do nothing on
- an install:
-
- >>> manager = SchemaManager(1, 3, 'zope.app.generations.demo2')
- >>> manager.install(context)
-
- We handle ImportErrors within the script specially, so they get promoted:
-
- >>> manager = SchemaManager(1, 3, 'zope.app.generations.demo3')
- >>> manager.install(context)
- Traceback (most recent call last):
- ImportError: No module named nonexistingmodule
-
- We'd better clean up:
-
- >>> context.connection.close()
- >>> conn.close()
- >>> db.close()
-
-
- """
-
- zope.interface.implements(IInstallableSchemaManager)
-
- def __init__(self, minimum_generation=0, generation=0, package_name=None):
- if generation < minimum_generation:
- raise ValueError("generation is less than minimum_generation",
- generation, minimum_generation)
- if minimum_generation < 0:
- raise ValueError("generations must be non-negative",
- minimum_generation)
-
- if generation and not package_name:
- raise ValueError("A package name must be supplied if the"
- " generation is non-zero")
-
- self.minimum_generation = minimum_generation
- self.generation = generation
- self.package_name = package_name
-
- def evolve(self, context, generation):
- """Evolve a database to reflect software/schema changes
- """
- name = "%s.evolve%d" % (self.package_name, generation)
-
- evolver = __import__(name, {}, {}, ['*'])
-
- evolver.evolve(context)
-
- def install(self, context):
- """Evolve a database to reflect software/schema changes
- """
- name = "%s.install" % self.package_name
-
- try:
- evolver = __import__(name, {}, {}, ['*'])
- except ImportError, m:
- if str(m) not in ('No module named %s' % name,
- 'No module named install'):
- # This was an import error *within* the module, so we re-raise.
- raise
- else:
- evolver.evolve(context)
-
- def getInfo(self, generation):
- """Get the information from the evolver function's doc string."""
- evolver = __import__(
- "%s.evolve%d" % (self.package_name, generation),
- {}, {}, ['*'])
- return evolver.evolve.__doc__
-
-
-class Context(object):
- pass
-
-
-def findManagers():
- # Hook to let Chris use this for Zope 2
- return zope.component.getUtilitiesFor(ISchemaManager)
-
-
-def PersistentDict():
- # Another hook to let Chris use this for Zope 2
- import persistent.dict
- return persistent.dict.PersistentDict()
-
-
-EVOLVE, EVOLVENOT, EVOLVEMINIMUM = 'EVOLVE', 'EVOLVENOT', 'EVOLVEMINIMUM'
-
-
-def evolve(db, how=EVOLVE):
- """Evolve a database
-
- We evolve a database using registered application schema managers.
- Here's an example (silly) schema manager:
-
- >>> from zope.app.generations.interfaces import ISchemaManager
- >>> class FauxApp(object):
- ... zope.interface.implements(ISchemaManager)
- ...
- ... erron = None # Raise an error is asked to evolve to this
- ...
- ... def __init__(self, name, minimum_generation, generation):
- ... self.name, self.generation = name, generation
- ... self.minimum_generation = minimum_generation
- ...
- ... def evolve(self, context, generation):
- ... if generation == self.erron:
- ... raise ValueError(generation)
- ...
- ... context.connection.root()[self.name] = generation
-
- Evolving a database will cause log messages to be written, so we need a
- logging handler:
-
- >>> from zope.testing import loggingsupport
- >>> loghandler = loggingsupport.InstalledHandler('zope.app.generations')
- >>> def print_log():
- ... print loghandler
- ... loghandler.clear()
-
- We also need to set up the component system, since we'll be
- registering utilities:
-
- >>> from zope.app.testing.placelesssetup import setUp, tearDown
- >>> setUp()
-
- Now, we'll create and register some handlers:
-
- >>> from zope.app.testing import ztapi
- >>> app1 = FauxApp('app1', 0, 1)
- >>> ztapi.provideUtility(ISchemaManager, app1, name='app1')
- >>> app2 = FauxApp('app2', 5, 11)
- >>> ztapi.provideUtility(ISchemaManager, app2, name='app2')
-
- If we create a new database, and evolve it, we'll simply update
- the generation data:
-
- >>> from ZODB.tests.util import DB
- >>> db = DB(database_name='testdb')
- >>> conn = db.open()
- >>> root = conn.root()
- >>> evolve(db)
- >>> conn.sync()
- >>> root[generations_key]['app1']
- 1
- >>> root[generations_key]['app2']
- 11
-
- >>> print_log()
- zope.app.generations INFO
- testdb: evolving in mode EVOLVE
-
- But nothing will have been done to the database:
-
- >>> root.get('app1')
- >>> root.get('app2')
-
- Now if we increase the generation of one of the apps:
-
- >>> app1.generation += 1
- >>> evolve(db)
-
- >>> print_log()
- zope.app.generations INFO
- testdb: evolving in mode EVOLVE
- zope.app.generations INFO
- testdb/app1: currently at generation 1, targetting generation 2
- zope.app.generations DEBUG
- testdb/app1: evolving to generation 2
- zope.app.generations DEBUG
- testdb/app2: up-to-date at generation 11
-
- We'll see that the generation data has updated:
-
- >>> conn.sync()
- >>> root[generations_key]['app1']
- 2
- >>> root[generations_key]['app2']
- 11
-
- And that the database was updated for that application:
-
- >>> root.get('app1')
- 2
- >>> root.get('app2')
-
- If there is an error updating a particular generation, but the
- generation is greater than the minimum generation, then we won't
- get an error from evolve, but we will get a log message.
-
- >>> app1.erron = 4
- >>> app1.generation = 7
- >>> evolve(db)
-
- >>> print_log()
- zope.app.generations INFO
- testdb: evolving in mode EVOLVE
- zope.app.generations INFO
- testdb/app1: currently at generation 2, targetting generation 7
- zope.app.generations DEBUG
- testdb/app1: evolving to generation 3
- zope.app.generations DEBUG
- testdb/app1: evolving to generation 4
- zope.app.generations ERROR
- testdb/app1: failed to evolve to generation 4
- zope.app.generations DEBUG
- testdb/app2: up-to-date at generation 11
-
- The database will have been updated for previous generations:
-
- >>> conn.sync()
- >>> root[generations_key]['app1']
- 3
- >>> root.get('app1')
- 3
-
- If we set the minimum generation for app1 to something greater than 3:
-
- >>> app1.minimum_generation = 4
-
- Then we'll get an error if we try to evolve, since we can't get
- past 3 and 3 is less than 4:
-
- >>> evolve(db)
- Traceback (most recent call last):
- ...
- UnableToEvolve: (4, u'app1', 7)
-
- We'll also get a log entry:
-
- >>> print_log()
- zope.app.generations INFO
- testdb: evolving in mode EVOLVE
- zope.app.generations INFO
- testdb/app1: currently at generation 3, targetting generation 7
- zope.app.generations DEBUG
- testdb/app1: evolving to generation 4
- zope.app.generations ERROR
- testdb/app1: failed to evolve to generation 4
-
- So far, we've used evolve in its default policy, in which we evolve
- as far as we can up to the current generation. There are two
- other policies:
-
- EVOLVENOT -- Don't evolve, but make sure that the application is
- at the minimum generation
-
- EVOLVEMINIMUM -- Evolve only to the minimum generation
-
- Let's change unset erron for app1 so we don't get an error when we
- try to evolve.
-
- >>> app1.erron = None
-
- Now, we'll call evolve with EVOLVENOT:
-
- >>> evolve(db, EVOLVENOT)
- Traceback (most recent call last):
- ...
- GenerationTooLow: (3, u'app1', 4)
-
- >>> print_log()
- zope.app.generations INFO
- testdb: evolving in mode EVOLVENOT
- zope.app.generations ERROR
- testdb/app1: current generation too low (3 < 4) but mode is EVOLVENOT
-
- We got an error because we aren't at the minimum generation for
- app1. The database generation for app1 is still 3 because we
- didn't do any evolution:
-
- >>> conn.sync()
- >>> root[generations_key]['app1']
- 3
- >>> root.get('app1')
- 3
-
- Now, if we use EVOLVEMINIMUM instead, we'll evolve to the minimum
- generation:
-
- >>> evolve(db, EVOLVEMINIMUM)
- >>> conn.sync()
- >>> root[generations_key]['app1']
- 4
- >>> root.get('app1')
- 4
-
- >>> print_log()
- zope.app.generations INFO
- testdb: evolving in mode EVOLVEMINIMUM
- zope.app.generations INFO
- testdb/app1: currently at generation 3, targetting generation 4
- zope.app.generations DEBUG
- testdb/app1: evolving to generation 4
- zope.app.generations DEBUG
- testdb/app2: up-to-date at generation 11
-
- If we happen to install an app that has a generation that is less
- than the database generation, we'll get an error, because there is
- no way to get the database to a generation that the app
- understands:
-
- >>> app1.generation = 2
- >>> app1.minimum_generation = 0
- >>> evolve(db)
- Traceback (most recent call last):
- ...
- GenerationTooHigh: (4, u'app1', 2)
-
- >>> print_log()
- zope.app.generations INFO
- testdb: evolving in mode EVOLVE
- zope.app.generations ERROR
- testdb/app1: current generation too high (4 > 2)
-
- We'd better clean up:
-
- >>> loghandler.uninstall()
- >>> conn.close()
- >>> db.close()
- >>> tearDown()
-
- """
- db_name = db.database_name or 'main db'
- logger.info('%s: evolving in mode %s' %
- (db_name, how))
- conn = db.open()
- try:
- context = Context()
- context.connection = conn
- root = conn.root()
- generations = root.get(generations_key)
- if generations is None:
- generations = root[generations_key] = PersistentDict()
- transaction.commit()
-
- for key, manager in sorted(findManagers()):
- generation = generations.get(key)
-
- if generation == manager.generation:
- logger.debug('%s/%s: up-to-date at generation %s' %
- (db_name, key, generation))
- continue
-
- if generation is None:
- # This is a new database, so no old data
-
- if IInstallableSchemaManager.providedBy(manager):
- try:
- logger.info("%s/%s: running install generation",
- db_name, key)
- manager.install(context)
- except:
- transaction.abort()
- logger.exception("%s/%s: failed to run install",
- db_name, key)
- raise
-
- generations[key] = manager.generation
- transaction.commit()
- continue
-
- if generation > manager.generation:
- logger.error('%s/%s: current generation too high (%d > %d)',
- db_name, key,
- generation, manager.generation)
- raise GenerationTooHigh(generation, key, manager.generation)
-
- if generation < manager.minimum_generation:
- if how == EVOLVENOT:
- logger.error('%s/%s: current generation too low '
- '(%d < %d) but mode is %s',
- db_name, key,
- generation, manager.minimum_generation,
- how)
- raise GenerationTooLow(
- generation, key, manager.minimum_generation)
- else:
- if how != EVOLVE:
- continue
-
- if how == EVOLVEMINIMUM:
- target = manager.minimum_generation
- else:
- target = manager.generation
-
- logger.info('%s/%s: currently at generation %d, targetting generation %d',
- db_name, key, generation, target)
-
- while generation < target:
- generation += 1
- try:
- transaction.begin()
- logger.debug('%s/%s: evolving to generation %d',
- db_name, key, generation)
- manager.evolve(context, generation)
- generations[key] = generation
- transaction.commit()
- except:
- # An unguarded handler is intended here
- transaction.abort()
- logger.exception(
- "%s/%s: failed to evolve to generation %d",
- db_name, key, generation)
-
- if generation <= manager.minimum_generation:
- raise UnableToEvolve(generation, key,
- manager.generation)
- break
- finally:
- conn.close()
-
-
-def evolveSubscriber(event):
- evolve(event.database, EVOLVE)
-
-
-def evolveNotSubscriber(event):
- evolve(event.database, EVOLVENOT)
-
-
-def evolveMinimumSubscriber(event):
- evolve(event.database, EVOLVEMINIMUM)
+# BBB imports
+from zope.generations.interfaces import (
+ GenerationTooHigh, GenerationTooLow, UnableToEvolve,
+ ISchemaManager, IInstallableSchemaManager)
+from zope.generations.generations import (
+ generations_key, SchemaManager, Context, findManagers, PersistentDict,
+ EVOLVE, EVOLVENOT, EVOLVEMINIMUM, evolve, evolveSubscriber,
+ evolveNotSubscriber, evolveMinimumSubscriber)
Modified: zope.app.generations/trunk/src/zope/app/generations/interfaces.py
===================================================================
--- zope.app.generations/trunk/src/zope/app/generations/interfaces.py 2010-09-18 11:31:37 UTC (rev 116569)
+++ zope.app.generations/trunk/src/zope/app/generations/interfaces.py 2010-09-18 12:06:31 UTC (rev 116570)
@@ -11,81 +11,9 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-"""Interfaces for experimental support for application database generations
+"""Interfaces for support for application database generations"""
-$Id$
-"""
-__docformat__ = 'restructuredtext'
-
-import zope.interface
-
-class GenerationError(Exception):
- """A database generation is invalid
- """
-
-class GenerationTooHigh(GenerationError):
- """A database generation is higher than an application generation
- """
-
-class GenerationTooLow(GenerationError):
- """A database generation is lower than an application minimum generation
- """
-
-class UnableToEvolve(GenerationError):
- """A database can't evolve to an application minimum generation
- """
-
-
-class ISchemaManager(zope.interface.Interface):
- """Manage schema evolution for an application."""
-
- minimum_generation = zope.interface.Attribute(
- "Minimum supported schema generation")
-
- generation = zope.interface.Attribute(
- "Current schema generation")
-
- def evolve(context, generation):
- """Evolve a database to the given schema generation.
-
- The database should be assumed to be at the schema
- generation one less than the given `generation`
- argument. In other words, the `evolve` method is only
- required to make one evolutionary step.
-
- The `context` argument has a connection attribute,
- providing a database connection to be used to change
- the database. A `context` argument is passed rather than
- a connection to make it possible to provide additional
- information later, if it becomes necessary.
-
- This method should *not* commit a transaction. The
- transaction will be committed by the caller if there is no
- error. It is acceptable to commit a transaction if there are no
- subsequent operations. The method may create savepoints.
-
- """
-
- def getInfo(generation):
- """Return an information string about the evolution that is used to
- upgrade to the specified generation.
-
- If no information is available, `None` should be returned.
- """
-
-class IInstallableSchemaManager(ISchemaManager):
- """Manage schema evolution for an application, including installation."""
-
- def install(context):
- """Perform any initial installation tasks
-
- The application has never had the application installed
- before. The schema manager should bring the database to the
- current generation.
-
- This method should *not* commit a transaction. The
- transaction will be committed by the caller if there is no
- error. It is acceptable to commit a transaction if there are no
- subsequent operations. The method may create savepoints.
-
- """
+# BBB imports
+from zope.generations.interfaces import (
+ GenerationError, GenerationTooHigh, GenerationTooLow, UnableToEvolve,
+ ISchemaManager, IInstallableSchemaManager)
Modified: zope.app.generations/trunk/src/zope/app/generations/subscriber.zcml
===================================================================
--- zope.app.generations/trunk/src/zope/app/generations/subscriber.zcml 2010-09-18 11:31:37 UTC (rev 116569)
+++ zope.app.generations/trunk/src/zope/app/generations/subscriber.zcml 2010-09-18 12:06:31 UTC (rev 116570)
@@ -1,28 +1,7 @@
<configure xmlns="http://namespaces.zope.org/zope">
-<!--
-<subscriber
- handler=".generations.evolveSubscriber"
- for="zope.processlifetime.IDatabaseOpenedWithRoot"
- >
- Evolve to current generation on startup
-</subscriber>
--->
+ <!-- BBB registration -->
-<!--
-<subscriber
- handler=".generations.evolveNotSubscriber"
- for="zope.processlifetime.IDatabaseOpenedWithRoot"
- >
- Don't evolve, but check for minimum generations on startup
-</subscriber>
--->
+ <include package="zope.generations" file="subscriber.zcml" />
-<subscriber
- handler=".generations.evolveMinimumSubscriber"
- for="zope.processlifetime.IDatabaseOpenedWithRoot"
- >
- Only evolve to minimum generations on startup
-</subscriber>
-
</configure>
Deleted: zope.app.generations/trunk/src/zope/app/generations/tests.py
===================================================================
--- zope.app.generations/trunk/src/zope/app/generations/tests.py 2010-09-18 11:31:37 UTC (rev 116569)
+++ zope.app.generations/trunk/src/zope/app/generations/tests.py 2010-09-18 12:06:31 UTC (rev 116570)
@@ -1,37 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 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.
-#
-##############################################################################
-"""Schema-generation tests
-
-$Id$
-"""
-
-from zope.app.testing import placelesssetup
-import doctest
-import unittest
-
-
-def tearDownREADME(test):
- placelesssetup.tearDown(test)
- test.globs['db'].close()
-
-
-def test_suite():
- return unittest.TestSuite((
- doctest.DocFileSuite(
- 'README.txt',
- setUp=placelesssetup.setUp, tearDown=tearDownREADME,
- ),
- doctest.DocTestSuite('zope.app.generations.generations'),
- doctest.DocTestSuite('zope.app.generations.utility'),
- ))
Modified: zope.app.generations/trunk/src/zope/app/generations/utility.py
===================================================================
--- zope.app.generations/trunk/src/zope/app/generations/utility.py 2010-09-18 11:31:37 UTC (rev 116569)
+++ zope.app.generations/trunk/src/zope/app/generations/utility.py 2010-09-18 12:06:31 UTC (rev 116570)
@@ -11,154 +11,11 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-"""Utility functions for evolving database generations.
+"""Utility functions for evolving database generations."""
-$Id$
-"""
-__docformat__ = "reStructuredText"
-
-import zope.app.publication.zopepublication
-
-
-def findObjectsMatching(root, condition):
- """Find all objects in the root that match the condition.
-
- The condition is a callable Python object that takes an object as an
- argument and must return `True` or `False`.
-
- All sub-objects of the root will also be searched recursively. All mapping
- objects providing `values()` are supported.
-
- Example:
-
- >>> class A(dict):
- ... def __init__(self, name):
- ... self.name = name
-
- >>> class B(dict):
- ... def __init__(self, name):
- ... self.name = name
-
- >>> class C(dict):
- ... def __init__(self, name):
- ... self.name = name
-
- >>> tree = A('a1')
- >>> tree['b1'] = B('b1')
- >>> tree['c1'] = C('c1')
- >>> tree['b1']['a2'] = A('a2')
- >>> tree['b1']['b2'] = B('b2')
- >>> tree['b1']['b2']['c2'] = C('c2')
- >>> tree['b1']['b2']['a3'] = A('a3')
-
- # Find all instances of class A
- >>> matches = findObjectsMatching(tree, lambda x: isinstance(x, A))
- >>> names = [x.name for x in matches]
- >>> names.sort()
- >>> names
- ['a1', 'a2', 'a3']
-
- # Find all objects having a '2' in the name
- >>> matches = findObjectsMatching(tree, lambda x: '2' in x.name)
- >>> names = [x.name for x in matches]
- >>> names.sort()
- >>> names
- ['a2', 'b2', 'c2']
- """
- if condition(root):
- yield root
-
- if hasattr(root, 'values'):
- for subobj in root.values():
- for match in findObjectsMatching(subobj, condition):
- yield match
-
-def findObjectsProviding(root, interface):
- """Find all objects in the root that provide the specified interface.
-
- All sub-objects of the root will also be searched recursively.
-
- Example:
-
- >>> from zope.interface import Interface, implements
- >>> class IA(Interface):
- ... pass
- >>> class IB(Interface):
- ... pass
- >>> class IC(IA):
- ... pass
-
- >>> class A(dict):
- ... implements(IA)
- ... def __init__(self, name):
- ... self.name = name
-
- >>> class B(dict):
- ... implements(IB)
- ... def __init__(self, name):
- ... self.name = name
-
- >>> class C(dict):
- ... implements(IC)
- ... def __init__(self, name):
- ... self.name = name
-
- >>> tree = A('a1')
- >>> tree['b1'] = B('b1')
- >>> tree['c1'] = C('c1')
- >>> tree['b1']['a2'] = A('a2')
- >>> tree['b1']['b2'] = B('b2')
- >>> tree['b1']['b2']['c2'] = C('c2')
- >>> tree['b1']['b2']['a3'] = A('a3')
-
- # Find all objects that provide IB
- >>> matches = findObjectsProviding(tree, IB)
- >>> names = [x.name for x in matches]
- >>> names.sort()
- >>> names
- ['b1', 'b2']
-
- # Find all objects that provide IA
- >>> matches = findObjectsProviding(tree, IA)
- >>> names = [x.name for x in matches]
- >>> names.sort()
- >>> names
- ['a1', 'a2', 'a3', 'c1', 'c2']
- """
- for match in findObjectsMatching(root, interface.providedBy):
- yield match
-
-
-def getRootFolder(context):
- """Get the root folder of the ZODB.
-
- We need some set up. Create a database:
-
- >>> from ZODB.tests.util import DB
- >>> from zope.app.generations.generations import Context
- >>> db = DB()
- >>> context = Context()
- >>> context.connection = db.open()
- >>> root = context.connection.root()
-
- Add a root folder:
-
- >>> from zope.site.folder import rootFolder
- >>> from zope.app.publication.zopepublication import ZopePublication
- >>> import transaction
- >>> root[ZopePublication.root_name] = rootFolder()
- >>> transaction.commit()
-
- Now we can get the root folder using the function:
-
- >>> getRootFolder(context) # doctest: +ELLIPSIS
- <zope.site.folder.Folder object at ...>
-
- We'd better clean up:
-
- >>> context.connection.close()
- >>> db.close()
-
- """
- return context.connection.root().get(
- zope.app.publication.zopepublication.ZopePublication.root_name, None)
+#BBB imports
+from zope.generations.utility import findObjectsMatching, findObjectsProviding
+try:
+ from zope.generations.utility import getRootFolder
+except ImportError:
+ pass
More information about the checkins
mailing list