Package trac :: Package util :: Module compat

Source Code for Module trac.util.compat

  1  # -*- coding: utf-8 -*- 
  2  # 
  3  # Copyright (C) 2006-2020 Edgewall Software 
  4  # Copyright (C) 2006 Matthew Good <[email protected]> 
  5  # Copyright (C) 2006 Christopher Lenz <[email protected]> 
  6  # All rights reserved. 
  7  # 
  8  # This software is licensed as described in the file COPYING, which 
  9  # you should have received as part of this distribution. The terms 
 10  # are also available at https://trac.edgewall.org/wiki/TracLicense. 
 11  # 
 12  # This software consists of voluntary contributions made by many 
 13  # individuals. For the exact contribution history, see the revision 
 14  # history and logs, available at https://trac.edgewall.org/log/. 
 15   
 16  """Various classes and functions to provide some backwards-compatibility with 
 17  previous versions of Python from 2.6 onward. 
 18  """ 
 19   
 20  import errno 
 21  import math 
 22  import os 
 23  import time 
 24   
 25  from trac.util.text import cleandoc 
 26   
 27  # Windows doesn't have a crypt module by default. 
 28  try: 
 29      from crypt import crypt 
 30  except ImportError: 
 31      try: 
 32          from passlib.hash import des_crypt 
 33      except ImportError: 
 34          crypt = None 
 35      else: 
36 - def crypt(secret, salt):
37 # encrypt method deprecated in favor of hash in passlib 1.7 38 if hasattr(des_crypt, 'hash'): 39 return des_crypt.using(salt=salt).hash(secret) 40 else: 41 return des_crypt.encrypt(secret, salt=salt)
42 43 # Import symbols previously defined here, kept around so that plugins importing 44 # them don't suddenly stop working 45 all = all 46 any = any 47 frozenset = frozenset 48 reversed = reversed 49 set = set 50 sorted = sorted 51 from functools import partial 52 from hashlib import md5, sha1 53 from itertools import groupby, tee
54 55 56 -class py_groupby(object):
57 """Use in templates as an alternative to `itertools.groupby`, 58 which leaks memory for Python < 2.5.3. 59 60 This class will be removed in Trac 1.3.1. 61 """
62 - def __init__(self, iterable, key=None):
63 if key is None: 64 key = lambda x: x 65 self.keyfunc = key 66 self.it = iter(iterable) 67 self.tgtkey = self.currkey = self.currvalue = xrange(0)
68 - def __iter__(self):
69 return self
70 - def next(self):
71 while self.currkey == self.tgtkey: 72 self.currvalue = self.it.next() # Exit on StopIteration 73 self.currkey = self.keyfunc(self.currvalue) 74 self.tgtkey = self.currkey 75 return self.currkey, self._grouper(self.tgtkey)
76 - def _grouper(self, tgtkey):
77 while self.currkey == tgtkey: 78 yield self.currvalue 79 self.currvalue = self.it.next() # Exit on StopIteration 80 self.currkey = self.keyfunc(self.currvalue)
81
82 -def rpartition(s, sep):
83 return s.rpartition(sep)
84 85 # An error is raised by subprocess if we ever pass close_fds=True on Windows. 86 # We want it to be True on all other platforms to not leak file descriptors. 87 close_fds = os.name != 'nt'
88 89 90 -def wait_for_file_mtime_change(filename):
91 """This function is typically called before a file save operation, 92 waiting if necessary for the file modification time to change. The 93 purpose is to avoid successive file updates going undetected by the 94 caching mechanism that depends on a change in the file modification 95 time to know when the file should be reparsed.""" 96 97 from trac.util import touch_file 98 try: 99 mtime = os.stat(filename).st_mtime 100 touch_file(filename) 101 while mtime == os.stat(filename).st_mtime: 102 time.sleep(1e-3) 103 touch_file(filename) 104 except OSError as e: 105 if e.errno == errno.ENOENT: 106 pass 107 else: 108 raise
109 110 111 try: 112 from collections import OrderedDict 113 except ImportError: 114 115 try: 116 from thread import get_ident as _get_ident 117 except ImportError: 118 from dummy_thread import get_ident as _get_ident
119 120 - class OrderedDict(dict):
121 'Dictionary that remembers insertion order' 122 # An inherited dict maps keys to values. 123 # The inherited dict provides __getitem__, __len__, 124 # __contains__, and get. 125 # The remaining methods are order-aware. 126 # Big-O running times for all methods are the same as for 127 # regular dictionaries. 128 129 # The internal self.__map dictionary maps keys to links in a 130 # doubly linked list. 131 # The circular doubly linked list starts and ends with a 132 # sentinel element. 133 # The sentinel element never gets deleted (this simplifies 134 # the algorithm). 135 # Each link is stored as a list of length three: 136 # [PREV, NEXT, KEY]. 137
138 - def __init__(self, *args, **kwds):
139 """Initialize an ordered dictionary. Signature is the same 140 as for regular dictionaries, but keyword arguments are not 141 recommended because their insertion order is arbitrary. 142 """ 143 if len(args) > 1: 144 raise TypeError('expected at most 1 arguments, got %d' 145 % len(args)) 146 try: 147 self.__root 148 except AttributeError: 149 self.__root = root = [] # sentinel node 150 root[:] = [root, root, None] 151 self.__map = {} 152 self.__update(*args, **kwds)
153
154 - def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
155 'od.__setitem__(i, y) <==> od[i]=y' 156 # Setting a new item creates a new link which goes at the 157 # end of the linked 158 # list, and the inherited dictionary is updated with the 159 # new key/value pair. 160 if key not in self: 161 root = self.__root 162 last = root[0] 163 last[1] = root[0] = self.__map[key] = [last, root, key] 164 dict_setitem(self, key, value)
165
166 - def __delitem__(self, key, dict_delitem=dict.__delitem__):
167 """od.__delitem__(y) <==> del od[y]""" 168 # Deleting an existing item uses self.__map to find the 169 # link which is then removed by updating the links in the 170 # predecessor and successor nodes. 171 dict_delitem(self, key) 172 link_prev, link_next, key = self.__map.pop(key) 173 link_prev[1] = link_next 174 link_next[0] = link_prev
175
176 - def __iter__(self):
177 """od.__iter__() <==> iter(od)""" 178 root = self.__root 179 curr = root[1] 180 while curr is not root: 181 yield curr[2] 182 curr = curr[1]
183
184 - def __reversed__(self):
185 """od.__reversed__() <==> reversed(od)""" 186 root = self.__root 187 curr = root[0] 188 while curr is not root: 189 yield curr[2] 190 curr = curr[0]
191
192 - def clear(self):
193 """od.clear() -> None. Remove all items from od.""" 194 try: 195 for node in self.__map.itervalues(): 196 del node[:] 197 root = self.__root 198 root[:] = [root, root, None] 199 self.__map.clear() 200 except AttributeError: 201 pass 202 dict.clear(self)
203
204 - def popitem(self, last=True):
205 """od.popitem() -> (k, v), return and remove a (key, value) 206 pair. Pairs are returned in LIFO order if last is true or 207 FIFO order if false. 208 """ 209 if not self: 210 raise KeyError('dictionary is empty') 211 root = self.__root 212 if last: 213 link = root[0] 214 link_prev = link[0] 215 link_prev[1] = root 216 root[0] = link_prev 217 else: 218 link = root[1] 219 link_next = link[1] 220 root[1] = link_next 221 link_next[0] = root 222 key = link[2] 223 del self.__map[key] 224 value = dict.pop(self, key) 225 return key, value
226 227 # -- the following methods do not depend on the internal structure -- 228
229 - def keys(self):
230 """od.keys() -> list of keys in od""" 231 return list(self)
232
233 - def values(self):
234 """od.values() -> list of values in od""" 235 return [self[key] for key in self]
236
237 - def items(self):
238 """od.items() -> list of (key, value) pairs in od""" 239 return [(key, self[key]) for key in self]
240
241 - def iterkeys(self):
242 """od.iterkeys() -> an iterator over the keys in od""" 243 return iter(self)
244
245 - def itervalues(self):
246 """od.itervalues -> an iterator over the values in od""" 247 for k in self: 248 yield self[k]
249
250 - def iteritems(self):
251 """od.iteritems -> an iterator over the (key, value) items 252 in od 253 """ 254 for k in self: 255 yield (k, self[k])
256
257 - def update(*args, **kwds):
258 """od.update(E, **F) -> None. Update od from dict/iterable 259 E and F. 260 261 If E is a dict instance, does: 262 for k in E: od[k] = E[k] 263 If E has a .keys() method, does: 264 for k in E.keys(): od[k] = E[k] 265 Or if E is an iterable of items, does: 266 for k, v in E: od[k] = v 267 In either case, this is followed by: 268 for k, v in F.items(): od[k] = v 269 """ 270 if len(args) > 2: 271 raise TypeError('update() takes at most 2 positional ' 272 'arguments (%d given)' % (len(args),)) 273 elif not args: 274 raise TypeError('update() takes at least 1 argument (0 given)') 275 self = args[0] 276 # Make progressively weaker assumptions about "other" 277 other = () 278 if len(args) == 2: 279 other = args[1] 280 if isinstance(other, dict): 281 for key in other: 282 self[key] = other[key] 283 elif hasattr(other, 'keys'): 284 for key in other.keys(): 285 self[key] = other[key] 286 else: 287 for key, value in other: 288 self[key] = value 289 for key, value in kwds.items(): 290 self[key] = value
291 292 # let subclasses override update without breaking __init__ 293 __update = update 294 295 __marker = object() 296
297 - def pop(self, key, default=__marker):
298 """od.pop(k[,d]) -> v, remove specified key and return the 299 corresponding value. If key is not found, d is returned if 300 given, otherwise KeyError is raised. 301 """ 302 if key in self: 303 result = self[key] 304 del self[key] 305 return result 306 if default is self.__marker: 307 raise KeyError(key) 308 return default
309
310 - def setdefault(self, key, default=None):
311 """od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if 312 k not in od 313 """ 314 if key in self: 315 return self[key] 316 self[key] = default 317 return default
318
319 - def __repr__(self, _repr_running={}):
320 """od.__repr__() <==> repr(od)""" 321 call_key = id(self), _get_ident() 322 if call_key in _repr_running: 323 return '...' 324 _repr_running[call_key] = 1 325 try: 326 if not self: 327 return '%s()' % (self.__class__.__name__,) 328 return '%s(%r)' % (self.__class__.__name__, self.items()) 329 finally: 330 del _repr_running[call_key]
331
332 - def __reduce__(self):
333 """Return state information for pickling""" 334 items = [[k, self[k]] for k in self] 335 inst_dict = vars(self).copy() 336 for k in vars(OrderedDict()): 337 inst_dict.pop(k, None) 338 if inst_dict: 339 return (self.__class__, (items,), inst_dict) 340 return self.__class__, (items,)
341
342 - def copy(self):
343 """od.copy() -> a shallow copy of od""" 344 return self.__class__(self)
345 346 @classmethod
347 - def fromkeys(cls, iterable, value=None):
348 """OD.fromkeys(S[, v]) -> New ordered dictionary with keys 349 from S and values equal to v (which defaults to None). 350 """ 351 d = cls() 352 for key in iterable: 353 d[key] = value 354 return d
355
356 - def __eq__(self, other):
357 """od.__eq__(y) <==> od==y. Comparison to another OD is 358 order-sensitive while comparison to a regular mapping is 359 order-insensitive. 360 """ 361 if isinstance(other, OrderedDict): 362 return len(self)==len(other) and self.items() == other.items() 363 return dict.__eq__(self, other)
364
365 - def __ne__(self, other):
366 return not self == other
367