From 5766f777d70263bc4bb951cfdc6811813966d26b Mon Sep 17 00:00:00 2001 From: DerThorsten Date: Wed, 20 Mar 2024 14:22:11 +0100 Subject: [PATCH] update docs --- build_mkdocs.sh | 43 +++++++------ docs/JavaScript_API.md | 100 +++++++++++++++++++++++++++++- include/pyjs/pre_js/init.js | 5 +- include/pyjs/pre_js/make_proxy.js | 1 - src/convert.cpp | 2 +- src/export_py_object.cpp | 50 +++++++-------- src/js_timestamp.cpp | 2 +- 7 files changed, 146 insertions(+), 57 deletions(-) diff --git a/build_mkdocs.sh b/build_mkdocs.sh index 0984065..1b359f6 100755 --- a/build_mkdocs.sh +++ b/build_mkdocs.sh @@ -29,7 +29,7 @@ if [ ! -d "$WASM_ENV_PREFIX" ]; then --yes \ python pybind11 nlohmann_json pybind11_json numpy \ bzip2 sqlite zlib libffi exceptiongroup \ - xeus xeus-lite xeus-python "xeus-javascript>=0.3.1" xtl "ipython=8.22.2=py311had7285e_1" "traitlets>=5.14.2" + xeus xeus-lite xeus-python "xeus-javascript>=0.3.2" xtl "ipython=8.22.2=py311had7285e_1" "traitlets>=5.14.2" else echo "Wasm env $WASM_ENV_NAME already exists" fi @@ -74,7 +74,7 @@ else fi -if false; then +if true; then echo "Building xeus-python" cd $THIS_DIR @@ -111,33 +111,32 @@ else echo "Skipping build xeus-python" fi -# no need to build xeus-javascript, the distributed version is fine -# if false; then -# echo "Building xeus-javascript" +if false; then + echo "Building xeus-javascript" -# cd $THIS_DIR -# source $EMSDK_DIR/emsdk_env.sh + cd $THIS_DIR + source $EMSDK_DIR/emsdk_env.sh -# cd ~/src/xeus-javascript -# mkdir -p build_wasm -# cd build_wasm + cd ~/src/xeus-javascript + mkdir -p build_wasm + cd build_wasm -# export PREFIX=$WASM_ENV_PREFIX -# export CMAKE_PREFIX_PATH=$PREFIX -# export CMAKE_SYSTEM_PREFIX_PATH=$PREFIX + export PREFIX=$WASM_ENV_PREFIX + export CMAKE_PREFIX_PATH=$PREFIX + export CMAKE_SYSTEM_PREFIX_PATH=$PREFIX -# emcmake cmake .. \ -# -DCMAKE_BUILD_TYPE=Release \ -# -DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ON \ -# -DCMAKE_INSTALL_PREFIX=$PREFIX \ -# -DXPYT_EMSCRIPTEN_WASM_BUILD=ON\ + emcmake cmake .. \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ON \ + -DCMAKE_INSTALL_PREFIX=$PREFIX \ + -DXPYT_EMSCRIPTEN_WASM_BUILD=ON\ -# emmake make -j8 install -# else -# echo "Skipping build xeus-javascript" -# fi + emmake make -j8 install +else + echo "Skipping build xeus-javascript" +fi diff --git a/docs/JavaScript_API.md b/docs/JavaScript_API.md index 6d02a28..f26a7c0 100644 --- a/docs/JavaScript_API.md +++ b/docs/JavaScript_API.md @@ -17,17 +17,111 @@ print(numpy.random.rand(3)) ### `eval` Evaluate a string with a Python expression. + +Example: +```javascript +const result = pyjs.eval("sum([i for i in range(100)])") +console.log(result); // 4950 +``` + ### `async_exec_eval` Schedule the execution of a string of Python code and return a promise. The last expression is returned as the result of the promise. + +Example: +```javascript +const py_code = ` +import asyncio +await asyncio.sleep(2) +sum([i for i in range(100)]) +` +result = await pyjs.async_exec_eval(py_code) +console.log(result); // 4950 +``` + ### `eval_file` -Evaluate a file with Python code. +Evaluate a file from the virtual file system. + +Example: +```javascript +const file_content = ` +import numpy + +def fobar(): + return "fubar" + +def foo(): + return "foo" + +if __name__ == "__main__": + print(fobar()) +` +pyjs.FS.writeFile("/hello_world.py", file_content); + +// evaluate the file +// print "fubar" +pyjs.eval_file("/hello_world.py") + +// use content from files scope +// prints foo +pyjs.eval("foo()") ; +``` + ### `pyobject` -A Python object exported as a JavaScript class +A Python object exported as a JavaScript class. +In Python, allmost everything is an object. This class holds the Python object +compiled to JavaScript. + + + + #### `py_call` Call the `__call__` method of a Python object. + +Example: +```javascript +const py_code = ` +class Foobar: + def __init__(self, foo): + self.foo = foo + def bar(self): + return f"I am {self.foo}" + + def __call__(self, foobar): + print(f"called Foobar.__call__ with foobar {foobar}") + +# last statement is returned +Foobar +` + +// py_foobar_cls is a pyobject on +// the JavaScript side and the class Foobar +// on the Python side +var py_foobar_cls = pyjs.exec_eval(py_code) + +// all function call-like statements (ie Foobar(2) need to be done via py_call) +var instance = py_foobar_cls.py_call(2) +// prints 2 +console.log(instance.foo) + +// prints "I am 2" +console.log(instance.bar.py_call()) + +// prints called Foobar.__call__ with foobar 42 +instance.py_call(42) +``` + #### `py_apply` Call the `__call__` method of a Python object with an array of arguments. + #### `get` -Get an attribute of a Python object. +call the bracket operator `[]` of a Python object. +Example: +```javascript +const arr = pyjs.exec_eval("import numpy;numpy.eye(2)"); +console.log(arr.get(0,0)) // prints 1 +console.log(arr.get(0,1)) // prints 0 +console.log(arr.get(1,0)) // prints 0 +console.log(arr.get(1,1)) // prints 1 +``` \ No newline at end of file diff --git a/include/pyjs/pre_js/init.js b/include/pyjs/pre_js/init.js index 5274c89..c819f09 100644 --- a/include/pyjs/pre_js/init.js +++ b/include/pyjs/pre_js/init.js @@ -114,7 +114,10 @@ Module['init_phase_1'] = async function(prefix, python_version) { } }; - + // [Symbol.toPrimitive](hint) for pyobject + Module['pyobject'].prototype[Symbol.toPrimitive] = function(hint) { + return this.__toPrimitive(); + }; diff --git a/include/pyjs/pre_js/make_proxy.js b/include/pyjs/pre_js/make_proxy.js index e0a7501..4415e2d 100644 --- a/include/pyjs/pre_js/make_proxy.js +++ b/include/pyjs/pre_js/make_proxy.js @@ -1,5 +1,4 @@ Module['make_proxy'] = function(py_object) { - // return py_object; const handler = { get(target, property, receiver) { var ret = target[property] diff --git a/src/convert.cpp b/src/convert.cpp index 5ec97b7..0597604 100644 --- a/src/convert.cpp +++ b/src/convert.cpp @@ -247,7 +247,7 @@ namespace pyjs if (stride != 1) { std::stringstream s; - s << "only continous arrays are allowe but stride is " << stride << " raw stride " + s << "only continous arrays are allowed but stride is " << stride << " raw stride " << info.strides[0] << " itemsize " << itemsize << " shape " << info.shape[0]; throw std::runtime_error(s.str().c_str()); } diff --git a/src/export_py_object.cpp b/src/export_py_object.cpp index ec39087..bf295f7 100644 --- a/src/export_py_object.cpp +++ b/src/export_py_object.cpp @@ -188,7 +188,7 @@ namespace pyjs std::cout << "unhanded error: " << e.what() << "\n"; } catch(...){ - std::cout<<"catched something...\n"; + std::cout<<"catched unhanded something.\n"; } })) @@ -202,16 +202,6 @@ namespace pyjs } })) - // .function("__usafe_void_val_val__", - // em::select_overload( - // [](py::object& pyobject, em::val val1, em::val val2) - // { - // { - // py::gil_scoped_acquire acquire; - // pyobject(val1, val2); - // } - // })) - .function("_raw_getattr", em::select_overload( [](py::object& pyobject, const std::string& attr_name) -> em::val @@ -234,6 +224,24 @@ namespace pyjs return ret; } })) + .function("__toPrimitive", + em::select_overload( + [](py::object& pyobject) -> em::val + { + auto numbers = py::module::import("numbers"); + if(py::isinstance(pyobject, numbers.attr("Number"))) + { + py::float_ pyf = pyobject.cast(); + const auto d = pyf.cast(); + return em::val(d); + } + else{ + const auto py_str = py::str(pyobject); + const std::string str = py_str.cast(); + return em::val(str); + } + }) + ) .function("toString", em::select_overload( @@ -255,23 +263,9 @@ namespace pyjs } catch (py::error_already_set& e) { - return em::val(py::str(pyobject).cast()); - } - }) - ) - .function("toJSON", - em::select_overload( - [](py::object& pyobject) -> em::val - { - auto json_module = py::module::import("json"); - auto json_dumps = json_module.attr("dumps"); - try{ - auto json_str = em::val(json_dumps(pyobject).cast()); - return json_str; - } - catch (py::error_already_set& e) - { - return em::val(py::str(pyobject).cast()); + const auto py_str = py::str(pyobject); + const std::string str = py_str.cast(); + return em::val(str); } }) ) diff --git a/src/js_timestamp.cpp b/src/js_timestamp.cpp index f56c7ab..de4138e 100644 --- a/src/js_timestamp.cpp +++ b/src/js_timestamp.cpp @@ -1 +1 @@ -#define PYJS_JS_UTC_TIMESTAMP "2024-03-15 12:48:37.105024" \ No newline at end of file +#define PYJS_JS_UTC_TIMESTAMP "2024-03-19 09:56:38.004136" \ No newline at end of file