Skip to content

Commit

Permalink
Rename call_function to call_py and allow it to call methods
Browse files Browse the repository at this point in the history
Remove call_method
  • Loading branch information
813gan committed Sep 15, 2024
1 parent e9cbf20 commit 8df677d
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 83 deletions.
7 changes: 2 additions & 5 deletions emacspy.el
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
32 changes: 9 additions & 23 deletions emacspy.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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())
Expand Down
74 changes: 30 additions & 44 deletions subinterpreter.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
22 changes: 11 additions & 11 deletions tests/test.el
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,26 @@
"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 ()
(should (string= "__main__" (py-get-global-variable "test" "__name__")))
(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))
Expand All @@ -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")))
Expand Down

0 comments on commit 8df677d

Please sign in to comment.