X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=sfa%2Futil%2Fmethod.py;h=7329036e400a55b03c3ff5f777eebc9124c19c80;hb=130e896408741add08ebc3a1b6c74aca11023c1e;hp=e7a12d867c062aff48c6963df200a2466023a2be;hpb=f13173726f8382eef380f1e754f24dd2b126a77b;p=sfa.git diff --git a/sfa/util/method.py b/sfa/util/method.py index e7a12d86..7329036e 100644 --- a/sfa/util/method.py +++ b/sfa/util/method.py @@ -1,29 +1,20 @@ # -# Base class for all GeniAPI functions +# Base class for all SfaAPI functions # # -### $Id$ -### $URL$ - -import xmlrpclib -from types import * -import textwrap -import os import time -import pprint +import textwrap + +from sfa.util.sfalogging import logger +from sfa.util.faults import SfaFault, SfaInvalidAPIMethod, SfaInvalidArgumentCount, SfaInvalidArgument -from types import StringTypes +from sfa.storage.parameter import Parameter, Mixed, python_type, xmlrpc_type -from sfa.util.faults import * -from sfa.util.parameter import Parameter, Mixed, python_type, xmlrpc_type -from sfa.util.auth import Auth -from sfa.util.debug import profile, log -# we inherit object because we use new-style classes for legacy methods -class Method (object): +class Method: """ - Base class for all GeniAPI functions. At a minimum, all GeniAPI + Base class for all SfaAPI functions. At a minimum, all SfaAPI functions must define: interfaces = [allowed interfaces] @@ -48,11 +39,9 @@ class Method (object): def call(self, *args): """ - Method body for all GeniAPI functions. Must override. - + Method body for all SfaAPI functions. Must override. """ - - return True + return None def __init__(self, api): self.name = self.__class__.__name__ @@ -64,10 +53,10 @@ class Method (object): # API may set this to a (addr, port) tuple if known self.source = None - + def __call__(self, *args, **kwds): """ - Main entry point for all GeniAPI functions. Type checks + Main entry point for all SFA API functions. Type checks arguments, authenticates, and executes call(). """ @@ -75,55 +64,51 @@ class Method (object): start = time.time() methodname = self.name if not self.api.interface or self.api.interface not in self.interfaces: - raise GeniInvalidAPIMethod, methodname, self.api.interface + raise SfaInvalidAPIMethod(methodname, self.api.interface) - # legacy code cannot be type-checked, due to the way Method.args() works - if not hasattr(self,"skip_typecheck"): - (min_args, max_args, defaults) = self.args() - - # Check that the right number of arguments were passed in - if len(args) < len(min_args) or len(args) > len(max_args): - raise GeniInvalidArgumentCount(len(args), len(min_args), len(max_args)) + (min_args, max_args, defaults) = self.args() - for name, value, expected in zip(max_args, args, self.accepts): - self.type_check(name, value, expected, args) + # Check that the right number of arguments were passed in + if len(args) < len(min_args) or len(args) > len(max_args): + raise SfaInvalidArgumentCount( + len(args), len(min_args), len(max_args)) + for name, value, expected in zip(max_args, args, self.accepts): + self.type_check(name, value, expected, args) + + logger.debug("method.__call__ [%s] : BEG %s" % ( + self.api.interface, methodname)) result = self.call(*args, **kwds) - runtime = time.time() - start - if self.api.config.GENI_API_DEBUG or hasattr(self, 'message'): - # XX print to some log file - # print >> log, "some output" - pass + runtime = time.time() - start + logger.debug("method.__call__ [%s] : END %s in %02f s (%s)" % + (self.api.interface, methodname, runtime, getattr(self, 'message', "[no-msg]"))) return result - except GeniFault, fault: + except SfaFault as fault: caller = "" # Prepend caller and method name to expected faults - fault.faultString = caller + ": " + self.name + ": " + fault.faultString + fault.faultString = caller + ": " + self.name + ": " + fault.faultString runtime = time.time() - start - - if self.api.config.GENI_API_DEBUG: - # XX print to some log file - #print >> log, "Some debugging output" - pass + logger.log_exc("Method %s raised an exception" % self.name) raise fault - - def help(self, indent = " "): + def help(self, indent=" "): """ Text documentation for the method. """ (min_args, max_args, defaults) = self.args() - text = "%s(%s) -> %s\n\n" % (self.name, ", ".join(max_args), xmlrpc_type(self.returns)) + text = "%s(%s) -> %s\n\n" % (self.name, + ", ".join(max_args), xmlrpc_type(self.returns)) text += "Description:\n\n" - lines = [indent + line.strip() for line in self.__doc__.strip().split("\n")] + lines = [indent + line.strip() + for line in self.__doc__.strip().split("\n")] text += "\n".join(lines) + "\n\n" def param_text(name, param, indent, step): @@ -146,9 +131,9 @@ class Method (object): # Print parameter documentation right below type if isinstance(param, Parameter): - wrapper = textwrap.TextWrapper(width = 70, - initial_indent = " " * param_offset, - subsequent_indent = " " * param_offset) + wrapper = textwrap.TextWrapper(width=70, + initial_indent=" " * param_offset, + subsequent_indent=" " * param_offset) text += "\n".join(wrapper.wrap(param.doc)) + "\n" param = param.type @@ -156,7 +141,7 @@ class Method (object): # Indent struct fields and mixed types if isinstance(param, dict): - for name, subparam in param.iteritems(): + for name, subparam in param.items(): text += param_text(name, subparam, indent + step, step) elif isinstance(param, Mixed): for subparam in param: @@ -187,16 +172,17 @@ class Method (object): That represents the minimum and maximum sets of arguments that this function accepts and the defaults for the optional arguments. """ - + # Inspect call. Remove self from the argument list. - max_args = self.call.func_code.co_varnames[1:self.call.func_code.co_argcount] - defaults = self.call.func_defaults + max_args = self.call.__code__.co_varnames[ + 1:self.call.__code__.co_argcount] + defaults = self.call.__defaults__ if defaults is None: defaults = () min_args = max_args[0:len(max_args) - len(defaults)] defaults = tuple([None for arg in min_args]) + defaults - + return (min_args, max_args, defaults) def type_check(self, name, value, expected, args): @@ -205,7 +191,7 @@ class Method (object): which may be a Python type, a typed value, a Parameter, a Mixed type, or a list or dictionary of possibly mixed types, values, Parameters, or Mixed types. - + Extraneous members of lists must be of the same type as the last specified type. For example, if the expected argument type is [int, bool], then [1, False] and [14, True, False, @@ -221,15 +207,15 @@ class Method (object): try: self.type_check(name, value, item, args) return - except GeniInvalidArgument, fault: + except SfaInvalidArgument as fault: pass raise fault # If an authentication structure is expected, save it and # authenticate after basic type checking is done. - #if isinstance(expected, Auth): + # if isinstance(expected, Auth): # auth = expected - #else: + # else: # auth = None # Get actual expected type from within the Parameter structure @@ -251,38 +237,42 @@ class Method (object): # Strings are a special case. Accept either unicode or str # types if a string is expected. - if expected_type in StringTypes and isinstance(value, StringTypes): + if issubclass(expected_type, str) and isinstance(value, str): pass # Integers and long integers are also special types. Accept # either int or long types if an int or long is expected. - elif expected_type in (IntType, LongType) and isinstance(value, (IntType, LongType)): + elif expected_type is int and isinstance(value, int): pass elif not isinstance(value, expected_type): - raise GeniInvalidArgument("expected %s, got %s" % \ + raise SfaInvalidArgument("expected %s, got %s" % (xmlrpc_type(expected_type), xmlrpc_type(type(value))), name) # If a minimum or maximum (length, value) has been specified - if expected_type in StringTypes: + if issubclass(expected_type, str): if min is not None and \ len(value.encode(self.api.encoding)) < min: - raise GeniInvalidArgument, "%s must be at least %d bytes long" % (name, min) + raise SfaInvalidArgument( + "%s must be at least %d bytes long" % (name, min)) if max is not None and \ len(value.encode(self.api.encoding)) > max: - raise GeniInvalidArgument, "%s must be at most %d bytes long" % (name, max) + raise SfaInvalidArgument( + "%s must be at most %d bytes long" % (name, max)) elif expected_type in (list, tuple, set): if min is not None and len(value) < min: - raise GeniInvalidArgument, "%s must contain at least %d items" % (name, min) + raise SfaInvalidArgument( + "%s must contain at least %d items" % (name, min)) if max is not None and len(value) > max: - raise GeniInvalidArgument, "%s must contain at most %d items" % (name, max) + raise SfaInvalidArgument( + "%s must contain at most %d items" % (name, max)) else: if min is not None and value < min: - raise GeniInvalidArgument, "%s must be > %s" % (name, str(min)) + raise SfaInvalidArgument("%s must be > %s" % (name, str(min))) if max is not None and value > max: - raise GeniInvalidArgument, "%s must be < %s" % (name, str(max)) + raise SfaInvalidArgument("%s must be < %s" % (name, str(max))) # If a list with particular types of items is expected if isinstance(expected, (list, tuple, set)): @@ -296,14 +286,15 @@ class Method (object): # If a struct with particular (or required) types of items is # expected. elif isinstance(expected, dict): - for key in value.keys(): + for key in list(value.keys()): if key in expected: - self.type_check(name + "['%s']" % key, value[key], expected[key], args) - for key, subparam in expected.iteritems(): + self.type_check(name + "['%s']" % + key, value[key], expected[key], args) + for key, subparam in expected.items(): if isinstance(subparam, Parameter) and \ subparam.optional is not None and \ - not subparam.optional and key not in value.keys(): - raise GeniInvalidArgument("'%s' not specified" % key, name) + not subparam.optional and key not in list(value.keys()): + raise SfaInvalidArgument("'%s' not specified" % key, name) - #if auth is not None: + # if auth is not None: # auth.check(self, *args)