|
31 | 31 | from DIRAC.Core.Security.X509Chain import X509Chain # pylint: disable=import-error
|
32 | 32 | from DIRAC.FrameworkSystem.Client.MonitoringClient import MonitoringClient
|
33 | 33 | from DIRAC.Resources.IdProvider.Utilities import getProvidersForInstance
|
34 |
| - |
35 | 34 | from DIRAC.Resources.IdProvider.IdProviderFactory import IdProviderFactory
|
36 | 35 |
|
37 | 36 | sLog = gLogger.getSubLogger(__name__.split(".")[-1])
|
@@ -396,28 +395,73 @@ def _getMethodName(self):
|
396 | 395 | raise NotImplementedError("Please, create the _getMethodName method")
|
397 | 396 |
|
398 | 397 | def _getMethodArgs(self, args):
|
399 |
| - """Decode args. |
| 398 | + """Search method arguments. |
| 399 | +
|
| 400 | + By default, the arguments are taken from the description of the method itself. |
| 401 | + Then the arguments received in the request are assigned by the name of the method arguments. |
| 402 | +
|
| 403 | + .. warning:: this means that the target methods cannot be wrapped in the decorator, |
| 404 | + or if so the decorator must duplicate the arguments and annotation of the target method |
| 405 | +
|
| 406 | + :param tuple args: positional arguments, they are determined by a variable path_< method name >, e.g.: |
| 407 | + `path_methodName = ['([A-z0-9-_]*)']`. In most cases, this is simply not used. |
400 | 408 |
|
401 | 409 | :return: tuple -- contain args and kwargs
|
402 | 410 | """
|
403 |
| - # Read information about method |
404 |
| - fArgs, fvArgs, fvKwargs, fDef = inspect.getargspec(self.methodObj) |
405 |
| - # Remove self argument from method arguments |
406 |
| - fArgs = [a for a in fArgs if a != "self"] |
407 |
| - # Pass all arguments |
408 |
| - kwargs = self.request.arguments.copy() if fvKwargs else {} |
409 |
| - # Get all defaults from method |
410 |
| - defaults = {a: fDef[fArgs[-len(fDef) :].index(a)] for a in fArgs[-len(fDef) :]} if fDef else {} |
411 |
| - # Calcule kwargs |
412 |
| - for arg in fArgs[len(args) :]: |
413 |
| - if arg not in defaults: |
414 |
| - kwargs[arg] = self.get_argument(arg) |
415 |
| - elif isinstance(defaults[arg], list): |
416 |
| - kwargs[arg] = self.get_arguments(arg) or defaults[arg] |
| 411 | + # Read signature of a target function |
| 412 | + # https://docs.python.org/3/library/inspect.html#inspect.Signature |
| 413 | + signature = inspect.signature(self.methodObj) |
| 414 | + |
| 415 | + # Collect all values of the arguments transferred in a request |
| 416 | + args = [unquote(a) for a in args] # positional arguments |
| 417 | + kwargs = {a: self.get_argument(a) for a in self.request.arguments} # keyword arguments |
| 418 | + |
| 419 | + # Create a mapping from request arguments to parameters |
| 420 | + bound = signature.bind(*args, **kwargs) |
| 421 | + # Set default values for missing arguments. |
| 422 | + bound.apply_defaults() |
| 423 | + |
| 424 | + keywordArguments = {} |
| 425 | + positionalArguments = [] |
| 426 | + # Now let's check whether the value of the argument corresponds to the type specified in the objective function or type of the default value. |
| 427 | + for name in signature.parameters: |
| 428 | + value = bound.arguments[name] |
| 429 | + kind = signature.parameters[name].kind |
| 430 | + default = signature.parameters[name].default |
| 431 | + |
| 432 | + # Determine what type of the target function argument is expected. By Default it's str. |
| 433 | + annotation = ( |
| 434 | + signature.parameters[name].annotation |
| 435 | + # Select the type specified in the target function, if any. |
| 436 | + # E.g.: def export_f(self, number:int): return S_OK(number) |
| 437 | + if signature.parameters[name].annotation is not inspect.Parameter.empty |
| 438 | + else str |
| 439 | + # If there is no argument annotation, take the default value type, if any |
| 440 | + # E.g.: def export_f(self, number=0): return S_OK(number) |
| 441 | + if default is inspect.Parameter.empty |
| 442 | + else type(default) |
| 443 | + ) |
| 444 | + |
| 445 | + # If the type of the argument value does not match the expectation, we convert it to the appropriate type |
| 446 | + if value != default: |
| 447 | + # Get list of the arguments |
| 448 | + if annotation is list: |
| 449 | + value = self.get_arguments(name) if name in self.request.arguments else [value] |
| 450 | + # Get integer argument |
| 451 | + elif annotation is int: |
| 452 | + value = int(value) |
| 453 | + |
| 454 | + # Collect positional parameters separately |
| 455 | + if kind == inspect.Parameter.POSITIONAL_ONLY: |
| 456 | + positionalArguments.append(value) |
| 457 | + elif kind == inspect.Parameter.VAR_POSITIONAL: |
| 458 | + positionalArguments.extend(value) |
| 459 | + elif kind == inspect.Parameter.VAR_KEYWORD: |
| 460 | + keywordArguments.update(value) |
417 | 461 | else:
|
418 |
| - kwargs[arg] = self.get_argument(arg, defaults[arg]) |
| 462 | + keywordArguments[name] = value |
419 | 463 |
|
420 |
| - return ([unquote(a) for a in args], kwargs) |
| 464 | + return (positionalArguments, keywordArguments) |
421 | 465 |
|
422 | 466 | def _getMethodAuthProps(self):
|
423 | 467 | """Resolves the hard coded authorization requirements for method.
|
|
0 commit comments