Replaces one value in an ``args, kwargs`` pair. Inspects the function signature to find an argument by name whether it is passed by position or keyword. For use in decorators and similar wrappers.
| 354 | |
| 355 | |
| 356 | class ArgReplacer(object): |
| 357 | """Replaces one value in an ``args, kwargs`` pair. |
| 358 | |
| 359 | Inspects the function signature to find an argument by name |
| 360 | whether it is passed by position or keyword. For use in decorators |
| 361 | and similar wrappers. |
| 362 | """ |
| 363 | |
| 364 | def __init__(self, func: Callable, name: str) -> None: |
| 365 | self.name = name |
| 366 | try: |
| 367 | self.arg_pos = self._getargnames(func).index(name) # type: Optional[int] |
| 368 | except ValueError: |
| 369 | # Not a positional parameter |
| 370 | self.arg_pos = None |
| 371 | |
| 372 | def _getargnames(self, func: Callable) -> List[str]: |
| 373 | try: |
| 374 | return getfullargspec(func).args |
| 375 | except TypeError: |
| 376 | if hasattr(func, "func_code"): |
| 377 | # Cython-generated code has all the attributes needed |
| 378 | # by inspect.getfullargspec, but the inspect module only |
| 379 | # works with ordinary functions. Inline the portion of |
| 380 | # getfullargspec that we need here. Note that for static |
| 381 | # functions the @cython.binding(True) decorator must |
| 382 | # be used (for methods it works out of the box). |
| 383 | code = func.func_code # type: ignore |
| 384 | return code.co_varnames[: code.co_argcount] |
| 385 | raise |
| 386 | |
| 387 | def get_old_value( |
| 388 | self, args: Sequence[Any], kwargs: Dict[str, Any], default: Any = None |
| 389 | ) -> Any: |
| 390 | """Returns the old value of the named argument without replacing it. |
| 391 | |
| 392 | Returns ``default`` if the argument is not present. |
| 393 | """ |
| 394 | if self.arg_pos is not None and len(args) > self.arg_pos: |
| 395 | return args[self.arg_pos] |
| 396 | else: |
| 397 | return kwargs.get(self.name, default) |
| 398 | |
| 399 | def replace( |
| 400 | self, new_value: Any, args: Sequence[Any], kwargs: Dict[str, Any] |
| 401 | ) -> Tuple[Any, Sequence[Any], Dict[str, Any]]: |
| 402 | """Replace the named argument in ``args, kwargs`` with ``new_value``. |
| 403 | |
| 404 | Returns ``(old_value, args, kwargs)``. The returned ``args`` and |
| 405 | ``kwargs`` objects may not be the same as the input objects, or |
| 406 | the input objects may be mutated. |
| 407 | |
| 408 | If the named argument was not found, ``new_value`` will be added |
| 409 | to ``kwargs`` and None will be returned as ``old_value``. |
| 410 | """ |
| 411 | if self.arg_pos is not None and len(args) > self.arg_pos: |
| 412 | # The arg to replace is passed positionally |
| 413 | old_value = args[self.arg_pos] |