diff --git a/emacspy.el b/emacspy.el index 68da61d..7d89340 100644 --- a/emacspy.el +++ b/emacspy.el @@ -133,11 +133,8 @@ https://docs.python.org/3/library/sys.html#sys.base_prefix" (py-import subinterpreter module as) (py-import subinterpreter module))) -(defun emacspy-call-function (subinterpreter function_name as &rest args) - (apply 'py-call-function subinterpreter function_name as args)) - -(defun emacspy-call-method (subinterpreter obj_name method_name as &rest args) - (apply 'py-call-method subinterpreter obj_name method_name as args)) +(defun emacspy-call (subinterpreter obj_name method_name &optional as &rest args) + (apply 'emacspy--call subinterpreter obj_name method_name as args nil)) (provide 'emacspy) diff --git a/emacspy.pyx b/emacspy.pyx index 092e689..47e0ee9 100644 --- a/emacspy.pyx +++ b/emacspy.pyx @@ -377,8 +377,7 @@ cdef extern from "subinterpreter.c": object destroy_subinterpreter(char*) object list_subinterpreters() object run_string(char*, char*, object) - object call_method(char*, object, object, object, object, object) - object call_function(char*, object, object, object, object) + object call_py(char*, object, object, object, object, object) object import_module(char*, object, object) object get_global_variable(char*, object) object get_object_attr(char*, object, object, object) @@ -436,31 +435,18 @@ def init(): raise ret return ret - @defun('py-call-method') - def call_object_python(interpreter_name, obj_name, method_name, target_name='', *args): - if target_name and target_name.to_python_type(): - target_name = target_name.to_python_type() - else: - target_name = '' - args_py = list((arg.to_python_type() for arg in args)) - ret = call_method(str_elisp2c(interpreter_name), obj_name.to_python_type(), \ - method_name.to_python_type(), tuple(), target_name, args_py) + @defun('emacspy--call') + def call_function_python(interpreter_name, object_name, method_name, target_name, args, kwargs): + target_name_py: str = target_name.to_python_type() or '' + args_py: tuple = tuple(args.to_python_type() or tuple()) + kwargs_py: dict = kwargs.to_python_type() or {} + ret = call_py(str_elisp2c(interpreter_name), \ + object_name.to_python_type(), method_name.to_python_type(), \ + target_name_py, args_py, kwargs_py) if isinstance(ret, BaseException): raise ret return ret - @defun('emacspy--call-function') - def call_function_python(interpreter_name, function_name, target_name, args, kwargs): - target_name: str = target_name.to_python_type() or '' - args_py: tuple = tuple(args.to_python_type() or ()) - kwargs_py: dict = kwargs.to_python_type() - ret = call_function(str_elisp2c(interpreter_name), function_name.to_python_type(), \ - target_name, args_py, kwargs_py) - if isinstance(ret, BaseException): - raise ret - return ret - - @defun('py-get-global-variable') def get_global_var(interpreter_name, var_name): ret = get_global_variable(str_elisp2c(interpreter_name), var_name.to_python_type()) diff --git a/subinterpreter.c b/subinterpreter.c index f734187..1fbbfa2 100644 --- a/subinterpreter.c +++ b/subinterpreter.c @@ -173,71 +173,57 @@ PyObject *run_string(char *interpreter_name, char *string, PyObject *target_name SUBINTERPRETER_RETURN; } -PyObject *call_method(char *interpreter_name, PyObject *obj_name, PyObject *method_name, - PyObject *kwnames, PyObject *target_name, PyObject *args_pylist) { +PyObject *call_py(char *interpreter_name, PyObject *obj_name, PyObject *method_name, + PyObject *target_name, PyObject *args_pytuple, PyObject *kwargs_pydict) { SUBINTERPRETER_SWITCH; PyObject *global_dict = PyModule_GetDict(sub_interpreter->main_module); - Py_ssize_t nargs = PyList_Size(args_pylist); - size_t nargsf = 1 + PyList_Size(args_pylist); // TODO sign to unsign conversion?? - size_t size_obj_args = nargsf * sizeof(PyObject); - PyObject **obj_with_args = malloc(size_obj_args); - assert(obj_with_args); - obj_with_args[0] = PyObject_GetItem(global_dict, obj_name); // New reference - - PyObject *obj = NULL; - - if (NULL == obj_with_args[0]) { - PyErr_SetObject(PyExc_KeyError, obj_name); - exception = PyErr_GetRaisedException(); - goto finish; - } - - for (unsigned int i = 0; i < nargs; ++i) { - obj_with_args[1 + i] = PyList_GetItem(args_pylist, i); - assert(obj_with_args[1 + i]); - } - - obj = PyObject_VectorcallMethod(method_name, obj_with_args, nargsf, kwnames); - exception = PyErr_GetRaisedException(); - if (exception) - goto finish; - - SETUP_RET; -finish: - Py_XDECREF(obj_with_args[0]); - free(obj_with_args); - SUBINTERPRETER_RETURN; -} - -PyObject *call_function(char *interpreter_name, PyObject *callable_name, PyObject *target_name, - PyObject *args_pylist, PyObject *kvargs_pydict) { - SUBINTERPRETER_SWITCH; - PyObject *global_dict = PyModule_GetDict(sub_interpreter->main_module); - PyObject *callable = PyObject_GetItem(global_dict, callable_name); // New reference + PyObject *call_obj = PyObject_GetItem(global_dict, obj_name); // New reference PyObject *obj = NULL; + PyObject *method = NULL; - if (NULL == callable) { + if (NULL == call_obj) { PyErr_Clear(); PyObject *builtins_name = PyUnicode_FromString("__builtins__"); PyObject *builtins = PyObject_GetItem(global_dict, builtins_name); Py_DECREF(builtins_name); - callable = PyObject_GetAttr(builtins, callable_name); // New reference + call_obj = PyObject_GetAttr(builtins, obj_name); // New reference } exception = PyErr_GetRaisedException(); if (exception) goto finish; - assert(callable); + assert(call_obj); - obj = PyObject_Call(callable, args_pylist, kvargs_pydict); // New reference + if (1 != PyObject_IsTrue(kwargs_pydict)) { + kwargs_pydict = NULL; + } + + if (1 == PyObject_IsTrue(method_name)) { + method = PyObject_GetAttr(call_obj, method_name); // New reference + + exception = PyErr_GetRaisedException(); + if (exception) + goto finish; + + if (!PyCallable_Check(method)) { + PyErr_Format(PyExc_ValueError, "'%U.%U' is not executable.", call_obj, + method_name); + exception = PyErr_GetRaisedException(); + goto finish; + } + obj = PyObject_Call(method, args_pytuple, kwargs_pydict); // New reference + } else { + obj = PyObject_Call(call_obj, args_pytuple, kwargs_pydict); // New reference + } exception = PyErr_GetRaisedException(); if (exception) goto finish; SETUP_RET; finish: - Py_XDECREF(callable); + Py_XDECREF(call_obj); + Py_XDECREF(method); SUBINTERPRETER_RETURN; } diff --git a/tests/test.el b/tests/test.el index aff1365..7b97d23 100644 --- a/tests/test.el +++ b/tests/test.el @@ -23,12 +23,12 @@ "test" (py-set-global "test" () "test_bool")))) ) (ert-deftest ert-test-emacspy-py-call-method () - (should (string= "/" (py-call-method "test" "ospath" "realpath" nil "/"))) - (should (py-call-method "test" "ospath" "realpath" "call_method_test_var" "/")) - (should (string= "/" (py-get-global-variable "test" "call_method_test_var"))) - (should-error (string= "/" (py-call-method "test" "ospath" "DUMMY_METHOD")) + (should (string= "/" (emacspy--call "test" "ospath" "realpath" nil '("/") nil))) + (should (emacspy--call "test" "ospath" "realpath" "call_method_test_var" '("/") nil)) + (should (string= "/" (py-get-global-variable "test" "call_method_test_var" ))) + (should-error (emacspy--call "test" "ospath" "DUMMY_METHOD" nil (emacspy-alist2hash nil)) :type 'python-exception) - (should-error (string= "/" (py-call-method "test" "NON_EXISTING_OBJECT" "DUMMY_METHOD")) + (should-error (emacspy--call "test" "NON_EXISTING_OBJECT" "DUMMY_METHOD" nil (emacspy-alist2hash nil)) :type 'python-exception) ) (ert-deftest ert-test-emacspy-py-get-global-variable () @@ -36,13 +36,13 @@ (should-error (py-get-global-variable "test" "NON_EXISTING_VARIABLE")) ) (ert-deftest ert-test-emacspy-emacspy--call-function () - (should (eq 3 (emacspy--call-function "test" "len" nil '("123") (emacspy-alist2hash nil)))) - (should (emacspy--call-function "test" "len" "call_function_test_var" '("123") (emacspy-alist2hash nil))) + (should (eq 3 (emacspy--call "test" "len" nil nil '("123") nil))) + (should (emacspy--call "test" "len" nil "call_function_test_var" '("123") nil)) (should (eq 3 (py-get-global-variable "test" "call_function_test_var"))) - (should-error (emacspy--call-function "test" "NON-EXISTING-FUNCTION" nil '("123") (emacspy-alist2hash nil)) + (should-error (emacspy--call "test" "NON-EXISTING-FUNCTION" nil nil '("123") (emacspy-alist2hash nil)) :type 'python-exception) - (should (emacspy--call-function "test" "dict" "call_function_kvargs_test_var" nil + (should (emacspy--call "test" "dict" nil "call_function_kvargs_test_var" nil (emacspy-alist2hash '(("some_test" . 1) ("test" . "also_test"))) )) (let ((ret (py-get-global-variable "test" "call_function_kvargs_test_var"))) (should (hash-table-p ret)) @@ -67,10 +67,10 @@ (ert-deftest ert-test-emacspy-import-custom-module () (should (py-import "test" "sys")) (should (py-get-object-attr "test" "sys" "path" "syspath")) - (should-not (py-call-method "test" "syspath" "append" nil "./tests/")) + (should-not (emacspy--call "test" "syspath" "append" nil '("./tests/") nil)) (should (py-import "test" "emacspy_test")) (should (py-get-object-attr "test" "emacspy_test" "test_obj" "test_obj")) - (should (string= "test" (py-call-method "test" "test_obj" "get_string"))) ) + (should (string= "test" (emacspy--call "test" "test_obj" "get_string" nil nil nil))) ) (ert-deftest ert-test-emacspy-non-existing-interpreter () (should-error (py-run-string "NON_EXISTING" "True")))