1 # Copyright (C) 2011-2014 by the Free Software Foundation, Inc.
3 # This file is part of GNU Mailman.
5 # GNU Mailman is free software: you can redistribute it and/or modify it under
6 # the terms of the GNU General Public License as published by the Free
7 # Software Foundation, either version 3 of the License, or (at your option)
10 # GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 # You should have received a copy of the GNU General Public License along with
16 # GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
18 """Unique ID generation.
20 Use these functions to create unique ids rather than inlining calls to hashlib
21 and whatnot. These are better instrumented for testing purposes.
24 from __future__
import absolute_import
, unicode_literals
37 from flufl
.lock
import Lock
39 from mailman
.config
import config
40 from mailman
.model
.uid
import UID
41 from mailman
.testing
import layers
45 class UniqueIDFactory
:
46 """A factory for unique ids."""
48 def __init__(self
, context
=None):
49 # We can't call reset() when the factory is created below, because
50 # config.VAR_DIR will not be set at that time. So initialize it at
53 self
._lock
_file
= None
55 self
._context
= context
56 layers
.MockAndMonkeyLayer
.register_reset(self
.reset
)
60 if self
._lockobj
is None:
61 # These will get automatically cleaned up by the test
63 self
._uid
_file
= os
.path
.join(config
.VAR_DIR
, '.uid')
65 self
._uid
_file
+= '.' + self
._context
66 self
._lock
_file
= self
._uid
_file
+ '.lock'
67 self
._lockobj
= Lock(self
._lock
_file
)
76 if layers
.is_testing():
77 # When in testing mode we want to produce predictable id, but we
78 # need to coordinate this among separate processes. We could use
79 # the database, but I don't want to add schema just to handle this
80 # case, and besides transactions could get aborted, causing some
81 # ids to be recycled. So we'll use a data file with a lock. This
82 # may still not be ideal due to race conditions, but I think the
83 # tests will be serialized enough (and the ids reset between
84 # tests) that it will not be a problem. Maybe.
85 return self
._next
_uid
()
98 with
open(self
._uid
_file
) as fp
:
99 uid
= int(fp
.read().strip())
101 with
open(self
._uid
_file
, 'w') as fp
:
102 fp
.write(str(next_uid
))
103 return uuid
.UUID(int=uid
)
104 except IOError as error
:
105 if error
.errno
!= errno
.ENOENT
:
107 with
open(self
._uid
_file
, 'w') as fp
:
109 return uuid
.UUID(int=1)
113 with
open(self
._uid
_file
, 'w') as fp
:
118 factory
= UniqueIDFactory()