From: Thierry Parmentelat Date: Tue, 17 Jun 2008 13:25:53 +0000 (+0000) Subject: legacy methods - this is more an excuse for studying how the API can be X-Git-Tag: PLCAPI-4.3-1~27 X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;h=8be4547f4c0fee6209d65fcdd759b6ff07e83013;p=plcapi.git legacy methods - this is more an excuse for studying how the API can be reorganized into several parts --- diff --git a/PLC/API.py b/PLC/API.py index 7fcfa094..5e97ebf1 100644 --- a/PLC/API.py +++ b/PLC/API.py @@ -83,10 +83,28 @@ except ImportError: from PLC.Config import Config from PLC.Faults import * import PLC.Methods +import PLC.Legacy + +def import_deep(name): + mod = __import__(name) + components = name.split('.') + for comp in components[1:]: + mod = getattr(mod, comp) + return mod class PLCAPI: + + # flat list of method names methods = PLC.Methods.methods + # dict {methodname:module} + legacy_map={} + for module in PLC.Legacy.__all__ : + for method in getattr(import_deep("PLC.Legacy."+module),"methods"): + legacy_map[method]=module + + all_methods = methods+legacy_map.keys() + def __init__(self, config = "/etc/planetlab/plc_config", encoding = "utf-8"): self.encoding = encoding @@ -111,14 +129,19 @@ class PLCAPI: """ # Look up method - if method not in self.methods: + if method not in self.all_methods: raise PLCInvalidAPIMethod, method - + # Get new instance of method try: classname = method.split(".")[-1] - module = __import__("PLC.Methods." + method, globals(), locals(), [classname]) - return getattr(module, classname)(self) + if method in self.methods: + module = __import__("PLC.Methods." + method, globals(), locals(), [classname]) + return getattr(module, classname)(self) + else: + modulename=self.legacy_map[method] + module = __import__("PLC.Legacy." + modulename, globals(), locals(), [classname]) + return getattr(module, classname)(self) except ImportError, AttributeError: raise PLCInvalidAPIMethod, method diff --git a/PLC/Legacy/NodeNetworks.py b/PLC/Legacy/NodeNetworks.py new file mode 100644 index 00000000..7e805e50 --- /dev/null +++ b/PLC/Legacy/NodeNetworks.py @@ -0,0 +1,78 @@ +# Thierry Parmentelat - INRIA +# $Id$ + +from PLC.Method import Method + +def import_deep(name): + mod = __import__(name) + components = name.split('.') + for comp in components[1:]: + mod = getattr(mod, comp) + return mod + +methods = [ + "AddNodeNetwork", + "AddNodeNetworkSetting", + "DeleteNodeNetwork", + "DeleteNodeNetworkSetting", + "GetNodeNetworkSettings", + "GetNodeNetworks", + "UpdateNodeNetwork", + "UpdateNodeNetworkSetting", +] + +# does any required renaming +def rename (x): + if x=='nodenetwork_id': + return 'interface_id' + if x=='nodenetwork_ids': + return 'interface_ids' + else: + return x + +# apply rename on list (columns) or dict (filter) args +def patch_legacy_arg (arg): + if isinstance(arg,list): + return [rename(x) for x in arg] + if isinstance(arg,dict): + return dict ( [ (rename(k),v) for (k,v) in arg.iteritems() ] ) + return arg + +def factory (legacyname): + # new method name + newname=legacyname.replace("NodeNetwork","Interface") + # locate new class + newclass=getattr(import_deep("PLC.Methods."+newname),newname) + # create class for legacy name + legacyclass = type(legacyname,(newclass,), + {"__doc__":"Legacy method - please use %s instead"%newname}) + # xxx should rewrite 'call' to handle any argument using nodenetwork_id(s) + for internal in ["roles","accepts","returns"]: + setattr(legacyclass,internal,getattr(newclass,internal)) + # turn off type checking, as introspection code fails on wrapped_call + setattr(legacyclass,"skip_typecheck",True) + # rewrite call + def wrapped_call (self,auth,*args, **kwds): + newargs=[patch_legacy_arg(x) for x in args] + newkwds=dict ( [ (k,patch_legacy_arg(v)) for (k,v) in kwds.iteritems() ] ) + return getattr(newclass,"call")(self,auth,*newargs,**newkwds) + setattr(legacyclass,"call",wrapped_call) + + return legacyclass + +# this does not work, as __module__ is not yet supported +#for legacyname in methods: +# setattr(__module__,legacyname,factory(legacyname)) +# this does not work either, as we are importing the module so we cannot locate it yet +#for legacyname in methods: +# this_module = import_deep(__name__) +# setattr(__module__,legacyname,factory(legacyname)) + +AddNodeNetwork=factory("AddNodeNetwork") +AddNodeNetworkSetting=factory("AddNodeNetworkSetting") +DeleteNodeNetwork=factory("DeleteNodeNetwork") +DeleteNodeNetworkSetting=factory("DeleteNodeNetworkSetting") +GetNodeNetworkSettings=factory("GetNodeNetworkSettings") +GetNodeNetworks=factory("GetNodeNetworks") +UpdateNodeNetwork=factory("UpdateNodeNetwork") +UpdateNodeNetworkSetting=factory("UpdateNodeNetworkSetting") diff --git a/PLC/Legacy/Types.py b/PLC/Legacy/Types.py new file mode 100644 index 00000000..71b23096 --- /dev/null +++ b/PLC/Legacy/Types.py @@ -0,0 +1,69 @@ +# Thierry Parmentelat - INRIA +# $Id$ + +from PLC.Method import Method + +def import_deep(name): + mod = __import__(name) + components = name.split('.') + for comp in components[1:]: + mod = getattr(mod, comp) + return mod + +map = { + "AddSliceAttributeType" : "AddTagType", + "DeleteSliceAttributeType" : "DeleteTagType", + "GetSliceAttributeTypes" : "GetTagTypes", + "UpdateSliceAttributeType" : "UpdateTagType", + "AddNodeNetworkSettingType" : "AddTagType", + "DeleteNodeNetworkSettingType" : "DeleteTagType", + "GetNodeNetworkSettingTypes" : "GetTagTypes", + "UpdateNodeNetworkSettingType" : "UpdateTagType", +} + +methods = map.keys() + +# does any required renaming +def rename (x): + if x=='name': + return 'tagname' + else: + return x + +# apply rename on list (columns) or dict (filter) args +def patch_legacy_arg (arg): + if isinstance(arg,list): + return [rename(x) for x in arg] + if isinstance(arg,dict): + return dict ( [ (rename(k),v) for (k,v) in arg.iteritems() ] ) + return arg + +def factory (legacyname, newname): + # locate new class + newclass=getattr(import_deep("PLC.Methods."+newname),newname) + # create class for legacy name + legacyclass = type(legacyname,(newclass,), + {"__doc__":"Legacy method - please use %s instead"%newname}) + for internal in ["roles","accepts","returns"]: + setattr(legacyclass,internal,getattr(newclass,internal)) + # turn off type checking, as introspection code fails on wrapped_call + setattr(legacyclass,"skip_typecheck",True) + # rewrite call + def wrapped_call (self,auth,*args, **kwds): + newargs=[patch_legacy_arg(x) for x in args] + newkwds=dict ( [ (k,patch_legacy_arg(v)) for (k,v) in kwds.iteritems() ] ) + return getattr(newclass,"call")(self,auth,*newargs,**newkwds) + setattr(legacyclass,"call",wrapped_call) + + return legacyclass + +# see NodeNetworks for attempts to avoid this + +AddSliceAttributeType=factory("AddSliceAttributeType","AddTagType") +DeleteSliceAttributeType=factory("DeleteSliceAttributeType","DeleteTagType") +GetSliceAttributeTypes=factory("GetSliceAttributeTypes","GetTagTypes") +UpdateSliceAttributeType=factory("UpdateSliceAttributeType","UpdateTagType") +AddNodeNetworkSettingType=factory("AddNodeNetworkSettingType","AddTagType") +DeleteNodeNetworkSettingType=factory("DeleteNodeNetworkSettingType","DeleteTagType") +GetNodeNetworkSettingTypes=factory("GetNodeNetworkSettingTypes","GetTagTypes") +UpdateNodeNetworkSettingType=factory("UpdateNodeNetworkSettingType","UpdateTagType") diff --git a/PLC/Legacy/__init__.py b/PLC/Legacy/__init__.py new file mode 100644 index 00000000..64896ac0 --- /dev/null +++ b/PLC/Legacy/__init__.py @@ -0,0 +1,11 @@ +# supposed to be managed manually +# syntax is modulename.methodname + +# xxx could we use __all__ and have each module define its +# own set of methods ? + +__all__ = """ +NodeNetworks +Types +""".split() + diff --git a/PLC/Method.py b/PLC/Method.py index 71977e60..a7f070ef 100644 --- a/PLC/Method.py +++ b/PLC/Method.py @@ -24,7 +24,8 @@ from PLC.Events import Event, Events from PLC.Nodes import Node, Nodes from PLC.Persons import Person, Persons -class Method: +# we inherit object because we use new-style classes for legacy methods +class Method (object): """ Base class for all PLCAPI functions. At a minimum, all PLCAPI functions must define: @@ -80,14 +81,17 @@ class Method: try: start = time.time() - (min_args, max_args, defaults) = self.args() + + # 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 PLCInvalidArgumentCount(len(args), len(min_args), len(max_args)) + # Check that the right number of arguments were passed in + if len(args) < len(min_args) or len(args) > len(max_args): + raise PLCInvalidArgumentCount(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) + for name, value, expected in zip(max_args, args, self.accepts): + self.type_check(name, value, expected, args) result = self.call(*args, **kwds) runtime = time.time() - start @@ -253,7 +257,7 @@ class Method: 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 diff --git a/PLC/Shell.py b/PLC/Shell.py index 6e4a77b2..3ed0a75a 100644 --- a/PLC/Shell.py +++ b/PLC/Shell.py @@ -165,7 +165,7 @@ class Shell: if role is not None: self.auth['Role'] = role - for method in PLC.Methods.methods: + for method in PLC.Methods.methods + PLC.API.PLCAPI.legacy_map.keys(): api_function = self.api.callable(method) if self.server is None: