@@ -196,8 +196,11 @@ def set_content_type_header(self, session):
196196 def encode (self , data ):
197197 return ElementTree .tostring (data )
198198
199- class RESTService (object ):
199+ class _RESTService (object ):
200200 """
201+ The _RESTService base class provides most of the implementation for
202+ RESTService. The RESTService subclass adds some parameter validation.
203+
201204 Implements a simple wrapper for calling a REST interface that returns
202205 either llsd or json, with optional authentication.
203206
@@ -230,6 +233,11 @@ class RESTService(object):
230233 undesired dependency between that module and the calling script.
231234 """
232235
236+ # describe __init__() params; used by _resolve_args()
237+ init_params = ('name' , 'baseurl' , 'codec' , 'authenticated' ,
238+ 'username' , 'password' , 'proxy_hostport' , 'cert' , 'basepath' ,
239+ 'cookie_policy' )
240+
233241 def __init__ (self , name , baseurl , codec = RESTEncoding .LLSD , authenticated = True ,
234242 username = None , password = None , proxy_hostport = None , cert = None , basepath = '' ,
235243 cookie_policy = None , ** session_params ):
@@ -382,15 +390,6 @@ def _url(self, basepath, path, method, path_param='path', basepath_param='basepa
382390
383391 basepath_param is the name of method's parameter passed to us as basepath.
384392 """
385- # Since caller accepts both 'basepath' and 'path', both are typically
386- # optional arguments -- though 'path' is usually first so its caller
387- # need not explicitly pass it as a keyword argument. But its caller
388- # MUST pass one or the other.
389- if not (basepath or path ):
390- # When you fail to pass a non-optional parameter, the interpreter
391- # raises TypeError. Treat this similarly.
392- raise TypeError ("{}() requires either {} or {}"
393- .format (method , path_param , basepath_param ))
394393 # if self.baseurl is None, use empty string instead
395394 baseurl = self .baseurl or ""
396395 if basepath :
@@ -667,6 +666,26 @@ def temp_codec(self, codec):
667666 finally :
668667 self .set_codec (prev )
669668
669+ class RESTService (_RESTService ):
670+ # We want the following validation for a consumer-instantiated
671+ # RESTService, just not for _RESTService instances implicitly constructed
672+ # by _resolve_args()
673+ def _url (self , basepath , path , method , path_param = 'path' , basepath_param = 'basepath' ):
674+ # Since caller accepts both 'basepath' and 'path', both are typically
675+ # optional arguments -- though 'path' is usually first so its caller
676+ # need not explicitly pass it as a keyword argument. But its caller
677+ # MUST pass one or the other.
678+ if not (basepath or path ):
679+ # When you fail to pass a non-optional parameter, the interpreter
680+ # raises TypeError. Treat this similarly.
681+ raise TypeError ("{}() requires either {} or {}"
682+ .format (method , path_param , basepath_param ))
683+
684+ # we have one or the other, pass to base-class method
685+ return super (RESTService , self )._url (
686+ basepath = basepath , path = path , method = method ,
687+ path_param = path_param , basepath_param = basepath_param )
688+
670689class _OldTLS (requests .adapters .HTTPAdapter ):
671690 """
672691 Helper for RESTService.enable_old_tls(), derived from
@@ -709,3 +728,72 @@ class SimpleRESTService(RESTService):
709728 """
710729 def __init__ (self , name , baseurl , * args , ** kwds ):
711730 RESTService .__init__ (self , name , baseurl , authenticated = False , * args , ** kwds )
731+
732+
733+ # convenience functions for simple one-shot use cases
734+ def get (url , ** kwds ):
735+ """
736+ url is the target URL; everything else must be keyword arguments
737+
738+ RESTService.__init__() keywords are passed to RESTService constructor;
739+ the rest are passed to requests.get()
740+ """
741+ svc , kwds = _resolve_args ('get' , url , kwds )
742+ return svc .get (query = '' , basepath = '' , ** kwds )
743+
744+ def post (url , ** kwds ):
745+ """
746+ url is the target URL; everything else must be keyword arguments
747+
748+ RESTService.__init__() keywords are passed to RESTService constructor;
749+ data (if passed) is encoded according to the RESTService codec;
750+ the rest are passed to requests.post()
751+ """
752+ svc , kwds = _resolve_args ('post' , url , kwds )
753+ return svc .post (path = '' , basepath = '' , ** kwds )
754+
755+ def put (url , ** kwds ):
756+ """
757+ url is the target URL; everything else must be keyword arguments
758+
759+ RESTService.__init__() keywords are passed to RESTService constructor;
760+ data (if passed) is encoded according to the RESTService codec;
761+ the rest are passed to requests.put()
762+ """
763+ svc , kwds = _resolve_args ('put' , url , kwds )
764+ return svc .put (path = '' , basepath = '' , ** kwds )
765+
766+ def delete (url , ** kwds ):
767+ """
768+ url is the target URL; everything else must be keyword arguments
769+
770+ RESTService.__init__() keywords are passed to RESTService constructor;
771+ the rest are passed to requests.delete()
772+ """
773+ svc , kwds = _resolve_args ('delete' , url , kwds )
774+ return svc .delete (path = '' , basepath = '' , ** kwds )
775+
776+ def _resolve_args (func , url , kwds ):
777+ """
778+ Given a dict of **kwds, split them into _RESTService constructor params
779+ versus anything else, which we assume to be extra keywords to pass to
780+ 'func'.
781+
782+ Return (_RESTService instance, remaining kwds).
783+ """
784+ # _RESTService.init_params includes the first two parameters, name and
785+ # baseurl, to simplify maintenance. But since we pass those explicitly
786+ # here, remove from the set of keywords we recognize.
787+ init_params = set (_RESTService .init_params [2 :])
788+ init_kwds = {}
789+ func_kwds = {}
790+ for key , value in kwds .items ():
791+ if key in init_params :
792+ dest_kwds = init_kwds
793+ else :
794+ dest_kwds = func_kwds
795+ dest_kwds [key ] = value
796+ return (_RESTService (name = 'temp ' + func , baseurl = url , ** init_kwds ),
797+ func_kwds )
798+
799+
0 commit comments