From 3c0e16030ceea78e47e45fc968a43d4221521bce Mon Sep 17 00:00:00 2001 From: Vitor Santos Costa Date: Sat, 1 Dec 2012 14:28:25 +0000 Subject: [PATCH] imprive python interface --- packages/python/examples/pyx.pl | 13 +- packages/python/examples/tut.pl | 6 +- packages/python/python.c | 278 +++++++++++++++++++++++--------- packages/python/python.pl | 158 +++++++++++++----- 4 files changed, 335 insertions(+), 120 deletions(-) diff --git a/packages/python/examples/pyx.pl b/packages/python/examples/pyx.pl index b28e5b49f..0bb180fe2 100644 --- a/packages/python/examples/pyx.pl +++ b/packages/python/examples/pyx.pl @@ -5,27 +5,28 @@ setup_dir :- prolog_load_context(directory, Dir), - atom_concat(Dir,'/pyx/',Base), + atom_concat(Dir,'pyx/',Base), assert(base(Base)). :- setup_dir. main :- + python_import(pyx), ex(X), flush_output, fail. main. -ex(hello_world) :- - c := pyx:canvas:canvas, +ex(hello) :- + c := canvas:canvas(_), := $c:text(0, 0, 'Hello, world!'), := $c:stroke(path:line(0, 0, 2, 0)), := $c:writePDFfile('hello'). ex(changebar) :- - g := pyx:graph:graphxy(width=8, x=graph:axis:bar), - atomic_concat(Source, 'minimal.dat', Data), - := $g:plot(graph:data:file(Data, xname=0, y=2), [graph:style:changebar]), + g := graph:graphxy(width=8, x=graph:axis:bar(_)), + base(Source), atomic_concat(Source, 'minimal.dat', Data), + := $g:plot(graph:data:file(Data, xname=0, y=2), [graph:style:changebar(_)]), := $g:writePDFfile(changebar). diff --git a/packages/python/examples/tut.pl b/packages/python/examples/tut.pl index 8a8799c40..c3d53a577 100644 --- a/packages/python/examples/tut.pl +++ b/packages/python/examples/tut.pl @@ -41,7 +41,7 @@ ex(home) :- ). ex(site) :- - X := site:getusersitepackages, + X := site:getusersitepackages(_), format('site packages=~a~n',[X]). ex(arith) :- @@ -122,10 +122,10 @@ ex(lists) :- := $a:remove(333), B := $a, format('a=~w~n', [B]), - := $a:reverse, + := $a:reverse(_), C := $a, format('a=~w~n', [C]), - := $a:sort, + := $a:sort(_), D := $a, format('a=~w~n', [D]). diff --git a/packages/python/python.c b/packages/python/python.c index 212cd709d..11ed48614 100644 --- a/packages/python/python.c +++ b/packages/python/python.c @@ -43,6 +43,9 @@ static functor_t FUNCTOR_dollar1, static PyObject *py_Main; static PyObject *term_to_python(term_t t); +static PyObject *ActiveModules[32]; +static int active_modules = 0; + static inline int proper_ascii_string(char *s) { @@ -67,6 +70,37 @@ get_p_int(PyObject *o, Py_ssize_t def) { return def; } +static PyObject * +find_obj(PyObject *ob, term_t lhs) +{ + char *s; + PyObject *out, *pName; + int arity = 0; + + if (!PL_get_atom_chars(lhs, &s)) { + atom_t name; + if (!PL_get_name_arity(lhs, &name, &arity) ) + return NULL; + s = PL_atom_chars(name); + } + if (ob) { + out = PyObject_GetAttrString(ob, s); + return out; + } + if (!ob && !arity) { + pName = PyString_FromString(s); + if (pName == NULL) { + return NULL; + } + if (( out = PyImport_Import(pName))) { + return out; + } + } + if (!ob && py_Main && (out = PyObject_GetAttrString(py_Main, s) ) ) + return out; + return NULL; +} + static int copy_to_dictionary(PyObject *dict, term_t targ, term_t taux) { @@ -647,6 +681,7 @@ term_to_python(term_t t) return NULL; if (!PL_get_pointer(targ, &ptr)) return NULL; + Py_INCREF((PyObject *)ptr); /* return __main__,s */ return (PyObject *)ptr; } else if (fun == FUNCTOR_abs1) { @@ -839,6 +874,61 @@ term_to_python(term_t t) rhs = term_to_python(trhs); return PyObject_GetItem(lhs, rhs); } + } else if (fun == FUNCTOR_colon2) { + term_t tleft = PL_new_term_ref(); + PyObject *pArgs, *o; + long i; + int arity; + atom_t name; + + o = NULL; + while (fun == FUNCTOR_colon2) { + if (! PL_get_arg(1, t, tleft) ) + return FALSE; + o = find_obj(o, tleft); + if (!o) + return FALSE; + if (! PL_get_arg(2, t, t) ) + return FALSE; + if (!PL_get_functor(t, &fun)) + break; + } + if (! PL_get_name_arity( t, &name, &arity) ) { + return NULL; + } + if (!arity) { + char *s; + PyObject *pValue; + + if (!PL_get_atom_chars(t, &s)) + return NULL; + if ((pValue = PyObject_GetAttrString(o, s)) == NULL) { + PyErr_Print(); + return NULL; + } + return pValue; + } + o = PyObject_GetAttrString(o, PL_atom_chars(name)); + if (!o || ! PyCallable_Check(o)) { + return NULL; + } + pArgs = PyTuple_New(arity); + for (i = 0 ; i < arity; i++) { + PyObject *pArg; + if (! PL_get_arg(i+1, t, tleft) ) + return NULL; + /* ignore (_) */ + if (i == 0 && PL_is_variable(tleft)) { + Py_DECREF(pArgs); + pArgs = NULL; + } + pArg = term_to_python(tleft); + if (pArg == NULL) + return NULL; + /* pArg reference stolen here: */ + PyTuple_SetItem(pArgs, i, pArg); + } + return PyObject_CallObject(o, pArgs); } else { atom_t name; int len; @@ -975,6 +1065,16 @@ assign_python(PyObject *root, term_t t, PyObject *e) return -1; } +static foreign_t +address_to_term(PyObject *pVal, term_t t) +{ + term_t to = PL_new_term_ref(), t1 = PL_new_term_ref(); + PL_put_pointer(t1, (void *)pVal); + PL_cons_functor(to, FUNCTOR_pointer1, t1); + Py_INCREF(pVal); + return PL_unify(t, to); +} + static foreign_t python_to_term(PyObject *pVal, term_t t) { @@ -1067,11 +1167,7 @@ python_to_term(PyObject *pVal, term_t t) PL_cons_functor(to, FUNCTOR_curly1, to); return PL_unify(t, to); } else { - term_t to = PL_new_term_ref(), t1 = PL_new_term_ref(); - PL_put_pointer(t1, (void *)pVal); - PL_cons_functor(to, FUNCTOR_pointer1, t1); - Py_INCREF(pVal); - return PL_unify(t, to); + return address_to_term(pVal, t); } } @@ -1095,6 +1191,7 @@ python_import(term_t mname, term_t mod) PyErr_Clear(); return FALSE; } + ActiveModules[active_modules++] = pModule; return python_to_term(pModule, mod); } @@ -1187,96 +1284,68 @@ python_is(term_t tobj, term_t tf) } static foreign_t -python_apply(term_t tin, term_t targs, term_t tf) +python_apply(term_t tin, term_t targs, term_t keywds, term_t tf) { PyObject *pF, *pValue; - PyObject *pArgs; - int i, arity, j; + PyObject *pArgs, *pKeywords; + int i, arity; atom_t aname; foreign_t out; term_t targ = PL_new_term_ref(); pF = term_to_python(tin); + if (PL_is_atom(keywds) ) + pKeywords = NULL; + else + pKeywords = term_to_python(keywds); if ( pF == NULL ) { return FALSE; } if (! PL_get_name_arity( targs, &aname, &arity) ) { return FALSE; } - /* follow chains of the form a.b.c.d.e() */ - while (aname == ATOM_colon && arity == 2) { - term_t tleft = PL_new_term_ref(); - PyObject *lhs; - - if (! PL_get_arg(1, targs, tleft) ) + PyObject_Print(pF,stderr,0);fprintf(stderr, "\n"); + if (aname == ATOM_t) { + if (arity == 0) + pArgs = NULL; + else + pArgs = term_to_python( targs ); + PyObject_Print(pArgs,stderr,0);fprintf(stderr, " "); + PyObject_Print(pKeywords,stderr,0);fprintf(stderr, "\n"); + } else { + pArgs = PyTuple_New(arity); + if (!pArgs) return FALSE; - lhs = term_to_python(tleft); - if ((pF = PyObject_GetAttr(pF, lhs)) == NULL) { - PyErr_Print(); - return FALSE; - } - if (! PL_get_arg(2, targs, targs) ) - return FALSE; - if (! PL_get_name_arity( targs, &aname, &arity) ) { - return FALSE; - } - } - if (PyFunction_Check(pF)) { - int tuple_inited = FALSE; - PyObject *pOpt = NULL; - for (j = arity ; j > 0; j--) { - term_t tsubarg = PL_new_term_ref(); + for (i = 0 ; i < arity; i++) { PyObject *pArg; - int posx; - Py_ssize_t pos; - - if (! PL_get_arg(j, targs, targ) ) + if (! PL_get_arg(i+1, targs, targ) ) return FALSE; - if (! PL_is_functor(targ, FUNCTOR_equal2) ) + /* ignore (_) */ + if (i == 0 && PL_is_variable(targ)) { + Py_DECREF(pArgs); + pArgs = NULL; break; - if (! PL_get_arg(1, targ, tsubarg) || !PL_get_integer(tsubarg, &posx) ) - break; - pos = posx; - if (!tuple_inited) { - if ((pOpt = PyFunction_GetDefaults(pF)) == NULL) - break; - tuple_inited = TRUE; } - if (! PL_get_arg(2, targ, tsubarg) ) - break; - pArg = term_to_python(tsubarg); + pArg = term_to_python(targ); if (pArg == NULL) return FALSE; /* pArg reference stolen here: */ - PyTuple_SetItem(pOpt, pos, pArg); + PyTuple_SetItem(pArgs, i, pArg); } - if (tuple_inited) { - if (PyFunction_SetDefaults(pF, pOpt) < 0) { - return FALSE; - } - arity = j; - } - } - pArgs = PyTuple_New(arity); - for (i = 0 ; i < arity; i++) { - PyObject *pArg; - if (! PL_get_arg(i+1, targs, targ) ) - return FALSE; - pArg = term_to_python(targ); - if (pArg == NULL) - return FALSE; - /* pArg reference stolen here: */ - PyTuple_SetItem(pArgs, i, pArg); } if (PyCallable_Check(pF)) { - pValue = PyObject_CallObject(pF, pArgs); - PyErr_Print(); + if (!arity) + pValue = PyObject_CallObject(pF, NULL); + else + pValue = PyObject_Call(pF, pArgs, pKeywords); + if (!pValue) + PyErr_Print(); } else { PyErr_Print(); return FALSE; } - PyErr_Print(); - Py_DECREF(pArgs); + if (pArgs) + Py_DECREF(pArgs); Py_DECREF(pF); if (pValue == NULL) return FALSE; @@ -1313,11 +1382,6 @@ python_access(term_t obj, term_t f, term_t out) PyErr_Print(); return FALSE; } - if ( PyCallable_Check(pValue) ) - pValue = PyObject_CallObject(pValue, NULL); - PyErr_Print(); - if (!pValue) - return FALSE; return python_to_term(pValue, out); } if (! PL_get_name_arity( f, &name, &arity) ) { @@ -1351,6 +1415,11 @@ python_access(term_t obj, term_t f, term_t out) PyObject *pArg; if (! PL_get_arg(i+1, f, targ) ) return FALSE; + /* ignore (_) */ + if (i == 0 && PL_is_variable(targ)) { + Py_DECREF(pArgs); + pArgs = NULL; + } pArg = term_to_python(targ); if (pArg == NULL) return FALSE; @@ -1368,6 +1437,66 @@ python_access(term_t obj, term_t f, term_t out) return python_to_term(pValue, out); } +static foreign_t +python_field(term_t f, term_t tobj, term_t tname, term_t tout) +{ + PyObject *o = NULL, *pF; + atom_t name; + char *s; + int arity; + + if (! PL_get_name_arity( f, &name, &arity) ) { + return FALSE; + } + /* follow chains of the form a.b.c.d.e() */ + while (name == ATOM_colon && arity == 2) { + term_t tleft = PL_new_term_ref(); + PyObject *lhs; + + if (! PL_get_arg(1, f, tleft) ) + return FALSE; + lhs = term_to_python(tleft); + if (o == NULL) { + o = lhs; + } else if ((o = PyObject_GetAttr(o, lhs)) == NULL) { + // PyErr_Print(); + PyErr_Clear(); + return FALSE; + } + if (! PL_get_arg(2, f, f) ) + return FALSE; + if (! PL_get_name_arity( f, &name, &arity) ) { + return FALSE; + } + } + s = PL_atom_chars(name); + if (!s || !o) { + return FALSE; + } else if ((pF = PyObject_GetAttrString(o, s)) == NULL) { + // PyErr_Print(); + PyErr_Clear(); + return FALSE; + } + return + address_to_term(pF, tobj) && + PL_unify_atom(tname, name) && + PL_unify(tout, f); +} + +static foreign_t +python_main_module(term_t mod) +{ + return address_to_term(py_Main, mod); +} + +static foreign_t +python_function(term_t tobj) +{ + PyObject *obj = term_to_python(tobj); + + return PyFunction_Check(obj); +} + static foreign_t python_run_command(term_t cmd) { @@ -1447,9 +1576,12 @@ install_python(void) PL_register_foreign("python_len", 2, python_len, 0); PL_register_foreign("python_is", 2, python_is, 0); PL_register_foreign("python_dir", 2, python_dir, 0); - PL_register_foreign("python_apply", 3, python_apply, 0); + PL_register_foreign("python_apply", 4, python_apply, 0); PL_register_foreign("python_access", 3, python_access, 0); + PL_register_foreign("python_field", 4, python_field, 0); PL_register_foreign("python_assign", 2, python_assign, 0); + PL_register_foreign("python_function", 1, python_function, 0); PL_register_foreign("python_run_command", 1, python_run_command, 0); + PL_register_foreign("python_main_module", 1, python_main_module, 0); } diff --git a/packages/python/python.pl b/packages/python/python.pl index 8023725ce..c4d309135 100644 --- a/packages/python/python.pl +++ b/packages/python/python.pl @@ -59,8 +59,7 @@ Data types are :- use_module(library(lists)). :- use_module(library(apply_macros)). :- use_module(library(charsio)). - -:- dynamic python_mref_cache/2. +:- dynamic python_mref_cache/2, python_obj_cache/2. := F :- python(F,_). @@ -91,51 +90,127 @@ module_extend(M0, M:E, MF, EF, MRef0, MRef) :- module_extend(MM, E, MF, EF, MRef1, MRef). module_extend(M, E, M, E, MRef, MRef). -% given an object, detect its len method -python_eval_term(Expression, O) :- - fetch_module(Expression, Module, Exp, MRef), !, +object_prefix('__obj__'(_)). +object_prefix('$'(_)). +object_prefix('__obj__'(_):_). +object_prefix('$'(_):_). + +% from an exp take an object, and its corresponding Prolog representation +descend_exp(V, _Obj, _F, _S) :- + var(V), !, + throw(error(instantiation_error,_)). +descend_exp(Exp, Obj, F, S) :- + object_prefix(Exp), + !, + python_field(Exp, Obj, F, S). +descend_exp(Exp, Obj, F, S) :- + python_mref_cache(_, MObj), + python_field(MObj:Exp, Obj, F, S), !. +descend_exp(Mod:Exp, Obj, F, S) :- + atom(Mod), + python_import(Mod, MObj), + python_field(MObj:Exp, Obj, F, S), !. + +python_class(Obj) :- + python_obj_cache(inspect:isclass(_), F), + python_apply(F, isclass(Obj), {}, true). + +process_obj(Obj, _, S, Obj, NS, Dict) :- + python_callable(Obj), !, + python_check_args(Obj, Obj, S, NS, Dict). +process_obj(Obj, _, S, Obj, NS, Dict) :- + python_class(Obj), + descend_object(Obj:'__init__', FObj, _, _), + python_check_args(Obj, FObj, S, NS, Dict). + +python_eval_term(Obj, Obj) :- + var(Obj), !. +python_eval_term('__obj__'(Obj), '__obj__'(Obj)) :- !. +python_eval_term($Name, Obj) :- !, + python_is($Name, Obj). +python_eval_term([H|T], [NH|NT]) :- !, + python_eval_term(H, NH), + python_eval_term(T, NT). +python_eval_term(N, N) :- atomic(N), !. +python_eval_term(Exp, O) :- + descend_exp(Exp, Obj, Old, S), !, ( - % avoid looking at : as field of module. - Exp = Obj:Field + python_function(Obj) -> - python_access(MRef, Exp, O) + python_check_args(Obj, Obj, S, NS, Dict), + python_apply(Ob, NS, Dict, O) ; - functor(Exp, F, _), - python_f(MRef, F, FRef), - python_check_args(FRef, Exp, NExp) + descend_exp(Obj:im_func, FObj, _, _) -> - python_apply(FRef, NExp, O) - ; - python_access(MRef, Exp, O) + python_check_args(FObj, Obj, S, NS, Dict), + python_apply(Obj, NS, Dict, O) + ; + descend_exp(Obj:'__init__':im_func, FObj, _, _) + -> + python_check_args(FObj, Obj, S, NS, Dict), + python_apply(Obj, NS, Dict, O) + ; + python_check_args('.', '.', S, NS, {}), + python_is(NS, O) ). -python_eval_term(Obj:Field, O) :- !, - python_access(Obj, Field, O). -python_eval_term(Obj, O) :- - python_is(Obj, O). +python_eval_term(S, O) :- + python_check_args('.', '.', S, NS, {}), + python_is(NS, O). - -python_check_args(FRef, Exp, NExp) :- +python_check_args(_FRef, _Ref, Exp, t, {}) :- + Exp =.. [_,V], var(V), !. +python_check_args(FRef, Ref, Exp, NExp, Dict) :- functor(Exp, _, Arity), - arg(Arity, Exp, _=_), !, - fetch_args(FRef, Dict), - Exp =.. [F|LArgs], - match_args(LArgs, Dict, NLArgs, _), - NExp =.. [F|NLArgs]. -python_check_args(FRef, Exp, Exp). + arg(Arity, Exp, A), nonvar(A), A = (_=_), !, + fetch_args(FRef, ArgNames0, Kwd, Defaults), + Exp =.. [_F|LArgs], + Defaults =.. [t|DefsL], + splice_class(FRef, Ref, ArgNames0, ArgNames), + match_args(LArgs, ArgNames, DefsL, NLArgs, Dict), + NExp =.. [t|NLArgs]. +python_check_args(FRef, _, Exp, NExp, {}) :- + Exp =.. [F|L], + maplist(python_eval_term, L, LF), + NExp =.. [F|LF]. -fetch_args(FRef, Args) :- - python_import('inspect', M), - python_f(M, getargspec, F), - python_apply(F, getargspec(FRef), Args), - ExtraArgs=t(Args, _, _, _). +% in case it is __init__ from __new__ +splice_class(Ref, Ref, ArgNames, ArgNames) :- !. +splice_class(_FRef, _Ref, [_|ArgNames], ArgNames). + +match_args([], _ArgNames, _Defaults, [], {}). +match_args([V=A|LArgs], ArgNames, Defaults, NLArgs, Dict) :- !, + match_named_args([V=A|LArgs], ArgNames, Defaults, NLArgs, Map), + map_to_dict(Map, Dict). +match_args([A|LArgs], [_|ArgNames], [_|Defaults], [VA|NLArgs], Dict) :- + python_eval_term(A, VA), + match_args(LArgs, ArgNames, Defaults, NLArgs, Dict). + +match_named_args([], _ArgNames, Defaults, Defaults, []). +match_named_args([K=A|LArgs], ArgNames, Defaults, NLArgs, Map) :- + match_from_anames(ArgNames, K, VA, Defaults, NDefaults), !, + python_eval_term(A, VA), + match_named_args(LArgs, ArgNames, NDefaults, NLArgs, Map). +match_named_args([K=A|LArgs], ArgNames, Defaults, NLArgs, [K=VA|Map]) :- + python_eval_term(A, VA), + match_named_args(LArgs, ArgNames, Defaults, NLArgs, Map). -match_args([], _, [], ok). -match_args([A=V|LArgs], Dict, [I=V|NLArgs], OK) :- - match_args(LArgs, Dict, NLArgs, ok), !, - ( nth0(I, Dict, A) -> true ; throw(type_error(argument(A=V))) ). -match_args([A|LArgs], Dict, [A|NLArgs], not_ok) :- - match_args(LArgs, Dict, NLArgs, _). +map_to_dict([X=V], {X:V}) :- !. +map_to_dict([X=V|Map], {X:V,NDict}) :- + map_to_dict(Map, {NDict}). + +match_from_anames([K|_ArgNames], K, VA, [_|Defaults], [VA|Defaults]) :- !. +match_from_anames([_|ArgNames], K, VA, [V|Defaults], [V|NDefaults]) :- + match_from_anames(ArgNames, K, VA, Defaults, NDefaults). + +fetch_args(FRef, Args, Kwd, Defaults) :- + FRef = '__obj__'(_), !, + python_mref_cache('inspect', M), + python_obj_cache(inspect:getargspec(_), F), + python_apply(F, getargspec(FRef), {}, ExtraArgs), + ExtraArgs=t(Args, _, Kwd, Defaults). +fetch_args(_, []). + python(Obj, Out) :- python_eval_term(Obj, Out). @@ -146,8 +221,15 @@ python_command(Cmd) :- start_python :- use_foreign_library(foreign(python)), init_python, + python_main_module(MRef), + assert(python_mref_cache('__main__', MRef)), python_command('import sys'), - python_command('import inspect'). + python_import('inspect'), + python_mref_cache(inspect, InspRef), + python_field(InspRef:isclass(_), IsClass, _, _), + assert(python_obj_cache(inspect:isclass(_), IsClass)), + python_field(InspRef:getargspec(_), GetArgSpec, _, _), + assert(python_obj_cache(inspect:getargspec(_), GetArgSpec)). add_cwd_to_python :- unix(getcwd(Dir)),