[Zope-CVS] SVN: zversioning/trunk/src/versioning/tests/README.txt
last changes for Greg
Uwe Oestermeier
uwe_oestermeier at iwm-kmrc.de
Sun Oct 10 08:18:03 EDT 2004
Log message for revision 27888:
last changes for Greg
Changed:
U zversioning/trunk/src/versioning/tests/README.txt
-=-
Modified: zversioning/trunk/src/versioning/tests/README.txt
===================================================================
--- zversioning/trunk/src/versioning/tests/README.txt 2004-10-10 11:54:21 UTC (rev 27887)
+++ zversioning/trunk/src/versioning/tests/README.txt 2004-10-10 12:18:03 UTC (rev 27888)
@@ -16,15 +16,16 @@
>>> from zope.interface import directlyProvides
>>> from zope.app.versioncontrol.repository import declare_versioned
>>> from versioning.tests.repository_setup import registerAdapter
- >>> from zope.app.folder import Folder, rootFolder
- >>> registerAdapter()
+ >>> from zope.app.location.location import Location
+ >>> from zope.app.tests.setup import setUpTraversal
-
- >>> sample = rootFolder()
+ >>> registerAdapter()
+ >>> setUpTraversal()
+ >>> sample = Location()
>>> directlyProvides(sample, zope.app.traversing.interfaces.IContainmentRoot)
- >>> a = sample["a"] = Folder()
- >>> b = sample["b"] = Folder()
- >>> c = b["c"] = Folder()
+ >>> a = sample["a"] = Location()
+ >>> b = sample["b"] = Location()
+ >>> c = b["c"] = Location()
>>> for x in (sample, a, b, c) :
... directlyProvides(x, zope.app.versioncontrol.interfaces.IVersionable)
@@ -105,657 +106,4 @@
only the standard containment structure meachanism.
-
-This package provides a framework for managing multiple versions of objects
-within a ZODB database. The framework defines several interfaces that objects
-may provide to participate with the framework. For an object to particpate in
-version control, it must provide `IVersionable`. `IVersionable` is an
-interface that promises that there will be adapters to:
-- `INonVersionedData`, and
-
-- `IPhysicallyLocatable`.
-
-It also requires that instances support `IPersistent` and `IAnnotatable`.
-
-Normally, these interfaces will be provided by adapters. To simplify the
-example, we'll just create a class that already implements the required
-interfaces directly. We need to be careful to avoid including the __name__
-and __parent__ attributes in state copies, so even a fairly simple
-implementation of INonVersionedData has to deal with these for objects that
-contain their own location information.
-
-
-
-Some basic queries may be asked of objects without using an instance of
-`IVersionControl`. In particular, we can determine whether an object can be
-managed by version control by checking for the `IVersionable` interface:
-
- >>> interfaces.IVersionable.providedBy(samp)
- True
- >>> interfaces.IVersionable.providedBy(42)
- False
-
-We can also determine whether an object is actually under version
-control using the `IVersioned` interface:
-
- >>> interfaces.IVersioned.providedBy(samp)
- False
- >>> interfaces.IVersioned.providedBy(42)
- False
-
-Placing an object under version control requires an instance of an
-`IVersionControl` object. This package provides an implementation of this
-interface on the `Repository` class (from
-`zope.app.versioncontrol.repository`). Only the `IVersionControl` instance is
-responsible for providing version control operations; an instance should never
-be asked to perform operations directly.
-
- >>> import zope.app.versioncontrol.repository
- >>> import zope.interface.verify
-
- >>> repository = zope.app.versioncontrol.repository.Repository()
- >>> zope.interface.verify.verifyObject(
- ... interfaces.IVersionControl,
- ... repository)
- True
-
-In order to actually use version control, there must be an
-interaction. This is needed to allow the framework to determine the
-user making changes. Let's set up an interaction now. First we need a
-principal. For our purposes, a principal just needs to have an id:
-
- >>> class FauxPrincipal:
- ... def __init__(self, id):
- ... self.id = id
- >>> principal = FauxPrincipal('bob')
-
-Then we need to define an participation for the principal in the
-interaction:
-
- >>> class FauxParticipation:
- ... interaction=None
- ... def __init__(self, principal):
- ... self.principal = principal
- >>> participation = FauxParticipation(principal)
-
-Finally, we can create the interaction:
-
- >>> import zope.security.management
- >>> zope.security.management.newInteraction(participation)
-
-Now, let's put an object under version control and verify that we can
-determine that fact by checking against the interface:
-
- >>> repository.applyVersionControl(samp)
- >>> interfaces.IVersioned.providedBy(samp)
- True
- >>> util.commit()
-
-Once an object is under version control, it's possible to get an
-information object that provides some interesting bits of data:
-
- >>> info = repository.getVersionInfo(samp)
- >>> type(info.history_id)
- <type 'str'>
-
-It's an error to ask for the version info for an object which isn't
-under revision control:
-
- >>> samp2 = Sample()
- >>> repository.getVersionInfo(samp2)
- Traceback (most recent call last):
- ...
- VersionControlError: Object is not under version control.
-
- >>> repository.getVersionInfo(42)
- Traceback (most recent call last):
- ...
- VersionControlError: Object is not under version control.
-
-You can retrieve a version of an object using the `.history_id` and a
-version selector. A version selector is a string that specifies which
-available version to return. The value `mainline` tells the
-`IVersionControl` to return the most recent version on the main branch.
-
- >>> ob = repository.getVersionOfResource(info.history_id, 'mainline')
- >>> type(ob)
- <class 'zope.app.versioncontrol.README.Sample'>
- >>> ob is samp
- False
- >>> root["ob"] = ob
- >>> ob.__name__ = "ob"
- >>> ob_info = repository.getVersionInfo(ob)
- >>> ob_info.history_id == info.history_id
- True
- >>> ob_info is info
- False
-
-Once version control has been applied, the object can be "checked
-out", modified and "checked in" to create new versions. For many
-applications, this parallels form-based changes to objects, but this
-is a matter of policy.
-
-Let's save some information about the current version of the object so
-we can see that it changes:
-
- >>> orig_history_id = info.history_id
- >>> orig_version_id = info.version_id
-
-Now, let's check out the object and add an attribute:
-
- >>> repository.checkoutResource(ob)
- >>> ob.value = 42
- >>> repository.checkinResource(ob)
- >>> util.commit()
-
-We can now compare information about the updated version with the
-original information:
-
- >>> newinfo = repository.getVersionInfo(ob)
- >>> newinfo.history_id == orig_history_id
- True
- >>> newinfo.version_id != orig_version_id
- True
-
-Retrieving both versions of the object allows use to see the
-differences between the two:
-
- >>> o1 = repository.getVersionOfResource(orig_history_id,
- ... orig_version_id)
- >>> o2 = repository.getVersionOfResource(orig_history_id,
- ... newinfo.version_id)
- >>> o1.value
- Traceback (most recent call last):
- ...
- AttributeError: 'Sample' object has no attribute 'value'
- >>> o2.value
- 42
-
-We can determine whether an object that's been checked out is
-up-to-date with the most recent version from the repository:
-
- >>> repository.isResourceUpToDate(o1)
- False
- >>> repository.isResourceUpToDate(o2)
- True
-
-Asking whether a non-versioned object is up-to-date produces an error:
-
- >>> repository.isResourceUpToDate(42)
- Traceback (most recent call last):
- ...
- VersionControlError: Object is not under version control.
-
- >>> repository.isResourceUpToDate(samp2)
- Traceback (most recent call last):
- ...
- VersionControlError: Object is not under version control.
-
-It's also possible to check whether an object has been changed since
-it was checked out. Since we're only looking at changes that have
-been committed to the database, we'll start by making a change and
-committing it without checking a new version into the version control
-repository.
-
- >>> repository.updateResource(samp)
- >>> repository.checkoutResource(samp)
- >>> util.commit()
-
- >>> repository.isResourceChanged(samp)
- False
- >>> samp.value += 1
- >>> util.commit()
-
-We can now see that the object has been changed since it was last
-checked in::
-
- >>> repository.isResourceChanged(samp)
- True
-
-Checking in the object and commiting shows that we can now veryify
-that the object is considered up-to-date after a subsequent checkout.
-We'll also demonstrate that `checkinResource()` can take an optional
-message argument; we'll see later how this can be used.
-
- >>> repository.checkinResource(samp, 'sample checkin')
- >>> util.commit()
-
- >>> repository.checkoutResource(samp)
- >>> util.commit()
-
- >>> repository.isResourceUpToDate(samp)
- True
- >>> repository.isResourceChanged(samp)
- False
- >>> repository.getVersionInfo(samp).version_id
- '3'
-
-It's also possible to use version control to discard changes that
-haven't been checked in yet, even though they've been committed to the
-database for the "working copy". This is done using the
-`uncheckoutResource()` method of the `IVersionControl` object:
-
- >>> samp.value
- 43
- >>> samp.value += 2
- >>> samp.value
- 45
- >>> util.commit()
- >>> repository.isResourceChanged(samp)
- True
- >>> repository.uncheckoutResource(samp)
- >>> util.commit()
-
- >>> samp.value
- 43
- >>> repository.isResourceChanged(samp)
- False
- >>> version_id = repository.getVersionInfo(samp).version_id
- >>> version_id
- '3'
-
-An old copy of an object can be "updated" to the most recent version
-of an object:
-
- >>> ob = repository.getVersionOfResource(orig_history_id, orig_version_id)
- >>> ob.__name__ = "foo"
- >>> repository.isResourceUpToDate(ob)
- False
- >>> repository.getVersionInfo(ob).version_id
- '1'
- >>> repository.updateResource(ob, version_id)
- >>> repository.getVersionInfo(ob).version_id == version_id
- True
- >>> ob.value
- 43
-
-It's possible to get a list of all the versions of a particular object
-from the repository as well. We can use any copy of the object to
-make the request:
-
- >>> list(repository.getVersionIds(samp))
- ['1', '2', '3']
- >>> list(repository.getVersionIds(ob))
- ['1', '2', '3']
-
-No version information is available for objects that have not had
-version control applied::
-
- >>> repository.getVersionIds(samp2)
- Traceback (most recent call last):
- ...
- VersionControlError: Object is not under version control.
-
- >>> repository.getVersionIds(42)
- Traceback (most recent call last):
- ...
- VersionControlError: Object is not under version control.
-
-
-Naming specific revisions
--------------------------
-
-Similar to other version control systems, specific versions may be
-given symbolic names, and these names may be used to retrieve versions
-from the repository. This package calls these names *labels*; they
-are similar to *tags* in CVS.
-
-Labels can be assigned to objects that are checked into the
-repository:
-
- >>> repository.labelResource(samp, 'my-first-label')
- >>> repository.labelResource(samp, 'my-second-label')
-
-The list of labels assigned to some version of an object can be
-retrieved using the repository's `getLabelsForResource()` method::
-
- >>> list(repository.getLabelsForResource(samp))
- ['my-first-label', 'my-second-label']
-
-The labels can be retrieved using any object that refers to the same
-line of history in the repository:
-
- >>> list(repository.getLabelsForResource(ob))
- ['my-first-label', 'my-second-label']
-
-Labels can be used to retrieve specific versions of an object from the
-repository:
-
- >>> repository.getVersionInfo(samp).version_id
- '3'
- >>> ob = repository.getVersionOfResource(orig_history_id, 'my-first-label')
- >>> repository.getVersionInfo(ob).version_id
- '3'
-
-It's also possible to move a label from one version to another, but
-only when this is specifically indicated as allowed:
-
- >>> ob = repository.getVersionOfResource(orig_history_id, orig_version_id)
- >>> ob.__name__ = "bar"
- >>> repository.labelResource(ob, 'my-second-label')
- Traceback (most recent call last):
- ...
- VersionControlError: The label my-second-label is already associated with a version.
- >>> repository.labelResource(ob, 'my-second-label', force=True)
-
-Labels can also be used to update an object to a specific version:
-
- >>> repository.getVersionInfo(ob).version_id
- '1'
- >>> repository.updateResource(ob, 'my-first-label')
- >>> repository.getVersionInfo(ob).version_id
- '3'
- >>> ob.value
- 43
-
-
-Sticky settings
----------------
-
-Similar to CVS, this package supports a sort of "sticky" updating: if
-an object is updated to a specific date, determination of whether
-it is up-to-date or changed is based on the version it was updated to.
-
- >>> repository.updateResource(samp, orig_version_id)
- >>> util.commit()
-
- >>> samp.value
- Traceback (most recent call last):
- ...
- AttributeError: 'Sample' object has no attribute 'value'
-
- >>> repository.getVersionInfo(samp).version_id == orig_version_id
- True
- >>> repository.isResourceChanged(samp)
- False
- >>> repository.isResourceUpToDate(samp)
- False
-
-The `isResourceUpToDate()` method indicates whether
-`checkoutResource()` will succeed or raise an exception::
-
- >>> repository.checkoutResource(samp)
- Traceback (most recent call last):
- ...
- VersionControlError: The selected resource has been updated to a particular version, label or date. The resource must be updated to the mainline or a branch before it may be checked out.
-
-
-TODO: Figure out how to write date-based tests. Perhaps the
-repository should implement a hook used to get the current date so
-tests can hook that.
-
-
-Examining the change history
-----------------------------
-
- >>> actions = {
- ... interfaces.ACTION_CHECKIN: "Check in",
- ... interfaces.ACTION_CHECKOUT: "Check out",
- ... interfaces.ACTION_UNCHECKOUT: "Uncheckout",
- ... interfaces.ACTION_UPDATE: "Update",
- ... }
-
- >>> entries = repository.getLogEntries(samp)
- >>> for entry in entries:
- ... print "Action:", actions[entry.action]
- ... print "Version:", entry.version_id
- ... print "Path:", entry.path
- ... if entry.message:
- ... print "Message:", entry.message
- ... print "--"
- Action: Update
- Version: 1
- Path: /samp
- --
- Action: Update
- Version: 3
- Path: /bar
- --
- Action: Update
- Version: 3
- Path: /foo
- --
- Action: Uncheckout
- Version: 3
- Path: /samp
- --
- Action: Check out
- Version: 3
- Path: /samp
- --
- Action: Check in
- Version: 3
- Path: /samp
- Message: sample checkin
- --
- Action: Check out
- Version: 2
- Path: /samp
- --
- Action: Update
- Version: 2
- Path: /samp
- --
- Action: Check in
- Version: 2
- Path: /ob
- --
- Action: Check out
- Version: 1
- Path: /ob
- --
- Action: Check in
- Version: 1
- Path: /samp
- Message: Initial checkin.
- --
-
-Note that the entry with the checkin entry for version 3 includes the
-comment passed to `checkinResource()`.
-
-The version history also contains the principal id related to each
-entry::
-
- >>> entries[0].user_id
- 'bob'
-
-
-Branches
---------
-
-The implementation contains some support for branching, but it's not
-fully exposed in the interface at this time. It's too early to
-document at this time. Branches will interact heavily with
-"stickiness".
-
-
-Supporting separately versioned subobjects
-------------------------------------------
-
-`INonVersionedData` is responsible for dealing with parts of the object
-state that should *not* be versioned as part of this object. This can
-include both subobjects that are versioned independently as well as
-object-specific data that isn't part of the abstract resource the
-version control framework is supporting.
-
-For the sake of examples, let's create a simple class that actually
-implements these to interfaces. In this example, we'll create a
-simple object that excluses any versionable subobjects and any
-subobjects with names that start with "bob". Note that as for the
-`Sample` class above, we're still careful to consider the values for
-`__name__` and `__parent__` to be non-versioned:
-
- >>> def ignored_item(name, ob):
- ... """Return True for non-versioned items."""
- ... return (interfaces.IVersionable.providedBy(ob)
- ... or name.startswith("bob")
- ... or (name in ["__name__", "__parent__"]))
-
- >>> class SampleContainer(Sample):
- ...
- ... # Methods defined by INonVersionedData
- ... def listNonVersionedObjects(self):
- ... return [ob for (name, ob) in self.__dict__.items()
- ... if ignored_item(name, ob)
- ... ]
- ...
- ... def removeNonVersionedData(self):
- ... for name, value in self.__dict__.items():
- ... if ignored_item(name, value):
- ... del self.__dict__[name]
- ...
- ... def getNonVersionedData(self):
- ... return [(name, ob) for (name, ob) in self.__dict__.items()
- ... if ignored_item(name, ob)
- ... ]
- ...
- ... def restoreNonVersionedData(self, data):
- ... for name, value in data:
- ... if name not in self.__dict__:
- ... self.__dict__[name] = value
-
-Let's take a look at how the `INonVersionedData` interface is used.
-We'll start by creating an instance of our sample container and
-storing it in the database:
-
- >>> box = SampleContainer()
- >>> box.__name__ = "box"
- >>> root[box.__name__] = box
-
-We'll also add some contained objects:
-
- >>> box.aList = [1, 2, 3]
-
- >>> samp1 = Sample()
- >>> samp1.__name__ = "box/samp1"
- >>> samp1.__parent__ = box
- >>> box.samp1 = samp1
-
- >>> box.bob_list = [3, 2, 1]
-
- >>> bob_samp = Sample()
- >>> bob_samp.__name__ = "box/bob_samp"
- >>> bob_samp.__parent__ = box
- >>> box.bob_samp = bob_samp
-
- >>> util.commit()
-
-Let's apply version control to the container:
-
- >>> repository.applyVersionControl(box)
-
-We'll start by showing some basics of how the INonVersionedData
-interface is used.
-
-The `getNonVersionedData()`, `removeNonVersionedData()`, and
-`restoreNonVersionedData()` methods work together, allowing the
-version control framework to ensure that data that is not versioned as
-part of the object is not lost or inappropriately stored in the
-repository as part of version control operations.
-
-The basic pattern for this trio of operations is simple:
-
-1. Use `getNonVersionedData()` to get a value that can be used to
- restore the current non-versioned data of the object.
-
-2. Use `removeNonVersionedData()` to remove any non-versioned data
- from the object so it doesn't enter the repository as object state
- is copied around.
-
-3. Make object state changes based on the version control operation
- being performed.
-
-4. Use `restoreNonVersionedData()` to restore the data retrieved using
- `getNonVersionedData()`.
-
-This is fairly simple to see in an example. Step 1 is to save the
-non-versioned data:
-
- >>> saved = box.getNonVersionedData()
-
-While the version control framework treats this as an opaque value, we
-can take a closer look to make sure we got what we expected (since we
-know our implementation):
-
- >>> names = [name for (name, ob) in saved]
- >>> names.sort()
- >>> names
- ['__name__', 'bob_list', 'bob_samp', 'samp1']
-
-Step 2 is to remove the data from the object:
-
- >>> box.removeNonVersionedData()
-
-The non-versioned data should no longer be part of the object:
-
- >>> box.bob_samp
- Traceback (most recent call last):
- ...
- AttributeError: 'SampleContainer' object has no attribute 'bob_samp'
-
-While versioned data should remain present:
-
- >>> box.aList
- [1, 2, 3]
-
-At this point, the version control framework will perform any
-appropriate state copies are needed.
-
-Once that's done, `restoreNonVersionedData()` will be called with the
-saved data to perform the restore operation:
-
- >>> box.restoreNonVersionedData(saved)
-
-We can verify that the restoraion has been performed by checking the
-non-versioned data:
-
- >>> box.bob_list
- [3, 2, 1]
- >>> type(box.samp1)
- <class 'zope.app.versioncontrol.README.Sample'>
-
-We can see how this is affects object state by making some changes to
-the container object's versioned and non-versioned data and watching
-how those attributes are affected by updating to specific versions
-using `updateResource()` and retrieving specific versions using
-`getVersionOfResource()`. Let's start by generating some new
-revisions in the repository:
-
- >>> repository.checkoutResource(box)
- >>> util.commit()
- >>> version_id = repository.getVersionInfo(box).version_id
-
- >>> box.aList.append(4)
- >>> box.bob_list.append(0)
- >>> repository.checkinResource(box)
- >>> util.commit()
-
- >>> box.aList
- [1, 2, 3, 4]
- >>> box.bob_list
- [3, 2, 1, 0]
-
- >>> repository.updateResource(box, version_id)
- >>> box.aList
- [1, 2, 3]
- >>> box.bob_list
- [3, 2, 1, 0]
-
-The list-remaining method of the `INonVersionedData` interface is a
-little different, but remains very tightly tied to the details of the
-object's state. The `listNonVersionedObjects()` method should return
-a sequence of all the objects that should not be copied as part of the
-object's state. The difference between this method and
-`getNonVersionedData()` may seem simple, but is significant in
-practice.
-
-The `listNonVersionedObjects()` method allows the version control
-framework to identify data that should not be included in state
-copies, without saying anything else about the data. The
-`getNonVersionedData()` method allows the INonVersionedData
-implementation to communicate with itself (by providing data to be
-restored by the `restoreNonVersionedData()` method) without exposing
-any information about how it communicates with itself (it could store
-all the relevant data into an external file and use the value returned
-to locate the state file again, if that was needed for some reason).
More information about the Zope-CVS
mailing list