[Checkins] SVN: zope.locking/branches/patricks-fix/ add tests for generations module, clean up other tests
Patrick Strawderman
patrick at zope.com
Tue Mar 15 19:26:29 EDT 2011
Log message for revision 120970:
add tests for generations module, clean up other tests
Changed:
U zope.locking/branches/patricks-fix/buildout.cfg
U zope.locking/branches/patricks-fix/setup.py
U zope.locking/branches/patricks-fix/src/zope/locking/README.txt
U zope.locking/branches/patricks-fix/src/zope/locking/annoying.txt
U zope.locking/branches/patricks-fix/src/zope/locking/configure.zcml
U zope.locking/branches/patricks-fix/src/zope/locking/generations.py
A zope.locking/branches/patricks-fix/src/zope/locking/generations.txt
U zope.locking/branches/patricks-fix/src/zope/locking/tests.py
U zope.locking/branches/patricks-fix/src/zope/locking/utility.py
U zope.locking/branches/patricks-fix/src/zope/locking/utils.py
-=-
Modified: zope.locking/branches/patricks-fix/buildout.cfg
===================================================================
--- zope.locking/branches/patricks-fix/buildout.cfg 2011-03-15 23:06:08 UTC (rev 120969)
+++ zope.locking/branches/patricks-fix/buildout.cfg 2011-03-15 23:26:29 UTC (rev 120970)
@@ -1,4 +1,8 @@
[buildout]
+extends =
+ http://download.zope.org/zopetoolkit/index/1.1/zopeapp-versions.cfg
+ http://download.zope.org/zopetoolkit/index/1.1/ztk-versions.cfg
+
develop = .
parts = py test
@@ -6,7 +10,7 @@
[test]
recipe = zc.recipe.testrunner
-eggs = zope.locking
+eggs = zope.locking [test]
extra-paths = parts/zope3/src
defaults = "--tests-pattern [fn]?tests --exit-with-status".split()
Modified: zope.locking/branches/patricks-fix/setup.py
===================================================================
--- zope.locking/branches/patricks-fix/setup.py 2011-03-15 23:06:08 UTC (rev 120969)
+++ zope.locking/branches/patricks-fix/setup.py 2011-03-15 23:26:29 UTC (rev 120970)
@@ -28,6 +28,9 @@
'zope.security',
'zope.testing',
],
+ extras_require=dict(
+ test=["zope.site"],
+ ),
zip_safe = False,
description=open("README.txt").read(),
long_description=(
Modified: zope.locking/branches/patricks-fix/src/zope/locking/README.txt
===================================================================
--- zope.locking/branches/patricks-fix/src/zope/locking/README.txt 2011-03-15 23:06:08 UTC (rev 120969)
+++ zope.locking/branches/patricks-fix/src/zope/locking/README.txt 2011-03-15 23:26:29 UTC (rev 120970)
@@ -49,7 +49,7 @@
>>> util.register(lock)
Traceback (most recent call last):
...
- AttributeError: 'NoneType' object has no attribute 'add'
+ TypeError: ...
>>> conn.add(util)
@@ -151,17 +151,15 @@
same object.
>>> util.register(tokens.ExclusiveLock(demo, 'mary'))
- ... # doctest: +ELLIPSIS
+ ...
Traceback (most recent call last):
...
RegistrationError: ...
>>> util.register(tokens.SharedLock(demo, ('mary', 'jane')))
- ... # doctest: +ELLIPSIS
Traceback (most recent call last):
...
RegistrationError: ...
>>> util.register(tokens.Freeze(demo))
- ... # doctest: +ELLIPSIS
Traceback (most recent call last):
...
RegistrationError: ...
@@ -331,13 +329,9 @@
Now we'll hack our code to make it think that it is two hours later, and then
check and modify the remaining_duration attribute.
- >>> def hackNow():
- ... return (datetime.datetime.now(pytz.utc) +
- ... datetime.timedelta(hours=2))
- ...
>>> import zope.locking.utils
- >>> oldNow = zope.locking.utils.now
- >>> zope.locking.utils.now = hackNow # make code think it's 2 hours later
+ >>> now = zope.locking.utils.now()
+ >>> zope.locking.utils.set_now(now + datetime.timedelta(hours=2))
>>> lock.duration
datetime.timedelta(0, 14400)
>>> two >= lock.remaining_duration >= one
@@ -366,11 +360,7 @@
important to remember that a lock ending with a timeout ends silently--that
is, no event is fired.
- >>> def hackNow():
- ... return (
- ... datetime.datetime.now(pytz.utc) + datetime.timedelta(days=1))
- ...
- >>> zope.locking.utils.now = hackNow # make code think it is a day later
+ >>> zope.locking.utils.set_now(now + datetime.timedelta(days=1))
>>> lock.ended == lock.expiration
True
>>> util.get(demo) is None
@@ -394,7 +384,7 @@
We'll undo the hacks, and also end the lock (that is no longer ended once
the hack is finished).
- >>> zope.locking.utils.now = oldNow # undo the hack
+ >>> zope.locking.utils.reset()
>>> lock.end()
Make sure to register tokens. Creating a lock but not registering it puts it
@@ -406,44 +396,44 @@
>>> lock.duration = datetime.timedelta(1)
- >>> lock.started # doctest: +ELLIPSIS
+ >>> lock.started
Traceback (most recent call last):
...
UnregisteredError: ...
- >>> lock.ended # doctest: +ELLIPSIS
+ >>> lock.ended
Traceback (most recent call last):
...
UnregisteredError: ...
- >>> lock.expiration # doctest: +ELLIPSIS
+ >>> lock.expiration
Traceback (most recent call last):
...
UnregisteredError: ...
- >>> lock.remaining_duration # doctest: +ELLIPSIS
+ >>> lock.remaining_duration
Traceback (most recent call last):
...
UnregisteredError: ...
>>> t = datetime.datetime.utcnow().replace(tzinfo=pytz.UTC)
- >>> lock.expiration = t # doctest: +ELLIPSIS
+ >>> lock.expiration = t
Traceback (most recent call last):
...
UnregisteredError: ...
- >>> lock.remaining_duration = datetime.timedelta(1) # doctest: +ELLIPSIS
+ >>> lock.remaining_duration = datetime.timedelta(1)
Traceback (most recent call last):
...
UnregisteredError: ...
>>> lock = util.register(lock)
>>> lock.end()
- >>> lock.expiration = t # doctest: +ELLIPSIS
+ >>> lock.expiration = t
Traceback (most recent call last):
...
EndedError
- >>> lock.remaining_duration = datetime.timedelta(1) # doctest: +ELLIPSIS
+ >>> lock.remaining_duration = datetime.timedelta(1)
Traceback (most recent call last):
...
EndedError
@@ -585,17 +575,14 @@
>>> list(util) == [lock]
True
>>> util.register(tokens.ExclusiveLock(demo, 'mary'))
- ... # doctest: +ELLIPSIS
Traceback (most recent call last):
...
RegistrationError: ...
>>> util.register(tokens.SharedLock(demo, ('mary', 'jane')))
- ... # doctest: +ELLIPSIS
Traceback (most recent call last):
...
RegistrationError: ...
>>> util.register(tokens.Freeze(demo))
- ... # doctest: +ELLIPSIS
Traceback (most recent call last):
...
RegistrationError: ...
@@ -777,7 +764,6 @@
>>> token.end()
You can only specify principals that are in the current interaction.
-# doctest + ELLIPSIS
>>> token = broker.lock('joe')
>>> sorted(token.principal_ids)
@@ -1009,19 +995,19 @@
True
>>> lock = util.register(tokens.ExclusiveLock(demo, 'mary'))
>>> handler = interfaces.ITokenHandler(lock) # for joe's interaction still
- >>> handler.duration = two # doctest: +ELLIPSIS
+ >>> handler.duration = two
Traceback (most recent call last):
...
ParticipationError: ...
- >>> handler.expiration = handler.started + three # doctest: +ELLIPSIS
+ >>> handler.expiration = handler.started + three
Traceback (most recent call last):
...
ParticipationError: ...
- >>> handler.remaining_duration = two # doctest: +ELLIPSIS
+ >>> handler.remaining_duration = two
Traceback (most recent call last):
...
ParticipationError: ...
- >>> handler.release() # doctest: +ELLIPSIS
+ >>> handler.release()
Traceback (most recent call last):
...
ParticipationError: ...
@@ -1074,24 +1060,24 @@
>>> handler.release()
>>> sorted(handler.principal_ids)
['mary']
- >>> handler.duration = two # doctest: +ELLIPSIS
+ >>> handler.duration = two
Traceback (most recent call last):
...
ParticipationError: ...
- >>> handler.expiration = handler.started + three # doctest: +ELLIPSIS
+ >>> handler.expiration = handler.started + three
Traceback (most recent call last):
...
ParticipationError: ...
- >>> handler.remaining_duration = two # doctest: +ELLIPSIS
+ >>> handler.remaining_duration = two
Traceback (most recent call last):
...
ParticipationError: ...
- >>> handler.release() # doctest: +ELLIPSIS
+ >>> handler.release()
Traceback (most recent call last):
...
ParticipationError: ...
- >>> handler.release(("joe",)) # doctest: +ELLIPSIS
+ >>> handler.release(("joe",))
Traceback (most recent call last):
...
ParticipationError: ...
@@ -1116,7 +1102,7 @@
>>> sorted(handler.principal_ids)
['joe', 'mary', 'susan']
>>> handler.release()
- >>> handler.add('jake') # doctest: +ELLIPSIS
+ >>> handler.add('jake')
Traceback (most recent call last):
...
ParticipationError: ...
Modified: zope.locking/branches/patricks-fix/src/zope/locking/annoying.txt
===================================================================
--- zope.locking/branches/patricks-fix/src/zope/locking/annoying.txt 2011-03-15 23:06:08 UTC (rev 120969)
+++ zope.locking/branches/patricks-fix/src/zope/locking/annoying.txt 2011-03-15 23:26:29 UTC (rev 120970)
@@ -87,13 +87,9 @@
Now we'll hack our code to make it think that it is two hours later, and then
check and modify the remaining_duration attribute.
- >>> def hackNow():
- ... return (datetime.datetime.now(pytz.utc) +
- ... datetime.timedelta(hours=2))
- ...
>>> import zope.locking.utils
- >>> oldNow = zope.locking.utils.now
- >>> zope.locking.utils.now = hackNow # make code think it's 2 hours later
+ >>> now = zope.locking.utils.now()
+ >>> zope.locking.utils.set_now(now + datetime.timedelta(hours=2))
>>> lock.duration
datetime.timedelta(0, 14400)
>>> two >= lock.remaining_duration >= one
@@ -119,7 +115,7 @@
... return (
... datetime.datetime.now(pytz.utc) + datetime.timedelta(days=1))
...
- >>> zope.locking.utils.now = hackNow # make code think it is a day later
+ >>> zope.locking.utils.set_now(now + datetime.timedelta(days=1))
>>> lock.ended >= lock.started
True
>>> util.get(demo) is None
@@ -147,7 +143,7 @@
We'll undo the hacks, and also end the lock (that is no longer ended once
the hack is finished).
- >>> zope.locking.utils.now = oldNow # undo the hack
+ >>> zope.locking.utils.reset()
>>> lock.end()
--------------
@@ -209,17 +205,14 @@
object.
>>> util.register(tokens.ExclusiveLock(demo, 'mary'))
- ... # doctest: +ELLIPSIS
Traceback (most recent call last):
...
RegistrationError: ...
>>> util.register(tokens.SharedLock(demo, ('mary', 'jane')))
- ... # doctest: +ELLIPSIS
Traceback (most recent call last):
...
RegistrationError: ...
>>> util.register(tokens.EndableFreeze(demo))
- ... # doctest: +ELLIPSIS
Traceback (most recent call last):
...
RegistrationError: ...
@@ -384,32 +377,32 @@
...
NotImplementedError
- >>> token.duration = "" # doctest: +ELLIPSIS
+ >>> token.duration = ""
Traceback (most recent call last):
...
ValueError: duration must be datetime.timedelta
- >>> token.remaining_duration = "" # doctest: +ELLIPSIS
+ >>> token.remaining_duration = ""
Traceback (most recent call last):
...
ValueError: duration must be datetime.timedelta
- >>> token.duration = datetime.timedelta(-1) # doctest: +ELLIPSIS
+ >>> token.duration = datetime.timedelta(-1)
Traceback (most recent call last):
...
ValueError: duration may not be negative
- >>> token.remaining_duration = datetime.timedelta(-1) # doctest: +ELLIPSIS
+ >>> token.remaining_duration = datetime.timedelta(-1)
Traceback (most recent call last):
...
ValueError: duration may not be negative
- >>> token.expiration = "" # doctest: +ELLIPSIS
+ >>> token.expiration = ""
Traceback (most recent call last):
...
ValueError: expiration must be datetime.datetime
- >>> token.expiration = datetime.datetime.utcnow() # doctest: +ELLIPSIS
+ >>> token.expiration = datetime.datetime.utcnow()
Traceback (most recent call last):
...
ValueError: expiration must be timezone-aware
Modified: zope.locking/branches/patricks-fix/src/zope/locking/configure.zcml
===================================================================
--- zope.locking/branches/patricks-fix/src/zope/locking/configure.zcml 2011-03-15 23:06:08 UTC (rev 120969)
+++ zope.locking/branches/patricks-fix/src/zope/locking/configure.zcml 2011-03-15 23:26:29 UTC (rev 120970)
@@ -79,6 +79,8 @@
<include file="generations.zcml" />
+ <include package="zope.keyreference"/>
+
<configure
xmlns:zcml="http://namespaces.zope.org/zcml"
zcml:condition="have apidoc"
Modified: zope.locking/branches/patricks-fix/src/zope/locking/generations.py
===================================================================
--- zope.locking/branches/patricks-fix/src/zope/locking/generations.py 2011-03-15 23:06:08 UTC (rev 120969)
+++ zope.locking/branches/patricks-fix/src/zope/locking/generations.py 2011-03-15 23:26:29 UTC (rev 120970)
@@ -61,11 +61,10 @@
utility due to this issue.
"""
for pid in list(util._principal_ids):
- # iterForPrincipalId only returns non-ended locks, so we know
- # they're still good.
new_tree = BTrees.OOBTree.OOTreeSet()
- for token in util.iterForPrincipalId(pid):
- new_tree.add(zope.keyreference.interfaces.IKeyReference(token))
+ for token in util._principal_ids[pid]:
+ if not token.ended:
+ new_tree.add(zope.keyreference.interfaces.IKeyReference(token))
if new_tree:
util._principal_ids[pid] = new_tree
else:
Added: zope.locking/branches/patricks-fix/src/zope/locking/generations.txt
===================================================================
--- zope.locking/branches/patricks-fix/src/zope/locking/generations.txt (rev 0)
+++ zope.locking/branches/patricks-fix/src/zope/locking/generations.txt 2011-03-15 23:26:29 UTC (rev 120970)
@@ -0,0 +1,171 @@
+===========
+Generations
+===========
+
+The format of the internal data structures the token utility uses has
+changed a few times in 1.2.x due to bugs and other issues;
+therefore the zope.locking package includes a zope.generations schema
+manager to evolve the old data structures.
+
+Note that the schema manager is optional, and can be excluded by
+excluding `generations.zcml`. Token utilities can be evolved manually
+using the helper functions in zope.locking.generations that are
+tested below.
+
+IMPORTANT: If the token utility is not registered in any site manager,
+it will also need to be evolved manually.
+
+Set Up
+------
+
+ >>> import datetime
+ >>> import random
+ >>> import zope.keyreference.interfaces
+ >>> import zope.location.traversing
+ >>> import zope.locking.generations
+ >>> import zope.locking.tokens
+ >>> import zope.locking.utils
+ >>> import zope.locking.utility
+ >>> import zope.site.folder
+ >>> import zope.site.hooks
+ >>> import zope.site.testing
+
+ >>> import BTrees.check
+ >>> import BTrees.OOBTree
+
+ >>> root = conn.root()
+ >>> gsm = zope.component.getGlobalSiteManager()
+ >>> gsm.registerAdapter(
+ ... zope.location.traversing.LocationPhysicallyLocatable)
+
+To test the `get_site_managers` function used by the schema manager,
+we add a host of sites to the database.
+
+ >>> app = root["Application"] = zope.site.folder.rootFolder()
+ >>> root._p_jar.add(app)
+
+ >>> sms = []
+ >>> sms.append(zope.site.testing.createSiteManager(app))
+ >>> zope.site.hooks.setSite(app)
+ >>> app["site1"] = zope.site.folder.Folder()
+ >>> sms.append(zope.site.testing.createSiteManager(app["site1"]))
+ >>> app["site1a"] = zope.site.folder.Folder()
+ >>> sms.append(zope.site.testing.createSiteManager(app["site1a"]))
+ >>> app["site2"] = zope.site.folder.Folder()
+ >>> sms.append(zope.site.testing.createSiteManager(app["site2"]))
+
+`get_site_managers` returns all of the site managers for the sites we
+created.
+
+ >>> sms == list(zope.locking.generations.get_site_managers(app))
+ True
+
+ >>> app["site3"] = zope.site.folder.Folder()
+ >>> sms.append(zope.site.testing.createSiteManager(app["site3"]))
+
+ >>> sms == list(zope.locking.generations.get_site_managers(app))
+ True
+
+The `find_token_utilities` function returns all of the token utilities
+from all of the sites.
+
+ >>> list(zope.locking.generations.find_token_utilities(app))
+ []
+
+
+ >>> iface = zope.locking.interfaces.ITokenUtility
+ >>> sm = app["site1a"].getSiteManager()
+ >>> util = zope.locking.utility.TokenUtility()
+ >>> util2 = zope.locking.utility.TokenUtility()
+ >>> zope.site.testing.addUtility(sm, "", iface, util) and None
+ >>> zope.site.testing.addUtility(sm, "named", iface, util2) and None
+ >>> utils = list(zope.locking.generations.find_token_utilities(app))
+ >>> utils == [util, util2]
+ True
+
+ >>> sm = app["site3"].getSiteManager()
+ >>> util3 = zope.locking.utility.TokenUtility()
+ >>> zope.site.testing.addUtility(sm, "", iface, util3) and None
+ >>> utils = list(zope.locking.generations.find_token_utilities(app))
+ >>> utils == [util, util2, util3]
+ True
+
+ >>> app["util"] = zope.locking.utility.TokenUtility()
+
+app["util"] isn't registered in any site manager, so it isn't found.
+
+ >>> app["util"] in list(
+ ... zope.locking.generations.find_token_utilities(app))
+ False
+
+The `fix_token_utility` function converts legacy utilities to the new
+format. We'll add tokens to the utilities to emulate the legacy
+behavior of storing Tokens directly as keys in TreeSets (bad idea).
+
+ >>> principals = ("henry", "mario", "edwige",)
+ >>> now = zope.locking.utils.now()
+ >>> def add_bad_tokens(util):
+ ... _now = zope.locking.utils.now()
+ ... for i in range(10):
+ ... obj = Demo()
+ ... pid = random.choice(principals)
+ ... token = zope.locking.tokens.ExclusiveLock(obj, pid)
+ ... if pid not in util._principal_ids:
+ ... util._principal_ids[pid] = BTrees.OOBTree.TreeSet()
+ ... util._principal_ids[pid].insert(token)
+ ... key = zope.keyreference.interfaces.IKeyReference(obj)
+ ... expiration = _now + datetime.timedelta(minutes=i+1)
+ ... util._locks[key] = (token, frozenset([pid]), expiration)
+ ... token.utility = util
+ ... token.expiration = expiration
+ ... if expiration not in util._expirations:
+ ... util._expirations[expiration] = BTrees.OOBTree.TreeSet()
+ ... util._expirations[expiration].insert(token)
+ ... util._principal_ids["louis"] = BTrees.OOBTree.TreeSet()
+
+ >>> add_bad_tokens(util)
+
+ >>> now = zope.locking.utils.now()
+
+Let's travel 5 minutes into the future, and call the fixer; it will only
+leave non-expired tokens in the utility.
+
+ >>> zope.locking.utils.set_now(now + datetime.timedelta(minutes=5))
+ >>> zope.locking.generations.fix_token_utility(util)
+ >>> len(util._locks)
+ 5
+
+ >>> "louis" in util._principal_ids
+ False
+
+ >>> def check_util(util):
+ ... for tree in util._principal_ids.values():
+ ... BTrees.check.check(tree)
+ ... for keyref in tree:
+ ... if not (
+ ... zope.keyreference.interfaces.IKeyReference.providedBy(keyref)):
+ ... raise Exception(
+ ... "Expected keyreference, got: %r" % (keyref,))
+
+ >>> check_util(util)
+
+ >>> util.__init__() # reset
+
+ >>> add_bad_tokens(util2)
+ >>> add_bad_tokens(util3)
+ >>> check_util(util2)
+ Traceback (most recent call last):
+ ...
+ Exception: Expected keyreference, got: ...
+
+The `clean_locks` function is used by the schema manager to find all
+token utilities and run `fix_token_utility` on them.
+
+ >>> class Context(object):
+ ... pass
+
+ >>> context = Context()
+ >>> context.connection = conn
+ >>> zope.locking.generations.clean_locks(context)
+ >>> for util in (util2, util3):
+ ... check_util(util)
Property changes on: zope.locking/branches/patricks-fix/src/zope/locking/generations.txt
___________________________________________________________________
Added: svn:eol-style
+ native
Modified: zope.locking/branches/patricks-fix/src/zope/locking/tests.py
===================================================================
--- zope.locking/branches/patricks-fix/src/zope/locking/tests.py 2011-03-15 23:06:08 UTC (rev 120969)
+++ zope.locking/branches/patricks-fix/src/zope/locking/tests.py 2011-03-15 23:26:29 UTC (rev 120970)
@@ -11,11 +11,13 @@
import zope.event
import zope.locking.testing
+import zope.site.testing
from zope.testing import doctest
def setUp(test):
+ zope.site.testing.siteSetUp()
zope.app.testing.placelesssetup.setUp(test)
db = test.globs['db'] = ZODB.DB(ZODB.MappingStorage.MappingStorage())
test.globs['conn'] = db.open()
@@ -25,10 +27,14 @@
zope.app.keyreference.persistent.KeyReferenceToPersistent,
[persistent.interfaces.IPersistent],
zope.app.keyreference.interfaces.IKeyReference)
+ zope.component.provideAdapter(
+ zope.app.keyreference.persistent.connectionOfPersistent,
+ [persistent.interfaces.IPersistent])
events = test.globs['events'] = []
zope.event.subscribers.append(events.append)
def tearDown(test):
+ zope.site.testing.siteTearDown()
zope.app.testing.placelesssetup.tearDown(test)
transaction.abort()
test.globs['conn'].close()
@@ -38,17 +44,16 @@
del events[:] # being paranoid
def test_suite():
- return unittest.TestSuite((
+ optionflags = doctest.ELLIPSIS
+ return unittest.TestSuite(
doctest.DocFileSuite(
'README.txt',
- setUp=setUp, tearDown=tearDown),
- doctest.DocFileSuite(
'annoying.txt',
- setUp=setUp, tearDown=tearDown),
- doctest.DocFileSuite(
'cleanup.txt',
+ 'generations.txt',
+ optionflags=optionflags,
setUp=setUp, tearDown=tearDown),
- ))
+ )
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')
Modified: zope.locking/branches/patricks-fix/src/zope/locking/utility.py
===================================================================
--- zope.locking/branches/patricks-fix/src/zope/locking/utility.py 2011-03-15 23:06:08 UTC (rev 120969)
+++ zope.locking/branches/patricks-fix/src/zope/locking/utility.py 2011-03-15 23:26:29 UTC (rev 120970)
@@ -1,5 +1,6 @@
import persistent
import persistent.interfaces
+import ZODB.interfaces
from BTrees.OOBTree import OOBTree, OOTreeSet
from zope import interface, component, event
@@ -54,8 +55,10 @@
token.utility = self
elif token.utility is not self:
raise ValueError('Lock is already registered with another utility')
- if persistent.interfaces.IPersistent.providedBy(token):
- self._p_jar.add(token)
+ if (persistent.interfaces.IPersistent.providedBy(token) and
+ not getattr(token, "_p_jar", None)):
+ conn = ZODB.interfaces.IConnection(self)
+ conn.add(token)
key_ref = IKeyReference(token.context)
current = self._locks.get(key_ref)
if current is not None:
Modified: zope.locking/branches/patricks-fix/src/zope/locking/utils.py
===================================================================
--- zope.locking/branches/patricks-fix/src/zope/locking/utils.py 2011-03-15 23:06:08 UTC (rev 120969)
+++ zope.locking/branches/patricks-fix/src/zope/locking/utils.py 2011-03-15 23:26:29 UTC (rev 120970)
@@ -2,7 +2,27 @@
import datetime
import pytz
-# this is a small convenience, but is more important as a convenient monkey-
-# patch opportunity for the package's README.txt doctest.
+# this is a small convenience, but is more important as a
+# convenient monkey-patch opportunity for the package's doctests.
+
+_now = None
+
def now():
+ if _now is not None:
+ return _now
return datetime.datetime.now(pytz.utc)
+
+def set_now(dt):
+ global _now
+ _now = dt
+
+def reset():
+ global _now
+ _now = None
+
+try:
+ import zope.testing.cleanup
+except ImportError:
+ pass
+else:
+ zope.testing.cleanup.addCleanUp(reset)
More information about the checkins
mailing list