wrapt 2.0.1__cp314-cp314-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of wrapt might be problematic. Click here for more details.

Binary file
wrapt/arguments.py ADDED
@@ -0,0 +1,59 @@
1
+ """The inspect.formatargspec() function was dropped in Python 3.11 but we need
2
+ it for when constructing signature changing decorators based on result of
3
+ inspect.getfullargspec(). The code here implements inspect.formatargspec() based
4
+ on Parameter and Signature from inspect module, which were added in Python 3.6.
5
+ Thanks to Cyril Jouve for the implementation.
6
+ """
7
+
8
+ from typing import Any, Callable, List, Mapping, Optional, Sequence, Tuple
9
+
10
+ try:
11
+ from inspect import Parameter, Signature
12
+ except ImportError:
13
+ from inspect import formatargspec # type: ignore[attr-defined]
14
+ else:
15
+
16
+ def formatargspec(
17
+ args: List[str],
18
+ varargs: Optional[str] = None,
19
+ varkw: Optional[str] = None,
20
+ defaults: Optional[Tuple[Any, ...]] = None,
21
+ kwonlyargs: Optional[Sequence[str]] = None,
22
+ kwonlydefaults: Optional[Mapping[str, Any]] = None,
23
+ annotations: Mapping[str, Any] = {},
24
+ formatarg: Callable[[str], str] = str,
25
+ formatvarargs: Callable[[str], str] = lambda name: "*" + name,
26
+ formatvarkw: Callable[[str], str] = lambda name: "**" + name,
27
+ formatvalue: Callable[[Any], str] = lambda value: "=" + repr(value),
28
+ formatreturns: Callable[[Any], str] = lambda text: " -> " + text,
29
+ formatannotation: Callable[[Any], str] = lambda annot: " -> " + repr(annot),
30
+ ) -> str:
31
+ if kwonlyargs is None:
32
+ kwonlyargs = ()
33
+ if kwonlydefaults is None:
34
+ kwonlydefaults = {}
35
+ ndefaults = len(defaults) if defaults else 0
36
+ parameters = [
37
+ Parameter(
38
+ arg,
39
+ Parameter.POSITIONAL_OR_KEYWORD,
40
+ default=defaults[i] if defaults and i >= 0 else Parameter.empty,
41
+ annotation=annotations.get(arg, Parameter.empty),
42
+ )
43
+ for i, arg in enumerate(args, ndefaults - len(args))
44
+ ]
45
+ if varargs:
46
+ parameters.append(Parameter(varargs, Parameter.VAR_POSITIONAL))
47
+ parameters.extend(
48
+ Parameter(
49
+ kwonlyarg,
50
+ Parameter.KEYWORD_ONLY,
51
+ default=kwonlydefaults.get(kwonlyarg, Parameter.empty),
52
+ annotation=annotations.get(kwonlyarg, Parameter.empty),
53
+ )
54
+ for kwonlyarg in kwonlyargs
55
+ )
56
+ if varkw:
57
+ parameters.append(Parameter(varkw, Parameter.VAR_KEYWORD))
58
+ return_annotation = annotations.get("return", Signature.empty)
59
+ return str(Signature(parameters, return_annotation=return_annotation))
wrapt/decorators.py ADDED
@@ -0,0 +1,522 @@
1
+ """This module implements decorators for implementing other decorators
2
+ as well as some commonly used decorators.
3
+
4
+ """
5
+
6
+ import sys
7
+ from functools import partial
8
+ from inspect import isclass, signature
9
+ from threading import Lock, RLock
10
+
11
+ from .__wrapt__ import BoundFunctionWrapper, CallableObjectProxy, FunctionWrapper
12
+ from .arguments import formatargspec
13
+
14
+ # Adapter wrapper for the wrapped function which will overlay certain
15
+ # properties from the adapter function onto the wrapped function so that
16
+ # functions such as inspect.getfullargspec(), inspect.signature() and
17
+ # inspect.getsource() return the correct results one would expect.
18
+
19
+
20
+ class _AdapterFunctionCode(CallableObjectProxy):
21
+
22
+ def __init__(self, wrapped_code, adapter_code):
23
+ super(_AdapterFunctionCode, self).__init__(wrapped_code)
24
+ self._self_adapter_code = adapter_code
25
+
26
+ @property
27
+ def co_argcount(self):
28
+ return self._self_adapter_code.co_argcount
29
+
30
+ @property
31
+ def co_code(self):
32
+ return self._self_adapter_code.co_code
33
+
34
+ @property
35
+ def co_flags(self):
36
+ return self._self_adapter_code.co_flags
37
+
38
+ @property
39
+ def co_kwonlyargcount(self):
40
+ return self._self_adapter_code.co_kwonlyargcount
41
+
42
+ @property
43
+ def co_varnames(self):
44
+ return self._self_adapter_code.co_varnames
45
+
46
+
47
+ class _AdapterFunctionSurrogate(CallableObjectProxy):
48
+
49
+ def __init__(self, wrapped, adapter):
50
+ super(_AdapterFunctionSurrogate, self).__init__(wrapped)
51
+ self._self_adapter = adapter
52
+
53
+ @property
54
+ def __code__(self):
55
+ return _AdapterFunctionCode(
56
+ self.__wrapped__.__code__, self._self_adapter.__code__
57
+ )
58
+
59
+ @property
60
+ def __defaults__(self):
61
+ return self._self_adapter.__defaults__
62
+
63
+ @property
64
+ def __kwdefaults__(self):
65
+ return self._self_adapter.__kwdefaults__
66
+
67
+ @property
68
+ def __signature__(self):
69
+ if "signature" not in globals():
70
+ return self._self_adapter.__signature__
71
+ else:
72
+ return signature(self._self_adapter)
73
+
74
+
75
+ class _BoundAdapterWrapper(BoundFunctionWrapper):
76
+
77
+ @property
78
+ def __func__(self):
79
+ return _AdapterFunctionSurrogate(
80
+ self.__wrapped__.__func__, self._self_parent._self_adapter
81
+ )
82
+
83
+ @property
84
+ def __signature__(self):
85
+ if "signature" not in globals():
86
+ return self.__wrapped__.__signature__
87
+ else:
88
+ return signature(self._self_parent._self_adapter)
89
+
90
+
91
+ class AdapterWrapper(FunctionWrapper):
92
+
93
+ __bound_function_wrapper__ = _BoundAdapterWrapper
94
+
95
+ def __init__(self, *args, **kwargs):
96
+ adapter = kwargs.pop("adapter")
97
+ super(AdapterWrapper, self).__init__(*args, **kwargs)
98
+ self._self_surrogate = _AdapterFunctionSurrogate(self.__wrapped__, adapter)
99
+ self._self_adapter = adapter
100
+
101
+ @property
102
+ def __code__(self):
103
+ return self._self_surrogate.__code__
104
+
105
+ @property
106
+ def __defaults__(self):
107
+ return self._self_surrogate.__defaults__
108
+
109
+ @property
110
+ def __kwdefaults__(self):
111
+ return self._self_surrogate.__kwdefaults__
112
+
113
+ @property
114
+ def __signature__(self):
115
+ return self._self_surrogate.__signature__
116
+
117
+
118
+ class AdapterFactory:
119
+ def __call__(self, wrapped):
120
+ raise NotImplementedError()
121
+
122
+
123
+ class DelegatedAdapterFactory(AdapterFactory):
124
+ def __init__(self, factory):
125
+ super(DelegatedAdapterFactory, self).__init__()
126
+ self.factory = factory
127
+
128
+ def __call__(self, wrapped):
129
+ return self.factory(wrapped)
130
+
131
+
132
+ adapter_factory = DelegatedAdapterFactory
133
+
134
+ # Decorator for creating other decorators. This decorator and the
135
+ # wrappers which they use are designed to properly preserve any name
136
+ # attributes, function signatures etc, in addition to the wrappers
137
+ # themselves acting like a transparent proxy for the original wrapped
138
+ # function so the wrapper is effectively indistinguishable from the
139
+ # original wrapped function.
140
+
141
+
142
+ def decorator(wrapper=None, /, *, enabled=None, adapter=None, proxy=FunctionWrapper):
143
+ """
144
+ The decorator should be supplied with a single positional argument
145
+ which is the `wrapper` function to be used to implement the
146
+ decorator. This may be preceded by a step whereby the keyword
147
+ arguments are supplied to customise the behaviour of the
148
+ decorator. The `adapter` argument is used to optionally denote a
149
+ separate function which is notionally used by an adapter
150
+ decorator. In that case parts of the function `__code__` and
151
+ `__defaults__` attributes are used from the adapter function
152
+ rather than those of the wrapped function. This allows for the
153
+ argument specification from `inspect.getfullargspec()` and similar
154
+ functions to be overridden with a prototype for a different
155
+ function than what was wrapped. The `enabled` argument provides a
156
+ way to enable/disable the use of the decorator. If the type of
157
+ `enabled` is a boolean, then it is evaluated immediately and the
158
+ wrapper not even applied if it is `False`. If not a boolean, it will
159
+ be evaluated when the wrapper is called for an unbound wrapper,
160
+ and when binding occurs for a bound wrapper. When being evaluated,
161
+ if `enabled` is callable it will be called to obtain the value to
162
+ be checked. If `False`, the wrapper will not be called and instead
163
+ the original wrapped function will be called directly instead.
164
+ The `proxy` argument provides a way of passing a custom version of
165
+ the `FunctionWrapper` class used in decorating the function.
166
+ """
167
+
168
+ if wrapper is not None:
169
+ # Helper function for creating wrapper of the appropriate
170
+ # time when we need it down below.
171
+
172
+ def _build(wrapped, wrapper, enabled=None, adapter=None):
173
+ if adapter:
174
+ if isinstance(adapter, AdapterFactory):
175
+ adapter = adapter(wrapped)
176
+
177
+ if not callable(adapter):
178
+ ns = {}
179
+
180
+ # Check if the signature argument specification has
181
+ # annotations. If it does then we need to remember
182
+ # it but also drop it when attempting to manufacture
183
+ # a standin adapter function. This is necessary else
184
+ # it will try and look up any types referenced in
185
+ # the annotations in the empty namespace we use,
186
+ # which will fail.
187
+
188
+ annotations = {}
189
+
190
+ if not isinstance(adapter, str):
191
+ if len(adapter) == 7:
192
+ annotations = adapter[-1]
193
+ adapter = adapter[:-1]
194
+ adapter = formatargspec(*adapter)
195
+
196
+ exec(f"def adapter{adapter}: pass", ns, ns)
197
+ adapter = ns["adapter"]
198
+
199
+ # Override the annotations for the manufactured
200
+ # adapter function so they match the original
201
+ # adapter signature argument specification.
202
+
203
+ if annotations:
204
+ adapter.__annotations__ = annotations
205
+
206
+ return AdapterWrapper(
207
+ wrapped=wrapped, wrapper=wrapper, enabled=enabled, adapter=adapter
208
+ )
209
+
210
+ return proxy(wrapped=wrapped, wrapper=wrapper, enabled=enabled)
211
+
212
+ # The wrapper has been provided so return the final decorator.
213
+ # The decorator is itself one of our function wrappers so we
214
+ # can determine when it is applied to functions, instance methods
215
+ # or class methods. This allows us to bind the instance or class
216
+ # method so the appropriate self or cls attribute is supplied
217
+ # when it is finally called.
218
+
219
+ def _wrapper(wrapped, instance, args, kwargs):
220
+ # We first check for the case where the decorator was applied
221
+ # to a class type.
222
+ #
223
+ # @decorator
224
+ # class mydecoratorclass:
225
+ # def __init__(self, arg=None):
226
+ # self.arg = arg
227
+ # def __call__(self, wrapped, instance, args, kwargs):
228
+ # return wrapped(*args, **kwargs)
229
+ #
230
+ # @mydecoratorclass(arg=1)
231
+ # def function():
232
+ # pass
233
+ #
234
+ # In this case an instance of the class is to be used as the
235
+ # decorator wrapper function. If args was empty at this point,
236
+ # then it means that there were optional keyword arguments
237
+ # supplied to be used when creating an instance of the class
238
+ # to be used as the wrapper function.
239
+
240
+ if instance is None and isclass(wrapped) and not args:
241
+ # We still need to be passed the target function to be
242
+ # wrapped as yet, so we need to return a further function
243
+ # to be able to capture it.
244
+
245
+ def _capture(target_wrapped):
246
+ # Now have the target function to be wrapped and need
247
+ # to create an instance of the class which is to act
248
+ # as the decorator wrapper function. Before we do that,
249
+ # we need to first check that use of the decorator
250
+ # hadn't been disabled by a simple boolean. If it was,
251
+ # the target function to be wrapped is returned instead.
252
+
253
+ _enabled = enabled
254
+ if type(_enabled) is bool:
255
+ if not _enabled:
256
+ return target_wrapped
257
+ _enabled = None
258
+
259
+ # Now create an instance of the class which is to act
260
+ # as the decorator wrapper function. Any arguments had
261
+ # to be supplied as keyword only arguments so that is
262
+ # all we pass when creating it.
263
+
264
+ target_wrapper = wrapped(**kwargs)
265
+
266
+ # Finally build the wrapper itself and return it.
267
+
268
+ return _build(target_wrapped, target_wrapper, _enabled, adapter)
269
+
270
+ return _capture
271
+
272
+ # We should always have the target function to be wrapped at
273
+ # this point as the first (and only) value in args.
274
+
275
+ target_wrapped = args[0]
276
+
277
+ # Need to now check that use of the decorator hadn't been
278
+ # disabled by a simple boolean. If it was, then target
279
+ # function to be wrapped is returned instead.
280
+
281
+ _enabled = enabled
282
+ if type(_enabled) is bool:
283
+ if not _enabled:
284
+ return target_wrapped
285
+ _enabled = None
286
+
287
+ # We now need to build the wrapper, but there are a couple of
288
+ # different cases we need to consider.
289
+
290
+ if instance is None:
291
+ if isclass(wrapped):
292
+ # In this case the decorator was applied to a class
293
+ # type but optional keyword arguments were not supplied
294
+ # for initialising an instance of the class to be used
295
+ # as the decorator wrapper function.
296
+ #
297
+ # @decorator
298
+ # class mydecoratorclass:
299
+ # def __init__(self, arg=None):
300
+ # self.arg = arg
301
+ # def __call__(self, wrapped, instance,
302
+ # args, kwargs):
303
+ # return wrapped(*args, **kwargs)
304
+ #
305
+ # @mydecoratorclass
306
+ # def function():
307
+ # pass
308
+ #
309
+ # We still need to create an instance of the class to
310
+ # be used as the decorator wrapper function, but no
311
+ # arguments are pass.
312
+
313
+ target_wrapper = wrapped()
314
+
315
+ else:
316
+ # In this case the decorator was applied to a normal
317
+ # function, or possibly a static method of a class.
318
+ #
319
+ # @decorator
320
+ # def mydecoratorfuntion(wrapped, instance,
321
+ # args, kwargs):
322
+ # return wrapped(*args, **kwargs)
323
+ #
324
+ # @mydecoratorfunction
325
+ # def function():
326
+ # pass
327
+ #
328
+ # That normal function becomes the decorator wrapper
329
+ # function.
330
+
331
+ target_wrapper = wrapper
332
+
333
+ else:
334
+ if isclass(instance):
335
+ # In this case the decorator was applied to a class
336
+ # method.
337
+ #
338
+ # class myclass:
339
+ # @decorator
340
+ # @classmethod
341
+ # def decoratorclassmethod(cls, wrapped,
342
+ # instance, args, kwargs):
343
+ # return wrapped(*args, **kwargs)
344
+ #
345
+ # instance = myclass()
346
+ #
347
+ # @instance.decoratorclassmethod
348
+ # def function():
349
+ # pass
350
+ #
351
+ # This one is a bit strange because binding was actually
352
+ # performed on the wrapper created by our decorator
353
+ # factory. We need to apply that binding to the decorator
354
+ # wrapper function that the decorator factory
355
+ # was applied to.
356
+
357
+ target_wrapper = wrapper.__get__(None, instance)
358
+
359
+ else:
360
+ # In this case the decorator was applied to an instance
361
+ # method.
362
+ #
363
+ # class myclass:
364
+ # @decorator
365
+ # def decoratorclassmethod(self, wrapped,
366
+ # instance, args, kwargs):
367
+ # return wrapped(*args, **kwargs)
368
+ #
369
+ # instance = myclass()
370
+ #
371
+ # @instance.decoratorclassmethod
372
+ # def function():
373
+ # pass
374
+ #
375
+ # This one is a bit strange because binding was actually
376
+ # performed on the wrapper created by our decorator
377
+ # factory. We need to apply that binding to the decorator
378
+ # wrapper function that the decorator factory
379
+ # was applied to.
380
+
381
+ target_wrapper = wrapper.__get__(instance, type(instance))
382
+
383
+ # Finally build the wrapper itself and return it.
384
+
385
+ return _build(target_wrapped, target_wrapper, _enabled, adapter)
386
+
387
+ # We first return our magic function wrapper here so we can
388
+ # determine in what context the decorator factory was used. In
389
+ # other words, it is itself a universal decorator. The decorator
390
+ # function is used as the adapter so that linters see a signature
391
+ # corresponding to the decorator and not the wrapper it is being
392
+ # applied to.
393
+
394
+ return _build(wrapper, _wrapper, adapter=decorator)
395
+
396
+ else:
397
+ # The wrapper still has not been provided, so we are just
398
+ # collecting the optional keyword arguments. Return the
399
+ # decorator again wrapped in a partial using the collected
400
+ # arguments.
401
+
402
+ return partial(decorator, enabled=enabled, adapter=adapter, proxy=proxy)
403
+
404
+
405
+ # Decorator for implementing thread synchronization. It can be used as a
406
+ # decorator, in which case the synchronization context is determined by
407
+ # what type of function is wrapped, or it can also be used as a context
408
+ # manager, where the user needs to supply the correct synchronization
409
+ # context. It is also possible to supply an object which appears to be a
410
+ # synchronization primitive of some sort, by virtue of having release()
411
+ # and acquire() methods. In that case that will be used directly as the
412
+ # synchronization primitive without creating a separate lock against the
413
+ # derived or supplied context.
414
+
415
+
416
+ def synchronized(wrapped):
417
+ """Depending on the nature of the `wrapped` object, will either return a
418
+ decorator which can be used to wrap a function or method, or a context
419
+ manager, both of which will act accordingly depending on how used, to
420
+ synchronize access to calling of the wrapped function, or the block of
421
+ code within the context manager. If it is an object which is a
422
+ synchronization primitive, such as a threading Lock, RLock, Semaphore,
423
+ Condition, or Event, then it is assumed that the object is to be used
424
+ directly as the synchronization primitive, otherwise a lock is created
425
+ automatically and attached to the wrapped object and used as the
426
+ synchronization primitive.
427
+ """
428
+
429
+ # Determine if being passed an object which is a synchronization
430
+ # primitive. We can't check by type for Lock, RLock, Semaphore etc,
431
+ # as the means of creating them isn't the type. Therefore use the
432
+ # existence of acquire() and release() methods. This is more
433
+ # extensible anyway as it allows custom synchronization mechanisms.
434
+
435
+ if hasattr(wrapped, "acquire") and hasattr(wrapped, "release"):
436
+ # We remember what the original lock is and then return a new
437
+ # decorator which accesses and locks it. When returning the new
438
+ # decorator we wrap it with an object proxy so we can override
439
+ # the context manager methods in case it is being used to wrap
440
+ # synchronized statements with a 'with' statement.
441
+
442
+ lock = wrapped
443
+
444
+ @decorator
445
+ def _synchronized(wrapped, instance, args, kwargs):
446
+ # Execute the wrapped function while the original supplied
447
+ # lock is held.
448
+
449
+ with lock:
450
+ return wrapped(*args, **kwargs)
451
+
452
+ class _PartialDecorator(CallableObjectProxy):
453
+
454
+ def __enter__(self):
455
+ lock.acquire()
456
+ return lock
457
+
458
+ def __exit__(self, *args):
459
+ lock.release()
460
+
461
+ return _PartialDecorator(wrapped=_synchronized)
462
+
463
+ # Following only apply when the lock is being created automatically
464
+ # based on the context of what was supplied. In this case we supply
465
+ # a final decorator, but need to use FunctionWrapper directly as we
466
+ # want to derive from it to add context manager methods in case it is
467
+ # being used to wrap synchronized statements with a 'with' statement.
468
+
469
+ def _synchronized_lock(context):
470
+ # Attempt to retrieve the lock for the specific context.
471
+
472
+ lock = vars(context).get("_synchronized_lock", None)
473
+
474
+ if lock is None:
475
+ # There is no existing lock defined for the context we
476
+ # are dealing with so we need to create one. This needs
477
+ # to be done in a way to guarantee there is only one
478
+ # created, even if multiple threads try and create it at
479
+ # the same time. We can't always use the setdefault()
480
+ # method on the __dict__ for the context. This is the
481
+ # case where the context is a class, as __dict__ is
482
+ # actually a dictproxy. What we therefore do is use a
483
+ # meta lock on this wrapper itself, to control the
484
+ # creation and assignment of the lock attribute against
485
+ # the context.
486
+
487
+ with synchronized._synchronized_meta_lock:
488
+ # We need to check again for whether the lock we want
489
+ # exists in case two threads were trying to create it
490
+ # at the same time and were competing to create the
491
+ # meta lock.
492
+
493
+ lock = vars(context).get("_synchronized_lock", None)
494
+
495
+ if lock is None:
496
+ lock = RLock()
497
+ setattr(context, "_synchronized_lock", lock)
498
+
499
+ return lock
500
+
501
+ def _synchronized_wrapper(wrapped, instance, args, kwargs):
502
+ # Execute the wrapped function while the lock for the
503
+ # desired context is held. If instance is None then the
504
+ # wrapped function is used as the context.
505
+
506
+ with _synchronized_lock(instance if instance is not None else wrapped):
507
+ return wrapped(*args, **kwargs)
508
+
509
+ class _FinalDecorator(FunctionWrapper):
510
+
511
+ def __enter__(self):
512
+ self._self_lock = _synchronized_lock(self.__wrapped__)
513
+ self._self_lock.acquire()
514
+ return self._self_lock
515
+
516
+ def __exit__(self, *args):
517
+ self._self_lock.release()
518
+
519
+ return _FinalDecorator(wrapped=wrapped, wrapper=_synchronized_wrapper)
520
+
521
+
522
+ synchronized._synchronized_meta_lock = Lock() # type: ignore[attr-defined]