diff --git a/integration_tests/CMakeLists.txt b/integration_tests/CMakeLists.txt index 0fdb5b12a7..72437cb3b9 100644 --- a/integration_tests/CMakeLists.txt +++ b/integration_tests/CMakeLists.txt @@ -36,6 +36,15 @@ set_property(TARGET lpython_rtlib PROPERTY INTERFACE_LINK_LIBRARIES ${LPYTHON_RTLIB_LIBRARY}) target_link_libraries(lpython_rtlib INTERFACE m) +find_package(Python COMPONENTS Development) +message("\n") +message("System has the Python development artifacts: ${Python_Development_FOUND}") +message("The Python include directories: ${Python_INCLUDE_DIRS}") +message("The Python libraries: ${Python_LIBRARIES}") +message("The Python library directories: ${Python_LIBRARY_DIRS}") +message("The Python runtime library directories: ${Python_RUNTIME_LIBRARY_DIRS}") +message("Python version: ${Python_VERSION}") + enable_testing() message("\n") @@ -57,7 +66,7 @@ message("LPYTHON_RTLIB_LIBRARY: ${LPYTHON_RTLIB_LIBRARY}") macro(RUN) - set(options FAIL) + set(options FAIL ENABLE_CPYTHON) set(oneValueArgs NAME IMPORT_PATH) set(multiValueArgs LABELS EXTRAFILES) cmake_parse_arguments(RUN "${options}" "${oneValueArgs}" @@ -68,6 +77,11 @@ macro(RUN) message(FATAL_ERROR "Must specify the NAME argument") endif() + set(enable_cpython "") + if (${RUN_ENABLE_CPYTHON}) + set(enable_cpython "--enable-cpython") + endif() + if (${KIND} IN_LIST RUN_LABELS) if (KIND STREQUAL "llvm") if (import_path) @@ -97,13 +111,13 @@ macro(RUN) if (import_path) add_custom_command( OUTPUT ${name}.c - COMMAND ${LPYTHON} -I ${CMAKE_CURRENT_SOURCE_DIR}/${import_path} --show-c ${CMAKE_CURRENT_SOURCE_DIR}/${name}.py > ${name}.c + COMMAND ${LPYTHON} ${enable_cpython} -I ${CMAKE_CURRENT_SOURCE_DIR}/${import_path} --show-c ${CMAKE_CURRENT_SOURCE_DIR}/${name}.py > ${name}.c DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${name}.py VERBATIM) else () add_custom_command( OUTPUT ${name}.c - COMMAND ${LPYTHON} --show-c ${CMAKE_CURRENT_SOURCE_DIR}/${name}.py > ${name}.c + COMMAND ${LPYTHON} ${enable_cpython} --show-c ${CMAKE_CURRENT_SOURCE_DIR}/${name}.py > ${name}.c DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${name}.py VERBATIM) endif() @@ -112,6 +126,12 @@ macro(RUN) target_include_directories(${name} PRIVATE ${CMAKE_SOURCE_DIR}) set_target_properties(${name} PROPERTIES LINKER_LANGUAGE C) target_link_libraries(${name} lpython_rtlib) + if (${RUN_ENABLE_CPYTHON}) + target_link_libraries(${name} Python::Python) + if (RUN_EXTRAFILES) + file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/${RUN_EXTRAFILES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + endif() + endif() add_test(${name} ${CMAKE_CURRENT_BINARY_DIR}/${name}) if (RUN_LABELS) set_tests_properties(${name} PROPERTIES LABELS "${RUN_LABELS}") @@ -456,6 +476,7 @@ RUN(NAME bindc_05 LABELS llvm c EXTRAFILES bindc_05b.c) RUN(NAME bindc_06 LABELS llvm c EXTRAFILES bindc_06b.c) +RUN(NAME bindpy_01 LABELS cpython c ENABLE_CPYTHON EXTRAFILES bindpy_01_module.py) RUN(NAME test_generics_01 LABELS cpython llvm c) RUN(NAME test_cmath LABELS cpython llvm c) RUN(NAME test_complex_01 LABELS cpython llvm c wasm wasm_x64) diff --git a/integration_tests/bindpy_01.py b/integration_tests/bindpy_01.py new file mode 100644 index 0000000000..8aff3785a9 --- /dev/null +++ b/integration_tests/bindpy_01.py @@ -0,0 +1,77 @@ +from lpython import i32, i64, u32, u64, f32, f64, pythoncall + +@pythoncall(module = "bindpy_01_module") +def add_ints(a: i32, b: i64, c: u32, d: u64) -> i64: + pass + +@pythoncall(module = "bindpy_01_module") +def multiply_ints(a: i32, b: i64, c: u32, d: u64) -> i64: + pass + +@pythoncall(module = "bindpy_01_module") +def add_floats(a: f32, b: f64) -> f64: + pass + +@pythoncall(module = "bindpy_01_module") +def multiply_floats(a: f32, b: f64) -> f64: + pass + +@pythoncall(module = "bindpy_01_module") +def get_hello_world(a: str, b: str) -> str: + pass + +@pythoncall(module = "bindpy_01_module") +def str_n_times(a: str, n: i32) -> str: + pass + +@pythoncall(module = "bindpy_01_module") +def get_cpython_version() -> str: + pass + +# Integers: +def test_ints(): + i: i32 + j: i64 + k: u32 + l: u64 + i = -5 + j = i64(24) + k = u32(20) + l = u64(92) + + assert add_ints(i, j, k, l) == i64(131) + assert multiply_ints(i, j, k, l) == i64(-220800) + +# Floats +def test_floats(): + a: f32 + b: f64 + a = f32(3.14) + b = -100.00 + + assert abs(add_floats(a, b) - (-96.86)) <= 1e-4 + assert abs(multiply_floats(a, b) - (-314.0)) <= 1e-4 + +# Strings +def test_strings(): + a: str + b: str + c: str + i: i32 + a = "hello" + b = "world" + i = 3 + + assert get_hello_world(a, b) == "hello world!" + assert str_n_times(a, i) == "hellohellohello" + assert get_hello_world(str_n_times(a, i), b) == "hellohellohello world!" + +def main0(): + print("CPython version: ", get_cpython_version()) + + test_ints() + test_floats() + test_strings() + + +main0() diff --git a/integration_tests/bindpy_01_module.py b/integration_tests/bindpy_01_module.py new file mode 100644 index 0000000000..658b0a4f8c --- /dev/null +++ b/integration_tests/bindpy_01_module.py @@ -0,0 +1,23 @@ +def get_cpython_version(): + import platform + return platform.python_version() + +def add_ints(a, b, c, d): + e = a + b + c + d + return e + +def multiply_ints(a, b, c, d): + e = a * b * c * d + return e + +def add_floats(a, b): + return a + b + +def multiply_floats(a, b): + return a * b + +def get_hello_world(a, b): + return f"{a} {b}!" + +def str_n_times(a, n): + return a * n diff --git a/src/bin/lpython.cpp b/src/bin/lpython.cpp index c171003ce7..4d0d9de724 100644 --- a/src/bin/lpython.cpp +++ b/src/bin/lpython.cpp @@ -1246,6 +1246,11 @@ int link_executable(const std::vector &infiles, cmd += " -I " + rtlib_header_dir; cmd += " -L" + base_path + " -Wl,-rpath," + base_path + " -l" + runtime_lib + " -lm"; + if (compiler_options.enable_cpython) { + std::string py_version = "3.10"; + std::string py_flags = R"(-I $CONDA_PREFIX/include/python)" + py_version + R"( -L$CONDA_PREFIX/lib -Wl,-rpath -Wl,$CONDA_PREFIX/lib -lpython)" + py_version + R"()"; + cmd += " " + py_flags; + } int err = system(cmd.c_str()); if (err) { std::cout << "The command '" + cmd + "' failed." << std::endl; @@ -1556,6 +1561,7 @@ int main(int argc, char *argv[]) app.add_flag("--get-rtl-header-dir", print_rtl_header_dir, "Print the path to the runtime library header file"); app.add_flag("--get-rtl-dir", print_rtl_dir, "Print the path to the runtime library file"); app.add_flag("--verbose", compiler_options.verbose, "Print debugging statements"); + app.add_flag("--enable-cpython", compiler_options.enable_cpython, "Enable CPython runtime"); // LSP specific options app.add_flag("--show-errors", show_errors, "Show errors when LSP is running in the background"); diff --git a/src/libasr/ASR.asdl b/src/libasr/ASR.asdl index 8beaf9067d..3341fe5729 100644 --- a/src/libasr/ASR.asdl +++ b/src/libasr/ASR.asdl @@ -86,7 +86,7 @@ symbol bool loaded_from_mod, bool intrinsic) | Function(symbol_table symtab, identifier name, ttype function_signature, identifier* dependencies, expr* args, stmt* body, expr? return_var, - access access, bool deterministic, bool side_effect_free, string? c_header) + access access, bool deterministic, bool side_effect_free, string? module_file) | GenericProcedure(symbol_table parent_symtab, identifier name, symbol* procs, access access) | CustomOperator(symbol_table parent_symtab, identifier name, @@ -137,6 +137,12 @@ presence = Required | Optional -- An interface that uses `iso_c_binding` and `bind(c)` is represented using -- abi=BindC. +-- abi=BindPython: the symbol's implementation is +-- stored in text format in the user source code file. +-- The symbol is executed using the CPython interpreter. +-- LPython manages the conversion of arguments to be passed to such symbols +-- and also converts the return values from such symbols. + -- abi=Interactive: the symbol's implementation has been provided by the -- previous REPL execution (e.g., if LLVM backend is used for the interactive -- mode, the previous execution generated machine code for this symbol's @@ -153,6 +159,7 @@ abi -- External ABI | LFortranModule -- Yes LFortran | GFortranModule -- Yes GFortran | BindC -- Yes C + | BindPython -- Yes Python | Interactive -- Yes Unspecified | Intrinsic -- Yes Unspecified diff --git a/src/libasr/asr_utils.h b/src/libasr/asr_utils.h index 3d9717fca8..c38aa600a3 100644 --- a/src/libasr/asr_utils.h +++ b/src/libasr/asr_utils.h @@ -2937,7 +2937,7 @@ inline ASR::asr_t* make_Function_t_util(Allocator& al, const Location& loc, ASR::deftypeType m_deftype, char* m_bindc_name, bool m_elemental, bool m_pure, bool m_module, bool m_inline, bool m_static, ASR::ttype_t** m_type_params, size_t n_type_params, ASR::symbol_t** m_restrictions, size_t n_restrictions, - bool m_is_restriction, bool m_deterministic, bool m_side_effect_free, char *m_c_header=nullptr) { + bool m_is_restriction, bool m_deterministic, bool m_side_effect_free, char *m_module_file=nullptr) { Vec arg_types; arg_types.reserve(al, n_args); ReplaceWithFunctionParamVisitor replacer(al, a_args, n_args); @@ -2961,7 +2961,7 @@ inline ASR::asr_t* make_Function_t_util(Allocator& al, const Location& loc, return ASR::make_Function_t( al, loc, m_symtab, m_name, func_type, m_dependencies, n_dependencies, a_args, n_args, m_body, n_body, m_return_var, m_access, m_deterministic, - m_side_effect_free, m_c_header); + m_side_effect_free, m_module_file); } class SymbolDuplicator { diff --git a/src/libasr/codegen/asr_to_c.cpp b/src/libasr/codegen/asr_to_c.cpp index 69ec369bd3..a446af75c0 100644 --- a/src/libasr/codegen/asr_to_c.cpp +++ b/src/libasr/codegen/asr_to_c.cpp @@ -890,10 +890,31 @@ R"( } std::string body; + if (compiler_options.enable_cpython) { + body += R"( + Py_Initialize(); + wchar_t* argv1 = Py_DecodeLocale("", NULL); + wchar_t** argv_ = {&argv1}; + PySys_SetArgv(1, argv_); +)"; + body += "\n"; + } + for (size_t i=0; ivisit_stmt(*x.m_body[i]); body += src; } + + if (compiler_options.enable_cpython) { + body += R"( + if (Py_FinalizeEx() < 0) { + fprintf(stderr,"BindPython: Unknown Error\n"); + exit(1); + } +)"; + body += "\n"; + } + src = contains + "int main(int argc, char* argv[])\n{\n" + indent1 + "_lpython_set_argv(argc, argv);\n" diff --git a/src/libasr/codegen/asr_to_c_cpp.h b/src/libasr/codegen/asr_to_c_cpp.h index 91ea23b4b1..ac15c521f4 100644 --- a/src/libasr/codegen/asr_to_c_cpp.h +++ b/src/libasr/codegen/asr_to_c_cpp.h @@ -503,6 +503,86 @@ R"(#include return "\ntemplate <" + template_for_Kokkos + ">\n" + func; } + std::string get_arg_conv_bind_python(const ASR::Function_t &x) { + + std::string arg_conv = R"( + pArgs = PyTuple_New()" + std::to_string(x.n_args) + R"(); +)"; + for (size_t i = 0; i < x.n_args; ++i) { + ASR::Variable_t *arg = ASRUtils::EXPR2VAR(x.m_args[i]); + arg_conv += R"( + pValue = )" + CUtils::get_py_obj_type_conv_func_from_ttype_t(arg->m_type) + "(" + + std::string(arg->m_name) + R"(); + if (!pValue) { + Py_DECREF(pArgs); + Py_DECREF(pModule); + fprintf(stderr, "Cannot convert argument\n"); + exit(1); + } + /* pValue reference stolen here: */ + PyTuple_SetItem(pArgs, )" + std::to_string(i) + R"(, pValue); +)"; + } + return arg_conv; + } + + std::string get_return_value_conv_bind_python(const ASR::Function_t &x) { + if (!x.m_return_var) return ""; + ASR::Variable_t* r_v = ASRUtils::EXPR2VAR(x.m_return_var); + std::string indent = "\n "; + std::string py_val_cnvrt = CUtils::get_py_obj_return_type_conv_func_from_ttype_t(r_v->m_type) + "(pValue)"; + std::string ret_var_decl = indent + CUtils::get_c_type_from_ttype_t(r_v->m_type) + " " + std::string(r_v->m_name) + ";"; + std::string ret_assign = indent + std::string(r_v->m_name) + " = " + py_val_cnvrt + ";"; + std::string ret_stmt = indent + "return " + std::string(r_v->m_name) + ";"; + std::string clear_pValue = ""; + if (!ASRUtils::is_aggregate_type(r_v->m_type)) { + clear_pValue = indent + "Py_DECREF(pValue);"; + } + return ret_var_decl + ret_assign + clear_pValue + ret_stmt + "\n"; + } + + std::string get_func_body_bind_python(const ASR::Function_t &x) { + user_headers.insert("Python.h"); + std::string indent(indentation_level*indentation_spaces, ' '); + std::string var_decls = "PyObject *pName, *pModule, *pFunc; PyObject *pArgs, *pValue;\n"; + std::string func_body = R"( + pName = PyUnicode_FromString(")" + std::string(x.m_module_file) + R"("); + if (pName == NULL) { + PyErr_Print(); + fprintf(stderr, "Failed to convert to unicode string )" + std::string(x.m_module_file) + R"(\n"); + exit(1); + } + + pModule = PyImport_Import(pName); + Py_DECREF(pName); + if (pModule == NULL) { + PyErr_Print(); + fprintf(stderr, "Failed to load python module )" + std::string(x.m_module_file) + R"(\n"); + exit(1); + } + + pFunc = PyObject_GetAttrString(pModule, ")" + std::string(x.m_name) + R"("); + if (!pFunc || !PyCallable_Check(pFunc)) { + if (PyErr_Occurred()) PyErr_Print(); + fprintf(stderr, "Cannot find function )" + std::string(x.m_name) + R"(\n"); + Py_XDECREF(pFunc); + Py_DECREF(pModule); + exit(1); + } +)" + get_arg_conv_bind_python(x) + R"( + pValue = PyObject_CallObject(pFunc, pArgs); + Py_DECREF(pArgs); + if (pValue == NULL) { + Py_DECREF(pFunc); + Py_DECREF(pModule); + PyErr_Print(); + fprintf(stderr,"Call failed\n"); + exit(1); + } +)" + get_return_value_conv_bind_python(x); + return "{\n" + indent + var_decls + func_body + "}\n"; + } + std::string declare_all_functions(const SymbolTable &scope) { std::string code, t; for (auto &item : scope.get_scope()) { @@ -552,14 +632,19 @@ R"(#include return; } ASR::FunctionType_t *f_type = ASRUtils::get_FunctionType(x); - if (f_type->m_abi == ASR::abiType::BindC - && f_type->m_deftype == ASR::deftypeType::Interface) { - if (x.m_c_header) { - user_headers.insert(std::string(x.m_c_header)); - src = ""; - return; - } else { - sub += ";\n"; + if (f_type->m_deftype == ASR::deftypeType::Interface) { + if (f_type->m_abi == ASR::abiType::BindC) { + if (x.m_module_file) { + user_headers.insert(std::string(x.m_module_file)); + src = ""; + return; + } else { + sub += ";\n"; + } + } else if (f_type->m_abi == ASR::abiType::BindPython) { + indentation_level += 1; + sub += "\n" + get_func_body_bind_python(x); + indentation_level -= 1; } } else { sub += "\n"; @@ -625,8 +710,8 @@ R"(#include src = sub; if (f_type->m_abi == ASR::abiType::BindC && f_type->m_deftype == ASR::deftypeType::Implementation) { - if (x.m_c_header) { - std::string header_name = std::string(x.m_c_header); + if (x.m_module_file) { + std::string header_name = std::string(x.m_module_file); user_headers.insert(header_name); emit_headers[header_name]+= "\n" + src; src = ""; diff --git a/src/libasr/codegen/c_utils.h b/src/libasr/codegen/c_utils.h index 2254d3fd47..300a638009 100644 --- a/src/libasr/codegen/c_utils.h +++ b/src/libasr/codegen/c_utils.h @@ -330,6 +330,87 @@ namespace CUtils { return type_src; } + static inline std::string get_py_obj_type_conv_func_from_ttype_t(ASR::ttype_t* t) { + int kind = ASRUtils::extract_kind_from_ttype_t(t); + std::string type_src = ""; + switch( t->type ) { + case ASR::ttypeType::Integer: { + switch (kind) + { + case 4: type_src = "PyLong_FromLong"; break; + case 8: type_src = "PyLong_FromLongLong"; break; + default: + throw CodeGenError("get_py_obj_type_conv_func: Unsupported kind in int type"); + } + break; + } + case ASR::ttypeType::UnsignedInteger: { + switch (kind) + { + case 4: type_src = "PyLong_FromUnsignedLong"; break; + case 8: type_src = "PyLong_FromUnsignedLongLong"; break; + default: + throw CodeGenError("get_py_obj_type_conv_func: Unsupported kind in unsigned int type"); + } + break; + } + case ASR::ttypeType::Logical: { + type_src = "PyBool_FromLong"; + break; + } + case ASR::ttypeType::Real: { + type_src = "PyFloat_FromDouble"; + break; + } + case ASR::ttypeType::Character: { + type_src = "PyUnicode_FromString"; + break; + } + default: { + throw CodeGenError("get_py_object_type_conv_func: Type " + ASRUtils::type_to_str_python(t) + " not supported yet."); + } + } + return type_src; + } + + static inline std::string get_py_obj_return_type_conv_func_from_ttype_t(ASR::ttype_t* t) { + int kind = ASRUtils::extract_kind_from_ttype_t(t); + std::string type_src = ""; + switch( t->type ) { + case ASR::ttypeType::Integer: { + switch (kind) + { + case 4: type_src = "PyLong_AsLong"; break; + case 8: type_src = "PyLong_AsLongLong"; break; + default: + throw CodeGenError("get_py_obj_type_conv_func: Unsupported kind in int type"); + } + break; + } + case ASR::ttypeType::UnsignedInteger: { + switch (kind) + { + case 4: type_src = "PyLong_AsUnsignedLong"; break; + case 8: type_src = "PyLong_AsUnsignedLongLong"; break; + default: + throw CodeGenError("get_py_obj_type_conv_func: Unsupported kind in unsigned int type"); + } + break; + } + case ASR::ttypeType::Real: { + type_src = "PyFloat_AsDouble"; + break; + } + case ASR::ttypeType::Character: { + type_src = "(char*)PyUnicode_AsUTF8"; + break; + } + default: { + throw CodeGenError("get_py_object_type_conv_func: Type " + ASRUtils::type_to_str_python(t) + " not supported yet."); + } + } + return type_src; + } } // namespace CUtils diff --git a/src/libasr/utils.h b/src/libasr/utils.h index 167314ffe9..7665ccfb37 100644 --- a/src/libasr/utils.h +++ b/src/libasr/utils.h @@ -56,6 +56,7 @@ struct CompilerOptions { bool emit_debug_line_column = false; bool verbose = false; bool pass_cumulative = false; + bool enable_cpython = false; std::vector import_paths; Platform platform; diff --git a/src/lpython/semantics/python_ast_to_asr.cpp b/src/lpython/semantics/python_ast_to_asr.cpp index a700cb0a1d..16c26c068d 100644 --- a/src/lpython/semantics/python_ast_to_asr.cpp +++ b/src/lpython/semantics/python_ast_to_asr.cpp @@ -3775,6 +3775,22 @@ class SymbolTableVisitor : public CommonVisitor { return ASR::down_cast(tmp); } + char* extract_keyword_val_from_decorator(AST::Call_t* x, std::string keyword) { + for (size_t i=0; i < x->n_keywords; i++) { + if (std::string(x->m_keywords[i].m_arg) == keyword) { + if (AST::is_a(*x->m_keywords[i].m_value)) { + std::string module_name = AST::down_cast( + x->m_keywords[i].m_value)->m_value; + return s2c(al, module_name); + } else { + throw SemanticError("module should be constant string in ccall", + x->base.base.loc); + } + } + } + return nullptr; + } + void visit_FunctionDef(const AST::FunctionDef_t &x) { dependencies.clear(al); SymbolTable *parent_scope = current_scope; @@ -3790,7 +3806,7 @@ class SymbolTableVisitor : public CommonVisitor { bool is_restriction = false; bool is_deterministic = false; bool is_side_effect_free = false; - char *bindc_name=nullptr, *c_header_file=nullptr; + char *bindc_name=nullptr, *module_file=nullptr; if (x.n_decorator_list > 0) { for(size_t i=0; i { current_procedure_interface = true; } else if (name == "ccallback" || name == "ccallable") { current_procedure_abi_type = ASR::abiType::BindC; + } else if (name == "pythoncall") { + current_procedure_abi_type = ASR::abiType::BindPython; + current_procedure_interface = true; } else if (name == "overload") { overload = true; } else if (name == "interface") { @@ -3825,23 +3844,14 @@ class SymbolTableVisitor : public CommonVisitor { AST::Call_t *call_d = AST::down_cast(dec); if (AST::is_a(*call_d->m_func)) { std::string name = AST::down_cast(call_d->m_func)->m_id; - if (name == "ccall" || "ccallable") { + if (name == "ccall" || name == "ccallable") { current_procedure_abi_type = ASR::abiType::BindC; - if (name == "ccall") current_procedure_interface = true; - if (call_d->n_keywords > 0) { - for (size_t i=0; i < call_d->n_keywords; i++) { - if (std::string(call_d->m_keywords[i].m_arg) == "header") { - if (AST::is_a(*call_d->m_keywords[i].m_value)) { - std::string header_name = AST::down_cast( - call_d->m_keywords[i].m_value)->m_value; - c_header_file = s2c(al, header_name); - } else { - throw SemanticError("header should be constant string in ccall/ccallable", - x.base.base.loc); - } - } - } - } + current_procedure_interface = (name == "ccall"); + module_file = extract_keyword_val_from_decorator(call_d, "header"); + } else if (name == "pythoncall") { + current_procedure_abi_type = ASR::abiType::BindPython; + current_procedure_interface = true; + module_file = extract_keyword_val_from_decorator(call_d, "module"); } else { throw SemanticError("Unsupported Decorator type", x.base.base.loc); @@ -3969,8 +3979,7 @@ class SymbolTableVisitor : public CommonVisitor { } ASR::accessType s_access = ASR::accessType::Public; ASR::deftypeType deftype = ASR::deftypeType::Implementation; - if (current_procedure_abi_type == ASR::abiType::BindC && - current_procedure_interface) { + if (current_procedure_interface) { deftype = ASR::deftypeType::Interface; } if (x.m_returns && !AST::is_a(*x.m_returns)) { @@ -4013,7 +4022,7 @@ class SymbolTableVisitor : public CommonVisitor { current_procedure_abi_type, s_access, deftype, bindc_name, vectorize, false, false, is_inline, is_static, tps.p, tps.size(), nullptr, 0, is_restriction, is_deterministic, is_side_effect_free, - c_header_file); + module_file); return_variable->m_type = return_type_; } else { throw SemanticError("Return variable must be an identifier (Name AST node) or an array (Subscript AST node)", @@ -4035,7 +4044,7 @@ class SymbolTableVisitor : public CommonVisitor { s_access, deftype, bindc_name, false, is_pure, is_module, is_inline, is_static, tps.p, tps.size(), nullptr, 0, is_restriction, is_deterministic, is_side_effect_free, - c_header_file); + module_file); } ASR::symbol_t * t = ASR::down_cast(tmp); parent_scope->add_symbol(sym_name, t); diff --git a/src/runtime/lpython/lpython.py b/src/runtime/lpython/lpython.py index 79f28948b4..d99d2ee561 100644 --- a/src/runtime/lpython/lpython.py +++ b/src/runtime/lpython/lpython.py @@ -457,6 +457,14 @@ def ccall(f): return f return CTypes(f) +def pythoncall(*args, **kwargs): + def inner(fn): + import importlib + module = importlib.import_module(kwargs["module"]) + fn_new = getattr(module, fn.__name__) + return fn_new + return inner + def union(f): fields = [] for name in f.__annotations__: diff --git a/tests/reference/asr_json-modules_02-70a491a.json b/tests/reference/asr_json-modules_02-70a491a.json index b258c5486c..c33018fb6a 100644 --- a/tests/reference/asr_json-modules_02-70a491a.json +++ b/tests/reference/asr_json-modules_02-70a491a.json @@ -6,7 +6,7 @@ "outfile": null, "outfile_hash": null, "stdout": "asr_json-modules_02-70a491a.stdout", - "stdout_hash": "e27e4f8a729e4a535ca8e5bd618187afea35f15fac507eef23bfe9f9", + "stdout_hash": "1cf3ded8f13b6d020f6eca4ef4247044cb7a83f08374170a767bff52", "stderr": null, "stderr_hash": null, "returncode": 0 diff --git a/tests/reference/asr_json-modules_02-70a491a.stdout b/tests/reference/asr_json-modules_02-70a491a.stdout index ade8f885d7..4c58732b79 100644 --- a/tests/reference/asr_json-modules_02-70a491a.stdout +++ b/tests/reference/asr_json-modules_02-70a491a.stdout @@ -75,7 +75,7 @@ "access": "Public", "deterministic": false, "side_effect_free": false, - "c_header": [] + "module_file": [] }, "loc": { "first": 0, @@ -582,7 +582,7 @@ "access": "Public", "deterministic": false, "side_effect_free": false, - "c_header": [] + "module_file": [] }, "loc": { "first": 51, @@ -808,7 +808,7 @@ "access": "Public", "deterministic": false, "side_effect_free": false, - "c_header": [] + "module_file": [] }, "loc": { "first": 154, @@ -968,7 +968,7 @@ "access": "Public", "deterministic": false, "side_effect_free": false, - "c_header": [] + "module_file": [] }, "loc": { "first": 188,