123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856(* Python generator *)openCodegenopenRpc.Typestypet=|Blockoftlist|Lineofstringletinline_defaults={|
def success(result):
"""
Called from each server dispatcher to return a successful response in case
no exceptions were thrown.
"""
return {"Status": "Success", "Value": result}
def handle_exception(exn):
"""
Defines the behavior of the generated CLI code when arguments are passed as
JSON and the dispatched function fails with an exception.
"""
raise exn
class Rpc_light_failure(Exception):
"""Each error variant defined in the interface is a subclass of this exception."""
def __init__(self, name, args):
super(Rpc_light_failure, self).__init__(
"Rpc_light_failure: {} ({})".format(name, args))
self.name = name
self.args = args
def failure(self):
"""Returned from the top-level server dispatcher to indicate a failed RPC call."""
# rpc-light marshals a single result differently to a list of results
args = list(self.args)
marshalled_args = args
if len(args) == 1:
marshalled_args = args[0]
return {'Status': 'Failure',
'ErrorDescription': [self.name, marshalled_args]}
class Unimplemented(Rpc_light_failure):
"""The called RPC method is not implemented."""
def __init__(self, arg):
super(Unimplemented, self).__init__("Unimplemented", [arg])
class InternalError(Rpc_light_failure):
"""
Indicates either an error in the generated bindings to dispatch the RPC
call, or an unknown exception raised by the implementation that is not
declared in the interface.
"""
def __init__(self, error):
super(InternalError, self).__init__("Internal_error", [error])
class UnmarshalException(InternalError):
"""The input does not have the expected structure."""
def __init__(self, thing, ty, desc):
super(UnmarshalException, self).__init__(
"UnmarshalException thing=%s ty=%s desc=%s" % (thing, ty, desc))
class TypeError(InternalError):
"""The type of an argument differs from the one defined in the interface."""
def __init__(self, expected, actual):
super(TypeError, self).__init__(
"TypeError expected=%s actual=%s" % (expected, actual))
class UnknownMethod(InternalError):
"""The called method is not defined in the interface."""
def __init__(self, name):
super(UnknownMethod, self).__init__("Unknown method %s" % name)
class ListAction(argparse.Action):
"""
Custom argparse action for specifying dictionaries on the command line.
The syntax is <prog> --dict_name key1 value1 --dict_name key2 value2 ...
"""
def __call__(self, parser, namespace, values, option_string=None):
key = values[0]
value = values[1]
if (hasattr(namespace, self.dest) and
getattr(namespace, self.dest) is not None):
getattr(namespace, self.dest)[key] = value
else:
setattr(namespace, self.dest, {key: value})|}letcompat_block=[Line"if sys.version_info[0] > 2:";Block[Line"long = int";Line"unicode = str";Line"str = bytes"];Line""]letreserved_exns=["Rpc_light_failure";"Unimplemented";"InternalError";"UnmarshalException";"TypeError";"UnknownMethod"]letreclines_of_tt=letindent=String.make4' 'inmatchtwith|Linex->[x]|Blockxs->letall=List.concat(List.maplines_of_txs)inList.map(function|""->""|x->indent^x)allletstring_of_tsts=String.concat"\n"(List.concat(List.maplines_of_tts))letoutput_docdescription=letprocessdescr=descr|>String.split_on_char'\n'|>List.map(functionl->Line(String.triml))inletdescr_lines=description|>List.mapprocess|>List.concat|>function|Line""::tl->tl|lst->lstin[Line{|"""|}]@descr_lines@[Line{|"""|}](* generate a fresh id *)letfresh_id=letcounter=ref5infun()->incrcounter;"tmp_"^string_of_int!counter(** [typecheck ty v] returns a python fragment which checks
[v] has type [ty] *)letrectypecheck:typea.atyp->string->tlist=funtyv->letopenPrintfinletraise_type_error=Line(sprintf{|raise TypeError("%s", repr(%s))|}(Rpcmarshal.ocaml_of_tty)v)inlethandle_basicb=letpython_of_basic:typea.abasic->string=function|Int64->"(int, long)"|Int32->"int"|Int->"(int, long)"|Char->"(str, unicode)"|String->"(str, unicode)"|Float->"float"|Bool->"bool"in[Line(sprintf"if not isinstance(%s, %s):"v(python_of_basicb));Block[raise_type_error]]inmatchtywith|BasicInt64->handle_basicInt64|BasicString->handle_basicString|BasicInt32->handle_basicInt32|BasicInt->handle_basicInt|BasicBool->handle_basicBool|BasicFloat->handle_basicFloat|BasicChar->handle_basicChar|DateTime->handle_basicString|Base64->handle_basicString|Struct{fields;_}->letcheckboxedfield=let(BoxedFieldf)=boxedfieldinletmember=sprintf"%s['%s']"vf.fnameinmatchf.fieldwith|Optionty->[Line(sprintf"if '%s' in %s:"f.fnamev);Block(typechecktymember)]|_->typecheckf.fieldmemberinList.concat(List.rev(List.mapcheck(List.revfields)))|Variant{variants;_}->letcheckfirstboxed_tag=let(BoxedTagt)=boxed_taginmatcht.tcontentswith|Unit->failwith"Can't happen: this has been filtered out"|ty->[Line(sprintf"%s %s[0] == '%s':"(iffirstthen"if"else"elif")vt.tname);Block(typecheckty(sprintf"%s[1]"v))]inletvariants_to_check=List.filter(fun(BoxedTagt)->matcht.tcontentswith|Unit->false|_->true)variantsinletcheck_contents=List.fold_left(funaccx->List.concat[acc;checkfalsex])(checktrue(List.hdvariants_to_check))(List.tlvariants_to_check)inletall_tags=List.map(fun(BoxedTagt)->t.tname)variantsinletpylist=sprintf"[%s]"(String.concat", "(List.map(funs->sprintf{|"%s"|}s)all_tags))in[Line(sprintf"if %s[0] not in %s:"vpylist);Block[raise_type_error]]@check_contents|Arrayt->letid=fresh_id()in[Line(sprintf"if not isinstance(%s, list):"v);Block[raise_type_error];Line(sprintf"for %s in %s:"idv);Block(typechecktid)]|Listt->letid=fresh_id()in[Line(sprintf"if not isinstance(%s, list):"v);Block[raise_type_error];Line(sprintf"for %s in %s:"idv);Block(typechecktid)]|Dict(key,va)->letid_k=fresh_id()inletid_v=fresh_id()in[Line(sprintf"if not isinstance(%s, dict):"v);Block[raise_type_error];Line(sprintf"for %s, %s in %s.items():"id_kid_vv);Block(typecheck(Basickey)id_k);Block(typecheckvaid_v)]|Unit->[Line(sprintf"if %s is not None:"v);Block[raise_type_error]]|Optiont->[Line(sprintf"if %s is not None:"v);Block(typechecktv)]|Tuple(a,b)->[Line(sprintf"if not (isinstance(%s, tuple) and len(%s) == 2):"vv);Block[raise_type_error];Line(sprintf"v1, v2 = %s"v)]@typechecka(Printf.sprintf"v1")@typecheckb(Printf.sprintf"v2")|Tuple3(a,b,c)->[Line(sprintf"if not (isinstance(%s, tuple) and len(%s) == 3):"vv);Block[raise_type_error];Line(sprintf"v1, v2, v3 = %s"v)]@typechecka(Printf.sprintf"v1")@typecheckb(Printf.sprintf"v2")@typecheckc(Printf.sprintf"v3")|Tuple4(a,b,c,d)->[Line(sprintf"if not (isinstance(%s, tuple) and len(%s) == 4):"vv);Block[raise_type_error];Line(sprintf"v1, v2, v3, v4 = %s"v)]@typechecka(Printf.sprintf"v1")@typecheckb(Printf.sprintf"v2")@typecheckc(Printf.sprintf"v3")@typecheckd(Printf.sprintf"v4")|Abstract_->failwith"Abstract types cannot be typechecked by pythongen"letrecvalue_of:typea.atyp->string=letopenPrintfinfunction|BasicInt64->"long(0)"|BasicInt->"long(0)"|BasicInt32->"0"|BasicChar->"'c'"|BasicString->{|"string"|}|BasicFloat->"1.1"|BasicBool->"True"|DateTime->{|"19700101T00:00:00Z"|}|Base64->{|"SGVsbG8sIHdvcmxkIQ=="|}|Struct{fields;_}->letmemberboxed_field=let(BoxedFieldf)=boxed_fieldinsprintf{|"%s": %s|}f.fname(value_off.field)insprintf"{%s}"(String.concat", "(List.mapmemberfields))|Variant_->"None"|Arrayt->sprintf"[%s]"(value_oft)|Listt->sprintf"[%s]"(value_oft)|Dict(key,va)->sprintf"{%s: %s}"(value_of(Basickey))(value_ofva)|Unit->"None"|Option_->"None"|Tuple_->"[]"|Tuple3_->"[]"|Tuple4_->"[]"|Abstract_->failwith"Cannot get default value for abstract types"letexn_varmyarg=letopenPrintfinletinner:typeab.(a,b)tag->tlist=function|tag->lethas_arg=matchtag.tcontentswith|Unit->false|_->truein(* Avoid generating a duplicate version of an existing exception defined in
the helpers *)ifList.memtag.tnamereserved_exnsthen[]else[Line"";Line"";Line(sprintf"class %s(Rpc_light_failure):"tag.tname);Block(output_doctag.tdescription@[Line""]@ifnothas_argthen[Line"def __init__(self)";Block[Line(sprintf{|super(%s, self).__init__("%s", [])|}tag.tnametag.tname)]]else[Line(sprintf"def __init__(self, arg_0):");Block([Line(sprintf{|super(%s, self).__init__("%s", [arg_0])|}tag.tnametag.tname)]@typechecktag.tcontents"arg_0"@[Line"self.arg_0 = arg_0"])])]inmatchmyargwith|BoxedDef{ty=Variant{variants;_};_}->List.concat(List.map(fun(BoxedTagt)->innert)variants)|BoxedDef{ty;_}->failwith(Printf.sprintf"Unable to handle non-variant exceptions (%s)"(Rpcmarshal.ocaml_of_tty))letskeleton_methodunimplementedi(BoxedFunctionm)=letinputs=Method.(find_inputsm.ty)inletoutput=Method.(find_outputm.ty)inletinputs=List.filter(function|Idl.Param.Boxed{Idl.Param.typedef;_}->(matchtypedef.tywith|Unit->false|_->true))inputsinletopenPrintfinletoutput_py(Idl.Param.Boxeda)=letvalue=value_ofa.Idl.Param.typedef.tyinmatcha.Idl.Param.typedef.ty,a.Idl.Param.namewith|Unit,_->[]|_,_->[Line(sprintf"return %s"value)]in[Line"";Line(sprintf"def %s(self%s):"m.Method.name(String.concat""(List.map(funx->", "^x)(List.map(fun(Idl.Param.Boxedx)->matchx.Idl.Param.namewith|Somen->n|None->failwith(Printf.sprintf"Parameter names required for python generation (%s)"m.Method.name))inputs))));Block(output_docm.Method.description@ifunimplementedthen[Line(sprintf{|raise Unimplemented("%s.%s")|}i.Interface.details.Idl.Interface.namem.Method.name)]elseoutput_pyoutput)]letexample_stub_useri(BoxedFunctionm)=letopenPrintfin[Line"";Line"# import necessary libraries if needed";Line"# we assume that your library providing the client is called myclient and it \
provides a connect method";Line"import myclient";Line"";Line{|if __name__ == "__main__":|};Block[Line"c = myclient.connect()";Line(Printf.sprintf"results = c.%s.%s({ %s })"i.Interface.details.Idl.Interface.namem.Method.name(String.concat", "(List.map(fun(Idl.Param.Boxeda)->sprintf"%s: %s"(matcha.Idl.Param.namewith|Somex->x|None->failwith(Printf.sprintf"Parameter names required for python generation (%s)"m.Method.name))(value_ofa.Idl.Param.typedef.ty))Method.(find_inputsm.ty))));Line"print(repr(results))"]]letexample_skeleton_userim=letopenPrintfin[Line"";Line"# import additional libraries if needed";Line"";Line(sprintf"class %s_myimplementation(%s_skeleton):"i.Interface.details.Idl.Interface.namei.Interface.details.Idl.Interface.name);Block([Line"# by default each method will return a Not_implemented error";Line"# ..."]@skeleton_methodfalseim@[Line"# ..."])]letskeleton_of_interfaceunimplementedsuffixi=letopenPrintfin[Line"";Line"";Line(sprintf"class %s_%s(object):"i.Interface.details.Idl.Interface.namesuffix);Block(output_doci.Interface.details.Idl.Interface.description@[Line"";Line"def __init__(self):";Block[Line"pass"]]@List.concat(List.map(skeleton_methodunimplementedi)i.Interface.methods))]lettest_impl_of_interface=skeleton_of_interfacefalse"test"letskeleton_of_interface=skeleton_of_interfacetrue"skeleton"letserver_of_interfacei=letopenPrintfinlettypecheck_method_wrapper(BoxedFunctionm)=letinputs=Method.(find_inputsm.ty)inletinputs=List.filter(function|Idl.Param.Boxed{Idl.Param.typedef;_}->(matchtypedef.tywith|Unit->false|_->true))inputsinletoutput=Method.(find_outputm.ty)inletextract_input(Idl.Param.Boxedarg)=letarg_name=matcharg.Idl.Param.namewith|Somex->x|None->failwith(Printf.sprintf"Parameter names requred for python generation (%s)"m.Method.name)in[Line(sprintf{|if "%s" not in args:|}arg_name);Block[Line(sprintf"raise UnmarshalException('argument missing', '%s', '')"arg_name)];Line(sprintf{|%s = args["%s"]|}arg_namearg_name)]@typecheckarg.Idl.Param.typedef.tyarg_nameinletcheck_output(Idl.Param.Boxedarg)=matcharg.Idl.Param.typedef.tywith|Unit->[]|_->(* The ocaml rpc-light doesn't actually support named results, instead we
have single anonymous results only. *)typecheckarg.Idl.Param.typedef.ty"results"in[Line"";Line(sprintf"def %s(self, args):"m.Method.name);Block([Line{|"""type-check inputs, call implementation, type-check outputs and return"""|};Line"if not isinstance(args, dict):";Block[Line"raise UnmarshalException('arguments', 'dict', repr(args))"]]@List.concat(List.mapextract_inputinputs)@[Line(sprintf"results = self._impl.%s(%s)"m.Method.name(String.concat", "(List.map(fun(Idl.Param.Boxedx)->matchx.Idl.Param.namewith|Somen->n|None->failwith"Parameter names required for python generation")inputs)))]@List.concat(List.mapcheck_output[output])@[Line"return results"])]inletdispatch_method(BoxedFunctionm)comma=Line(sprintf{| "%s.%s": self.%s%s|}i.Interface.details.Idl.Interface.namem.Method.namem.Method.namecomma)inletdispatch_dictmethods=letrecintersperse_commas(list:Codegen.boxed_fnlist)=matchlistwith|[]->[]|[x]->[dispatch_methodx""]|x::y::tl->dispatch_methodx","::intersperse_commas(y::tl)in[Line"self._dispatcher_dict = {"]@intersperse_commasmethods@[Line"}"]in[Line"";Line"";Line(sprintf"class %s_server_dispatcher(object):"i.Interface.details.Idl.Interface.name);Block(output_doci.Interface.details.Idl.Interface.description@[Line"";Line"def __init__(self, impl):";Block([Line{|"""impl is a proxy object whose methods contain the implementation"""|};Line"self._impl = impl"]@dispatch_dicti.Interface.methods)]@List.concat(List.maptypecheck_method_wrapperi.Interface.methods)@[Line"";Line"def _dispatch(self, method, params):";Block[Line{|"""type check inputs, call implementation, type check outputs and return"""|};Line"args = params[0]";Line"return success(self._dispatcher_dict[method](args))"]])]lettest_impl_of_interfacesi=letopenPrintfin[Line"";Line"";Line(sprintf"class %s_server_test(%s_server_dispatcher):"i.Interfaces.namei.Interfaces.name);Block[Line{|"""Create a server which will respond to all calls, returning arbitrary values. This is intended as a marshal/unmarshal test."""|};Line"";Line"def __init__(self):";Block[Line(sprintf"%s_server_dispatcher.__init__(self%s)"i.Interfaces.name(String.concat""(List.map(funi->sprintf", %s_server_dispatcher(%s_test())"i.Interface.details.Idl.Interface.namei.Interface.details.Idl.Interface.name)i.Interfaces.interfaces)))]]]letcommandline_parse_(BoxedFunctionm)=letopenPrintfinletinputs=Method.(find_inputsm.ty)inletinputs=List.filter(function|Idl.Param.Boxed{Idl.Param.typedef;_}->(matchtypedef.tywith|Unit->false|_->true))inputsin[Line"";Line(sprintf"def _parse_%s(self):"m.Method.name);Block(output_docm.Method.description@[Line"# in --json mode we don't have any other arguments";Line"if ('--json' in sys.argv or '-j' in sys.argv):";Block[Line"jsondict = json.loads(sys.stdin.readline(),)";Line"jsondict['json'] = True";Line"return jsondict"];Line(sprintf"parser = argparse.ArgumentParser(description='%s')"(String.concat" "m.Method.description));Line"parser.add_argument('-j', '--json', action='store_const', const=True, \
default=False, help='Read json from stdin, print json to stdout', \
required=False)"]@List.map(fun(Idl.Param.Boxeda)->letname=matcha.Idl.Param.namewith|Somes->s|None->failwith(Printf.sprintf"Parameter names required for python generation (%s)"m.Method.name)inmatcha.Idl.Param.typedef.tywith|Dict(_,_)->Line(sprintf"parser.add_argument('--%s', default={}, nargs=2, action=ListAction, \
help='%s')"name(String.concat" "a.Idl.Param.description))|_->Line(sprintf"parser.add_argument('%s', action='store', help='%s'%s)"name(String.concat" "a.Idl.Param.description)(matcha.Idl.Param.typedef.tywith|BasicInt->", type=long"|BasicInt64->", type=long"|BasicInt32->", type=int"|BasicBool->", type=lambda x: json.loads(x.lower())"|BasicFloat->", type=float"|_->"")))inputs@[Line"return vars(parser.parse_args())"])]letcommandline_run_(BoxedFunctionm)=letopenPrintfin[Line"";Line(sprintf"def %s(self):"m.Method.name);Block(output_docm.Method.description@[Line"use_json = False";Line"try:";Block[Line(sprintf"request = self._parse_%s()"m.Method.name);Line"use_json = 'json' in request and request['json']";Line(sprintf"results = self.dispatcher.%s(request)"m.Method.name);Line"print(json.dumps(results))"];Line"except Exception as exn:";Block[Line"if use_json:";Block[Line"handle_exception(exn)"];Line"else:";Block[Line"traceback.print_exc()";Line"raise exn"]]])]letcommandline_of_interfacei=letopenPrintfin[Line"";Line"";Line(sprintf"class %s_commandline(object):"i.Interface.details.Idl.Interface.name);Block([Line{|"""Parse command-line arguments and call an implementation."""|};Line"";Line"def __init__(self, impl):";Block[Line"self.impl = impl";Line(sprintf"self.dispatcher = %s_server_dispatcher(self.impl)"i.Interface.details.Idl.Interface.name)]]@List.concat(List.map(commandline_parsei)i.Interface.methods)@List.concat(List.map(commandline_runi)i.Interface.methods))]letof_interfaces?(helpers=inline_defaults)i=letopenPrintfinletdispatch_classicomma=Line(sprintf{| "%s": self.%s._dispatch%s|}i.Interface.details.Idl.Interface.namei.Interface.details.Idl.Interface.namecomma)inletdispatch_dictmethods=letrecintersperse_commaslist=matchlistwith|[]->[]|[x]->[dispatch_classx""]|x::y::tl->dispatch_classx","::intersperse_commas(y::tl)in[Line"self._dispatcher_dict = {"]@intersperse_commasmethods@[Line"}"]in[Line(sprintf{|"""Bindings generated for interface %s by rpclib"""|}i.Interfaces.name);Line"";Line"from __future__ import print_function, division";Line"";Line"import argparse";Line"import json";Line"import logging";Line"import sys";Line"import traceback";Line""]@compat_block@(helpers|>String.split_on_char'\n'|>List.map(funline->Lineline))@(tryList.mapexn_vari.Interfaces.error_decls|>List.flattenwith|e->Printf.fprintfstderr"Error while generating exceptions of %s"i.Interfaces.name;raisee)@List.fold_left(funacci->acc@server_of_interfacei@skeleton_of_interfacei@test_impl_of_interfacei@commandline_of_interfacei)[]i.Interfaces.interfaces@[Line"";Line"";Line(sprintf"class %s_server_dispatcher(object):"i.Interfaces.name);Block[Line{|"""Demux calls to individual interface server_dispatchers"""|};Line"";Line(sprintf"def __init__(self%s):"(String.concat""(List.map(funx->", "^x^"=None")(List.map(funi->i.Interface.details.Idl.Interface.name)i.Interfaces.interfaces))));Block(List.map(funi->Line(sprintf"self.%s = %s"i.Interface.details.Idl.Interface.namei.Interface.details.Idl.Interface.name))i.Interfaces.interfaces@dispatch_dicti.Interfaces.interfaces);Line"";Line"def _dispatch(self, method, params):";Block[Line"try:";Block[Line{|logging.log("method = %s params = %s", method, repr(params))|};(* str.split is never empty in python *)Line"class_ = method.split('.')[0]";Line"if class_ in self._dispatcher_dict:";Block[Line(sprintf"return self._dispatcher_dict[class_](method, params)")];Line"raise UnknownMethod(method)"];Line"except Rpc_light_failure as rpc_exn:";Block[Line{|logging.log("caught %s", rpc_exn)|};Line"traceback.print_exc()";Line{|logging.log("returning %s", repr(rpc_exn.failure()))|};Line"return rpc_exn.failure()"];Line"except Exception as exn:";Block[Line{|logging.log("caught %s", exn)|};Line"traceback.print_exc()";Line"return InternalError(str(exn)).failure()"]]]]@test_impl_of_interfacesi@[Line""]