1.1 --- a/imip_store.py Sun Sep 06 23:20:02 2015 +0200
1.2 +++ b/imip_store.py Mon Sep 07 18:54:54 2015 +0200
1.3 @@ -64,19 +64,32 @@
1.4 value.
1.5 """
1.6
1.7 + f = codecs.open(filename, "rb", encoding="utf-8")
1.8 + try:
1.9 + l = []
1.10 + for line in f.readlines():
1.11 + t = line.strip(" \r\n").split("\t")
1.12 + if empty_defaults:
1.13 + t = self._set_defaults(t, empty_defaults)
1.14 + l.append(tuple(t))
1.15 + return l
1.16 + finally:
1.17 + f.close()
1.18 +
1.19 + def _get_table_atomic(self, user, filename, empty_defaults=None):
1.20 +
1.21 + """
1.22 + From the file for the given 'user' having the given 'filename', return
1.23 + a list of tuples representing the file's contents.
1.24 +
1.25 + The 'empty_defaults' is a list of (index, value) tuples indicating the
1.26 + default value where a column either does not exist or provides an empty
1.27 + value.
1.28 + """
1.29 +
1.30 self.acquire_lock(user)
1.31 try:
1.32 - f = codecs.open(filename, "rb", encoding="utf-8")
1.33 - try:
1.34 - l = []
1.35 - for line in f.readlines():
1.36 - t = line.strip(" \r\n").split("\t")
1.37 - if empty_defaults:
1.38 - t = self._set_defaults(t, empty_defaults)
1.39 - l.append(tuple(t))
1.40 - return l
1.41 - finally:
1.42 - f.close()
1.43 + return self._get_table(user, filename, empty_defaults)
1.44 finally:
1.45 self.release_lock(user)
1.46
1.47 @@ -91,17 +104,30 @@
1.48 value.
1.49 """
1.50
1.51 + f = codecs.open(filename, "wb", encoding="utf-8")
1.52 + try:
1.53 + for item in items:
1.54 + if empty_defaults:
1.55 + item = self._set_defaults(list(item), empty_defaults)
1.56 + f.write("\t".join(item) + "\n")
1.57 + finally:
1.58 + f.close()
1.59 + fix_permissions(filename)
1.60 +
1.61 + def _set_table_atomic(self, user, filename, items, empty_defaults=None):
1.62 +
1.63 + """
1.64 + For the given 'user', write to the file having the given 'filename' the
1.65 + 'items'.
1.66 +
1.67 + The 'empty_defaults' is a list of (index, value) tuples indicating the
1.68 + default value where a column either does not exist or provides an empty
1.69 + value.
1.70 + """
1.71 +
1.72 self.acquire_lock(user)
1.73 try:
1.74 - f = codecs.open(filename, "wb", encoding="utf-8")
1.75 - try:
1.76 - for item in items:
1.77 - if empty_defaults:
1.78 - item = self._set_defaults(list(item), empty_defaults)
1.79 - f.write("\t".join(item) + "\n")
1.80 - finally:
1.81 - f.close()
1.82 - fix_permissions(filename)
1.83 + self._set_table(user, filename, items, empty_defaults)
1.84 finally:
1.85 self.release_lock(user)
1.86
1.87 @@ -420,7 +446,7 @@
1.88 # Attempt to read providers, with a declaration of the datetime
1.89 # from which such providers are considered as still being active.
1.90
1.91 - t = self._get_table(user, filename, [(1, None)])
1.92 + t = self._get_table_atomic(user, filename, [(1, None)])
1.93 try:
1.94 dt_string = t[0][0]
1.95 except IndexError:
1.96 @@ -469,7 +495,7 @@
1.97 return False
1.98
1.99 t.insert(0, (dt_string,))
1.100 - self._set_table(user, filename, t, [(1, "")])
1.101 + self._set_table_atomic(user, filename, t, [(1, "")])
1.102 return True
1.103
1.104 def set_freebusy_providers(self, user, dt, providers):
1.105 @@ -517,17 +543,28 @@
1.106
1.107 # Free/busy period access.
1.108
1.109 - def get_freebusy(self, user):
1.110 + def get_freebusy(self, user, name=None, get_table=None):
1.111
1.112 "Get free/busy details for the given 'user'."
1.113
1.114 - filename = self.get_object_in_store(user, "freebusy")
1.115 + filename = self.get_object_in_store(user, name or "freebusy")
1.116 if not filename or not exists(filename):
1.117 return []
1.118 else:
1.119 - return map(lambda t: FreeBusyPeriod(*t), self._get_table(user, filename, [(4, None)]))
1.120 + return map(lambda t: FreeBusyPeriod(*t),
1.121 + (get_table or self._get_table_atomic)(user, filename, [(4, None)]))
1.122 +
1.123 + def get_freebusy_for_update(self, user, name=None):
1.124
1.125 - def get_freebusy_for_other(self, user, other):
1.126 + """
1.127 + Get free/busy details for the given 'user', locking the table. Dependent
1.128 + code must release this lock regardless of it completing successfully.
1.129 + """
1.130 +
1.131 + self.acquire_lock(user)
1.132 + return self.get_freebusy(user, name, self._get_table)
1.133 +
1.134 + def get_freebusy_for_other(self, user, other, get_table=None):
1.135
1.136 "For the given 'user', get free/busy details for the 'other' user."
1.137
1.138 @@ -535,20 +572,39 @@
1.139 if not filename or not exists(filename):
1.140 return []
1.141 else:
1.142 - return map(lambda t: FreeBusyPeriod(*t), self._get_table(user, filename, [(4, None)]))
1.143 + return map(lambda t: FreeBusyPeriod(*t),
1.144 + (get_table or self._get_table_atomic)(user, filename, [(4, None)]))
1.145 +
1.146 + def get_freebusy_for_other_for_update(self, user, other):
1.147
1.148 - def set_freebusy(self, user, freebusy):
1.149 + """
1.150 + For the given 'user', get free/busy details for the 'other' user,
1.151 + locking the table. Dependent code must release this lock regardless of
1.152 + it completing successfully.
1.153 + """
1.154 +
1.155 + self.acquire_lock(user)
1.156 + return self.get_freebusy_for_other(user, other, self._get_table)
1.157 +
1.158 + def set_freebusy(self, user, freebusy, name=None, set_table=None):
1.159
1.160 "For the given 'user', set 'freebusy' details."
1.161
1.162 - filename = self.get_object_in_store(user, "freebusy")
1.163 + filename = self.get_object_in_store(user, name or "freebusy")
1.164 if not filename:
1.165 return False
1.166
1.167 - self._set_table(user, filename, map(lambda fb: fb.as_tuple(strings_only=True), freebusy))
1.168 + (set_table or self._set_table_atomic)(user, filename,
1.169 + map(lambda fb: fb.as_tuple(strings_only=True), freebusy))
1.170 return True
1.171
1.172 - def set_freebusy_for_other(self, user, freebusy, other):
1.173 + def set_freebusy_in_update(self, user, freebusy, name=None):
1.174 +
1.175 + "For the given 'user', set 'freebusy' details during a compound update."
1.176 +
1.177 + return self.set_freebusy(user, freebusy, name, self._set_table)
1.178 +
1.179 + def set_freebusy_for_other(self, user, freebusy, other, set_table=None):
1.180
1.181 "For the given 'user', set 'freebusy' details for the 'other' user."
1.182
1.183 @@ -556,9 +612,23 @@
1.184 if not filename:
1.185 return False
1.186
1.187 - self._set_table(user, filename, map(lambda fb: fb.as_tuple(strings_only=True), freebusy))
1.188 + (set_table or self._set_table_atomic)(user, filename,
1.189 + map(lambda fb: fb.as_tuple(strings_only=True), freebusy))
1.190 return True
1.191
1.192 + def set_freebusy_for_other_in_update(self, user, freebusy, other):
1.193 +
1.194 + """
1.195 + For the given 'user', set 'freebusy' details for the 'other' user during
1.196 + a compound update.
1.197 + """
1.198 +
1.199 + return self.set_freebusy_for_other(user, freebusy, other, self._set_table)
1.200 +
1.201 + # Release methods.
1.202 +
1.203 + release_freebusy = release_lock
1.204 +
1.205 # Object status details access.
1.206
1.207 def _get_requests(self, user, queue):
1.208 @@ -569,7 +639,7 @@
1.209 if not filename or not exists(filename):
1.210 return None
1.211
1.212 - return self._get_table(user, filename, [(1, None)])
1.213 + return self._get_table_atomic(user, filename, [(1, None)])
1.214
1.215 def get_requests(self, user):
1.216