diff options
| author | Barry Warsaw | 2011-04-04 14:32:17 -0400 |
|---|---|---|
| committer | Barry Warsaw | 2011-04-04 14:32:17 -0400 |
| commit | 17887e4d1e56915647e1d395e18573db2b9ea3ba (patch) | |
| tree | 8dc9f275e878539040ee25857593570aca1ec113 /src/mailman/utilities/uid.py | |
| parent | cd18f50624f33a9556492168933d5fe088d51a04 (diff) | |
| download | mailman-17887e4d1e56915647e1d395e18573db2b9ea3ba.tar.gz mailman-17887e4d1e56915647e1d395e18573db2b9ea3ba.tar.zst mailman-17887e4d1e56915647e1d395e18573db2b9ea3ba.zip | |
Diffstat (limited to 'src/mailman/utilities/uid.py')
| -rw-r--r-- | src/mailman/utilities/uid.py | 58 |
1 files changed, 48 insertions, 10 deletions
diff --git a/src/mailman/utilities/uid.py b/src/mailman/utilities/uid.py index cc2cf3b12..6fceeb606 100644 --- a/src/mailman/utilities/uid.py +++ b/src/mailman/utilities/uid.py @@ -32,9 +32,13 @@ __all__ = [ import os import time +import errno import hashlib -from mailman.testing.layers import MockAndMonkeyLayer +from flufl.lock import Lock + +from mailman.config import config +from mailman.testing import layers from mailman.utilities.passwords import SALT_LENGTH @@ -42,14 +46,31 @@ from mailman.utilities.passwords import SALT_LENGTH class UniqueIDFactory: """A factory for unique ids.""" - # The predictable id. - predictable_id = None + def __init__(self): + # We can't call reset() when the factory is created below, because + # config.VAR_DIR will not be set at that time. So initialize it at + # the first use. + self._uid_file = None + self._lock_file = None + self._lock = None def new_uid(self, bytes=None): - if MockAndMonkeyLayer.testing_mode: - uid = self.predictable_id - self.predictable_id += 1 - return unicode(uid) + if layers.is_testing(): + if self._lock is None: + # These will get automatically cleaned up by the test + # infrastructure. + self._uid_file = os.path.join(config.VAR_DIR, '.uid') + self._lock_file = self._uid_file + '.lock' + self._lock = Lock(self._lock_file) + # When in testing mode we want to produce predictable id, but we + # need to coordinate this among separate processes. We could use + # the database, but I don't want to add schema just to handle this + # case, and besides transactions could get aborted, causing some + # ids to be recycled. So we'll use a data file with a lock. This + # may still not be ideal due to race conditions, but I think the + # tests will be serialized enough (and the ids reset between + # tests) that it will not be a problem. Maybe. + return self._next_uid() salt = os.urandom(SALT_LENGTH) h = hashlib.sha1(repr(time.time())) h.update(salt) @@ -57,11 +78,28 @@ class UniqueIDFactory: h.update(bytes) return unicode(h.hexdigest(), 'us-ascii') + def _next_uid(self): + with self._lock: + try: + with open(self._uid_file) as fp: + uid = fp.read().strip() + next_uid = int(uid) + 1 + with open(self._uid_file, 'w') as fp: + fp.write(str(next_uid)) + except IOError as error: + if error.errno != errno.ENOENT: + raise + with open(self._uid_file, 'w') as fp: + fp.write('2') + return '1' + return unicode(uid, 'us-ascii') + def reset(self): - self.predictable_id = 1 + with self._lock: + with open(self._uid_file, 'w') as fp: + fp.write('1') factory = UniqueIDFactory() -factory.reset() -MockAndMonkeyLayer.register_reset(factory.reset) +layers.MockAndMonkeyLayer.register_reset(factory.reset) |
