summaryrefslogtreecommitdiff
path: root/ironic/api
diff options
context:
space:
mode:
authorSteve Baker <sbaker@redhat.com>2020-04-17 16:30:34 +1200
committerSteve Baker <sbaker@redhat.com>2020-06-02 15:08:53 +1200
commit14a8b308da44feb16c472da2099b1eda8ce4e8dc (patch)
tree1667f5a2d07b7920c07967013480532957a1b0c5 /ironic/api
parent68cc5fbcb785126d38bc6cdab6c365f6d679deca (diff)
downloadironic-14a8b308da44feb16c472da2099b1eda8ce4e8dc.tar.gz
Add function definition handling
Story: 1651346 Task: 10551 Change-Id: Ia569192f7958852cfdc5479c159d92b289c7a717
Diffstat (limited to 'ironic/api')
-rw-r--r--ironic/api/expose.py10
-rw-r--r--ironic/api/functions.py181
2 files changed, 186 insertions, 5 deletions
diff --git a/ironic/api/expose.py b/ironic/api/expose.py
index 593ccacc9..94c0e8d76 100644
--- a/ironic/api/expose.py
+++ b/ironic/api/expose.py
@@ -25,9 +25,9 @@ import traceback
from oslo_config import cfg
from oslo_log import log
import pecan
-import wsme
import wsme.rest.args
+from ironic.api import functions
from ironic.api import types as atypes
LOG = log.getLogger(__name__)
@@ -58,11 +58,11 @@ pecan_json_decorate = pecan.expose(
def expose(*args, **kwargs):
- sig = wsme.signature(*args, **kwargs)
+ sig = functions.signature(*args, **kwargs)
def decorate(f):
sig(f)
- funcdef = wsme.api.FunctionDefinition.get(f)
+ funcdef = functions.FunctionDefinition.get(f)
funcdef.resolve_types(atypes.registry)
@functools.wraps(f)
@@ -212,7 +212,7 @@ class validate(object):
self.param_types = param_types
def __call__(self, func):
- argspec = wsme.api.getargspec(func)
- fd = wsme.api.FunctionDefinition.get(func)
+ argspec = functions.getargspec(func)
+ fd = functions.FunctionDefinition.get(func)
fd.set_arg_types(argspec, self.param_types)
return func
diff --git a/ironic/api/functions.py b/ironic/api/functions.py
new file mode 100644
index 000000000..8b4cebbdd
--- /dev/null
+++ b/ironic/api/functions.py
@@ -0,0 +1,181 @@
+# Copyright 2011-2019 the WSME authors and contributors
+# (See https://opendev.org/x/wsme/)
+#
+# This module is part of WSME and is also released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import functools
+import inspect
+import logging
+
+log = logging.getLogger(__name__)
+
+
+def iswsmefunction(f):
+ return hasattr(f, '_wsme_definition')
+
+
+def wrapfunc(f):
+ @functools.wraps(f)
+ def wrapper(*args, **kwargs):
+ return f(*args, **kwargs)
+ wrapper._wsme_original_func = f
+ return wrapper
+
+
+def getargspec(f):
+ f = getattr(f, '_wsme_original_func', f)
+ return inspect.getargspec(f)
+
+
+class FunctionArgument(object):
+ """An argument definition of an api entry"""
+ def __init__(self, name, datatype, mandatory, default):
+ #: argument name
+ self.name = name
+
+ #: Data type
+ self.datatype = datatype
+
+ #: True if the argument is mandatory
+ self.mandatory = mandatory
+
+ #: Default value if argument is omitted
+ self.default = default
+
+ def resolve_type(self, registry):
+ self.datatype = registry.resolve_type(self.datatype)
+
+
+class FunctionDefinition(object):
+ """An api entry definition"""
+ def __init__(self, func):
+ #: Function name
+ self.name = func.__name__
+
+ #: Function documentation
+ self.doc = func.__doc__
+
+ #: Return type
+ self.return_type = None
+
+ #: The function arguments (list of :class:`FunctionArgument`)
+ self.arguments = []
+
+ #: If the body carry the datas of a single argument, its type
+ self.body_type = None
+
+ #: Status code
+ self.status_code = 200
+
+ #: True if extra arguments should be ignored, NOT inserted in
+ #: the kwargs of the function and not raise UnknownArgument
+ #: exceptions
+ self.ignore_extra_args = False
+
+ #: Dictionnary of protocol-specific options.
+ self.extra_options = None
+
+ @staticmethod
+ def get(func):
+ """Returns the :class:`FunctionDefinition` of a method."""
+ if not hasattr(func, '_wsme_definition'):
+ fd = FunctionDefinition(func)
+ func._wsme_definition = fd
+
+ return func._wsme_definition
+
+ def get_arg(self, name):
+ """Returns a :class:`FunctionArgument` from its name"""
+ for arg in self.arguments:
+ if arg.name == name:
+ return arg
+ return None
+
+ def resolve_types(self, registry):
+ self.return_type = registry.resolve_type(self.return_type)
+ self.body_type = registry.resolve_type(self.body_type)
+ for arg in self.arguments:
+ arg.resolve_type(registry)
+
+ def set_options(self, body=None, ignore_extra_args=False, status_code=200,
+ rest_content_types=('json', 'xml'), **extra_options):
+ self.body_type = body
+ self.status_code = status_code
+ self.ignore_extra_args = ignore_extra_args
+ self.rest_content_types = rest_content_types
+ self.extra_options = extra_options
+
+ def set_arg_types(self, argspec, arg_types):
+ args, varargs, keywords, defaults = argspec
+ if args[0] == 'self':
+ args = args[1:]
+ arg_types = list(arg_types)
+ if self.body_type is not None:
+ arg_types.append(self.body_type)
+ for i, argname in enumerate(args):
+ datatype = arg_types[i]
+ mandatory = defaults is None or i < (len(args) - len(defaults))
+ default = None
+ if not mandatory:
+ default = defaults[i - (len(args) - len(defaults))]
+ self.arguments.append(FunctionArgument(argname, datatype,
+ mandatory, default))
+
+
+class signature(object):
+
+ """Decorator that specify the argument types of an exposed function.
+
+ :param return_type: Type of the value returned by the function
+ :param argN: Type of the Nth argument
+ :param body: If the function takes a final argument that is supposed to be
+ the request body by itself, its type.
+ :param status_code: HTTP return status code of the function.
+ :param ignore_extra_args: Allow extra/unknow arguments (default to False)
+
+ Most of the time this decorator is not supposed to be used directly,
+ unless you are not using WSME on top of another framework.
+
+ If an adapter is used, it will provide either a specialised version of this
+ decororator, either a new decorator named @wsexpose that takes the same
+ parameters (it will in addition expose the function, hence its name).
+ """
+
+ def __init__(self, *types, **options):
+ self.return_type = types[0] if types else None
+ self.arg_types = []
+ if len(types) > 1:
+ self.arg_types.extend(types[1:])
+ if 'body' in options:
+ self.arg_types.append(options['body'])
+ self.wrap = options.pop('wrap', False)
+ self.options = options
+
+ def __call__(self, func):
+ argspec = getargspec(func)
+ if self.wrap:
+ func = wrapfunc(func)
+ fd = FunctionDefinition.get(func)
+ if fd.extra_options is not None:
+ raise ValueError("This function is already exposed")
+ fd.return_type = self.return_type
+ fd.set_options(**self.options)
+ if self.arg_types:
+ fd.set_arg_types(argspec, self.arg_types)
+ return func
+
+
+sig = signature