evolve(db, how=0)
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
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() >>> conn = db.open() >>> root = conn.root() >>> evolve(db) >>> conn.sync() >>> root[generations_key]['app1'] 1 >>> root[generations_key]['app2'] 11
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)
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.
>>> from zope.testing import loggingsupport >>> handler = loggingsupport.InstalledHandler('zope.app.generations')>>> app1.erron = 4 >>> app1.generation = 7 >>> evolve(db)>>> print handler zope.app.generations ERROR Failed to evolve database to generation 4 for app1
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 = 5
Then we'll get an error if we try to evolve, since we can't get past 3 and 3 is less than 5:
>>> evolve(db) Traceback (most recent call last): ... UnableToEvolve: (4, u'app1', 7)
We'll also get a log entry:
>>> print handler zope.app.generations ERROR Failed to evolve database to generation 4 for app1 zope.app.generations ERROR Failed to evolve database to generation 4 for app1
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:
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', 5)
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'] 5 >>> root.get('app1') 5
If we happen to install an app that has a generation that is less that 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: (5, u'app1', 2)
We'd better clean up:
>>> handler.uninstall() >>> conn.close() >>> db.close() >>> tearDown()