/usr/share/pyshared/zope.app.generations-3.6.1.egg-info/PKG-INFO is in python-zope.app.generations 3.6.1-0ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 | Metadata-Version: 1.1
Name: zope.app.generations
Version: 3.6.1
Summary: Zope Application Schema Generations
Home-page: http://pypi.python.org/pypi/zope.app.generations
Author: Zope Corporation and Contributors
Author-email: zope-dev@zope.org
License: ZPL 2.1
Description: 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.
.. contents::
======================
Detailed Documentation
======================
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
=======
CHANGES
=======
3.6.1 (2012-01-23)
------------------
- Replaced an undeclared test dependency on ``zope.app.authentication`` with
``zope.password``.
3.6.0 (2010-09-17)
------------------
- ``zope.app.generations`` depended on ``zope.app.applicationcontrol`` but
did not declare it. Modernized dependecy to ``zope.applicationcontrol`` as
the needed interface has been moved there.
- Using python's ``doctest`` module instead of deprecated
``zope.testing.doctest[unit]``.
- Replaced a testing dependency on ``zope.app.securitypolicy`` with one on
``zope.securitypolicy``.
3.5.1 (2010-01-08)
------------------
- Depend on new ``zope.processlifetime`` interfaces instead of using
BBB imports from ``zope.app.appsetup``.
- Fix ftesting.zcml due to ``zope.securitypolicy`` update.
- Fix tests using a newer zope.publisher that requires zope.login.
3.5.0 (2009-04-05)
------------------
- Moved ``getRootFolder`` utility method from
``zope.app.zopeappgenerations`` to ``zope.app.generations.utility``.
- Removed not necessary install dependency on ``zope.app.testing``.
3.4.2 (2009-01-27)
------------------
- Provide more logging output for the various stages and actions of evolving a
database.
- Fixed bug: A failing last generation would allow starting an app server
without having evolved to the minimum generation.
- Substitute zope.app.zapi by direct calls to its wrapped apis. See
bug 219302.
- Corrected author email and home page address.
3.4.1 (2007-10-31)
------------------
- Resolve ``ZopeSecurityPolicy`` deprecation warning.
3.4.0 (2007-10-24)
------------------
- Initial release independent of the main Zope tree.
Keywords: zope3 zodb schema generation
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Zope Public License
Classifier: Programming Language :: Python
Classifier: Natural Language :: English
Classifier: Operating System :: OS Independent
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Framework :: Zope3
|