Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/django/utils/functional.py: 32%

228 statements  

« prev     ^ index     » next       coverage.py v7.0.5, created at 2023-01-17 06:13 +0000

1import copy 

2import itertools 

3import operator 

4import warnings 

5from functools import total_ordering, wraps 

6 

7 

8class cached_property: 

9 """ 

10 Decorator that converts a method with a single self argument into a 

11 property cached on the instance. 

12 

13 A cached property can be made out of an existing method: 

14 (e.g. ``url = cached_property(get_absolute_url)``). 

15 """ 

16 

17 name = None 

18 

19 @staticmethod 

20 def func(instance): 

21 raise TypeError( 

22 "Cannot use cached_property instance without calling " 

23 "__set_name__() on it." 

24 ) 

25 

26 def __init__(self, func, name=None): 

27 from django.utils.deprecation import RemovedInDjango50Warning 

28 

29 if name is not None: 

30 warnings.warn( 

31 "The name argument is deprecated as it's unnecessary as of " 

32 "Python 3.6.", 

33 RemovedInDjango50Warning, 

34 stacklevel=2, 

35 ) 

36 self.real_func = func 

37 self.__doc__ = getattr(func, "__doc__") 

38 

39 def __set_name__(self, owner, name): 

40 if self.name is None: 

41 self.name = name 

42 self.func = self.real_func 

43 elif name != self.name: 

44 raise TypeError( 

45 "Cannot assign the same cached_property to two different names " 

46 "(%r and %r)." % (self.name, name) 

47 ) 

48 

49 def __get__(self, instance, cls=None): 

50 """ 

51 Call the function and put the return value in instance.__dict__ so that 

52 subsequent attribute access on the instance returns the cached value 

53 instead of calling cached_property.__get__(). 

54 """ 

55 if instance is None: 

56 return self 

57 res = instance.__dict__[self.name] = self.func(instance) 

58 return res 

59 

60 

61class classproperty: 

62 """ 

63 Decorator that converts a method with a single cls argument into a property 

64 that can be accessed directly from the class. 

65 """ 

66 

67 def __init__(self, method=None): 

68 self.fget = method 

69 

70 def __get__(self, instance, cls=None): 

71 return self.fget(cls) 

72 

73 def getter(self, method): 

74 self.fget = method 

75 return self 

76 

77 

78class Promise: 

79 """ 

80 Base class for the proxy class created in the closure of the lazy function. 

81 It's used to recognize promises in code. 

82 """ 

83 

84 pass 

85 

86 

87def lazy(func, *resultclasses): 

88 """ 

89 Turn any callable into a lazy evaluated callable. result classes or types 

90 is required -- at least one is needed so that the automatic forcing of 

91 the lazy evaluation code is triggered. Results are not memoized; the 

92 function is evaluated on every access. 

93 """ 

94 

95 @total_ordering 

96 class __proxy__(Promise): 

97 """ 

98 Encapsulate a function call and act as a proxy for methods that are 

99 called on the result of that function. The function is not evaluated 

100 until one of the methods on the result is called. 

101 """ 

102 

103 __prepared = False 

104 

105 def __init__(self, args, kw): 

106 self.__args = args 

107 self.__kw = kw 

108 if not self.__prepared: 

109 self.__prepare_class__() 

110 self.__class__.__prepared = True 

111 

112 def __reduce__(self): 

113 return ( 

114 _lazy_proxy_unpickle, 

115 (func, self.__args, self.__kw) + resultclasses, 

116 ) 

117 

118 def __repr__(self): 

119 return repr(self.__cast()) 

120 

121 @classmethod 

122 def __prepare_class__(cls): 

123 for resultclass in resultclasses: 

124 for type_ in resultclass.mro(): 

125 for method_name in type_.__dict__: 

126 # All __promise__ return the same wrapper method, they 

127 # look up the correct implementation when called. 

128 if hasattr(cls, method_name): 

129 continue 

130 meth = cls.__promise__(method_name) 

131 setattr(cls, method_name, meth) 

132 cls._delegate_bytes = bytes in resultclasses 

133 cls._delegate_text = str in resultclasses 

134 if cls._delegate_bytes and cls._delegate_text: 

135 raise ValueError( 

136 "Cannot call lazy() with both bytes and text return types." 

137 ) 

138 if cls._delegate_text: 

139 cls.__str__ = cls.__text_cast 

140 elif cls._delegate_bytes: 

141 cls.__bytes__ = cls.__bytes_cast 

142 

143 @classmethod 

144 def __promise__(cls, method_name): 

145 # Builds a wrapper around some magic method 

146 def __wrapper__(self, *args, **kw): 

147 # Automatically triggers the evaluation of a lazy value and 

148 # applies the given magic method of the result type. 

149 res = func(*self.__args, **self.__kw) 

150 return getattr(res, method_name)(*args, **kw) 

151 

152 return __wrapper__ 

153 

154 def __text_cast(self): 

155 return func(*self.__args, **self.__kw) 

156 

157 def __bytes_cast(self): 

158 return bytes(func(*self.__args, **self.__kw)) 

159 

160 def __bytes_cast_encoded(self): 

161 return func(*self.__args, **self.__kw).encode() 

162 

163 def __cast(self): 

164 if self._delegate_bytes: 

165 return self.__bytes_cast() 

166 elif self._delegate_text: 

167 return self.__text_cast() 

168 else: 

169 return func(*self.__args, **self.__kw) 

170 

171 def __str__(self): 

172 # object defines __str__(), so __prepare_class__() won't overload 

173 # a __str__() method from the proxied class. 

174 return str(self.__cast()) 

175 

176 def __eq__(self, other): 

177 if isinstance(other, Promise): 

178 other = other.__cast() 

179 return self.__cast() == other 

180 

181 def __lt__(self, other): 

182 if isinstance(other, Promise): 

183 other = other.__cast() 

184 return self.__cast() < other 

185 

186 def __hash__(self): 

187 return hash(self.__cast()) 

188 

189 def __mod__(self, rhs): 

190 if self._delegate_text: 

191 return str(self) % rhs 

192 return self.__cast() % rhs 

193 

194 def __add__(self, other): 

195 return self.__cast() + other 

196 

197 def __radd__(self, other): 

198 return other + self.__cast() 

199 

200 def __deepcopy__(self, memo): 

201 # Instances of this class are effectively immutable. It's just a 

202 # collection of functions. So we don't need to do anything 

203 # complicated for copying. 

204 memo[id(self)] = self 

205 return self 

206 

207 @wraps(func) 

208 def __wrapper__(*args, **kw): 

209 # Creates the proxy object, instead of the actual value. 

210 return __proxy__(args, kw) 

211 

212 return __wrapper__ 

213 

214 

215def _lazy_proxy_unpickle(func, args, kwargs, *resultclasses): 

216 return lazy(func, *resultclasses)(*args, **kwargs) 

217 

218 

219def lazystr(text): 

220 """ 

221 Shortcut for the common case of a lazy callable that returns str. 

222 """ 

223 return lazy(str, str)(text) 

224 

225 

226def keep_lazy(*resultclasses): 

227 """ 

228 A decorator that allows a function to be called with one or more lazy 

229 arguments. If none of the args are lazy, the function is evaluated 

230 immediately, otherwise a __proxy__ is returned that will evaluate the 

231 function when needed. 

232 """ 

233 if not resultclasses: 

234 raise TypeError("You must pass at least one argument to keep_lazy().") 

235 

236 def decorator(func): 

237 lazy_func = lazy(func, *resultclasses) 

238 

239 @wraps(func) 

240 def wrapper(*args, **kwargs): 

241 if any( 

242 isinstance(arg, Promise) 

243 for arg in itertools.chain(args, kwargs.values()) 

244 ): 

245 return lazy_func(*args, **kwargs) 

246 return func(*args, **kwargs) 

247 

248 return wrapper 

249 

250 return decorator 

251 

252 

253def keep_lazy_text(func): 

254 """ 

255 A decorator for functions that accept lazy arguments and return text. 

256 """ 

257 return keep_lazy(str)(func) 

258 

259 

260empty = object() 

261 

262 

263def new_method_proxy(func): 

264 def inner(self, *args): 

265 if (_wrapped := self._wrapped) is empty: 

266 self._setup() 

267 _wrapped = self._wrapped 

268 return func(_wrapped, *args) 

269 

270 inner._mask_wrapped = False 

271 return inner 

272 

273 

274class LazyObject: 

275 """ 

276 A wrapper for another class that can be used to delay instantiation of the 

277 wrapped class. 

278 

279 By subclassing, you have the opportunity to intercept and alter the 

280 instantiation. If you don't need to do that, use SimpleLazyObject. 

281 """ 

282 

283 # Avoid infinite recursion when tracing __init__ (#19456). 

284 _wrapped = None 

285 

286 def __init__(self): 

287 # Note: if a subclass overrides __init__(), it will likely need to 

288 # override __copy__() and __deepcopy__() as well. 

289 self._wrapped = empty 

290 

291 def __getattribute__(self, name): 

292 if name == "_wrapped": 

293 # Avoid recursion when getting wrapped object. 

294 return super().__getattribute__(name) 

295 value = super().__getattribute__(name) 

296 # If attribute is a proxy method, raise an AttributeError to call 

297 # __getattr__() and use the wrapped object method. 

298 if not getattr(value, "_mask_wrapped", True): 

299 raise AttributeError 

300 return value 

301 

302 __getattr__ = new_method_proxy(getattr) 

303 

304 def __setattr__(self, name, value): 

305 if name == "_wrapped": 

306 # Assign to __dict__ to avoid infinite __setattr__ loops. 

307 self.__dict__["_wrapped"] = value 

308 else: 

309 if self._wrapped is empty: 

310 self._setup() 

311 setattr(self._wrapped, name, value) 

312 

313 def __delattr__(self, name): 

314 if name == "_wrapped": 

315 raise TypeError("can't delete _wrapped.") 

316 if self._wrapped is empty: 

317 self._setup() 

318 delattr(self._wrapped, name) 

319 

320 def _setup(self): 

321 """ 

322 Must be implemented by subclasses to initialize the wrapped object. 

323 """ 

324 raise NotImplementedError( 

325 "subclasses of LazyObject must provide a _setup() method" 

326 ) 

327 

328 # Because we have messed with __class__ below, we confuse pickle as to what 

329 # class we are pickling. We're going to have to initialize the wrapped 

330 # object to successfully pickle it, so we might as well just pickle the 

331 # wrapped object since they're supposed to act the same way. 

332 # 

333 # Unfortunately, if we try to simply act like the wrapped object, the ruse 

334 # will break down when pickle gets our id(). Thus we end up with pickle 

335 # thinking, in effect, that we are a distinct object from the wrapped 

336 # object, but with the same __dict__. This can cause problems (see #25389). 

337 # 

338 # So instead, we define our own __reduce__ method and custom unpickler. We 

339 # pickle the wrapped object as the unpickler's argument, so that pickle 

340 # will pickle it normally, and then the unpickler simply returns its 

341 # argument. 

342 def __reduce__(self): 

343 if self._wrapped is empty: 

344 self._setup() 

345 return (unpickle_lazyobject, (self._wrapped,)) 

346 

347 def __copy__(self): 

348 if self._wrapped is empty: 

349 # If uninitialized, copy the wrapper. Use type(self), not 

350 # self.__class__, because the latter is proxied. 

351 return type(self)() 

352 else: 

353 # If initialized, return a copy of the wrapped object. 

354 return copy.copy(self._wrapped) 

355 

356 def __deepcopy__(self, memo): 

357 if self._wrapped is empty: 

358 # We have to use type(self), not self.__class__, because the 

359 # latter is proxied. 

360 result = type(self)() 

361 memo[id(self)] = result 

362 return result 

363 return copy.deepcopy(self._wrapped, memo) 

364 

365 __bytes__ = new_method_proxy(bytes) 

366 __str__ = new_method_proxy(str) 

367 __bool__ = new_method_proxy(bool) 

368 

369 # Introspection support 

370 __dir__ = new_method_proxy(dir) 

371 

372 # Need to pretend to be the wrapped class, for the sake of objects that 

373 # care about this (especially in equality tests) 

374 __class__ = property(new_method_proxy(operator.attrgetter("__class__"))) 

375 __eq__ = new_method_proxy(operator.eq) 

376 __lt__ = new_method_proxy(operator.lt) 

377 __gt__ = new_method_proxy(operator.gt) 

378 __ne__ = new_method_proxy(operator.ne) 

379 __hash__ = new_method_proxy(hash) 

380 

381 # List/Tuple/Dictionary methods support 

382 __getitem__ = new_method_proxy(operator.getitem) 

383 __setitem__ = new_method_proxy(operator.setitem) 

384 __delitem__ = new_method_proxy(operator.delitem) 

385 __iter__ = new_method_proxy(iter) 

386 __len__ = new_method_proxy(len) 

387 __contains__ = new_method_proxy(operator.contains) 

388 

389 

390def unpickle_lazyobject(wrapped): 

391 """ 

392 Used to unpickle lazy objects. Just return its argument, which will be the 

393 wrapped object. 

394 """ 

395 return wrapped 

396 

397 

398class SimpleLazyObject(LazyObject): 

399 """ 

400 A lazy object initialized from any function. 

401 

402 Designed for compound objects of unknown type. For builtins or objects of 

403 known type, use django.utils.functional.lazy. 

404 """ 

405 

406 def __init__(self, func): 

407 """ 

408 Pass in a callable that returns the object to be wrapped. 

409 

410 If copies are made of the resulting SimpleLazyObject, which can happen 

411 in various circumstances within Django, then you must ensure that the 

412 callable can be safely run more than once and will return the same 

413 value. 

414 """ 

415 self.__dict__["_setupfunc"] = func 

416 super().__init__() 

417 

418 def _setup(self): 

419 self._wrapped = self._setupfunc() 

420 

421 # Return a meaningful representation of the lazy object for debugging 

422 # without evaluating the wrapped object. 

423 def __repr__(self): 

424 if self._wrapped is empty: 

425 repr_attr = self._setupfunc 

426 else: 

427 repr_attr = self._wrapped 

428 return "<%s: %r>" % (type(self).__name__, repr_attr) 

429 

430 def __copy__(self): 

431 if self._wrapped is empty: 

432 # If uninitialized, copy the wrapper. Use SimpleLazyObject, not 

433 # self.__class__, because the latter is proxied. 

434 return SimpleLazyObject(self._setupfunc) 

435 else: 

436 # If initialized, return a copy of the wrapped object. 

437 return copy.copy(self._wrapped) 

438 

439 def __deepcopy__(self, memo): 

440 if self._wrapped is empty: 

441 # We have to use SimpleLazyObject, not self.__class__, because the 

442 # latter is proxied. 

443 result = SimpleLazyObject(self._setupfunc) 

444 memo[id(self)] = result 

445 return result 

446 return copy.deepcopy(self._wrapped, memo) 

447 

448 __add__ = new_method_proxy(operator.add) 

449 

450 @new_method_proxy 

451 def __radd__(self, other): 

452 return other + self 

453 

454 

455def partition(predicate, values): 

456 """ 

457 Split the values into two sets, based on the return value of the function 

458 (True/False). e.g.: 

459 

460 >>> partition(lambda x: x > 3, range(5)) 

461 [0, 1, 2, 3], [4] 

462 """ 

463 results = ([], []) 

464 for item in values: 

465 results[predicate(item)].append(item) 

466 return results