Use the pmjs shell
mainThe pmjs shell is a basic JavaScript shell that ships with PythonMonkey. It functions similarly to the node shell, acting as a REPL or a way to run JavaScript programs.
# Conceptually similar to node
pmjsrepository·main·Indexed 21 days ago
https://github.com/distributive-network/pythonmonkeyPythonMonkey is a Mozilla SpiderMonkey JavaScript engine embedded into the Python runtime, enabling high-performance, zero-copy interoperability between JavaScript and Python. It allows developers to use JS libraries in Python and vice-versa through features like the eval() function, CommonJS module support via require(), and intelligent type coercion for strings, arrays, and objects. The project includes the pmjs shell and pmdb debugger.
The pmjs shell is a basic JavaScript shell that ships with PythonMonkey. It functions similarly to the node shell, acting as a REPL or a way to run JavaScript programs.
# Conceptually similar to node
pmjsPythonMonkey provides seamless, high-performance data interchange between Python and JavaScript without serialization or pipes. Both engines run in the same process.
Array methods work on Python list objects via a JS API Proxy.Object methods work on Python dict objects.list and dict are coerced to JS Array and Object respectively.Date objects are represented by Python datetime.datetime objects.null, and undefined are passed by value.PythonMonkey's pmjs starts a CommonJS subsystem with Node.js-like semantics (searching module.paths, understanding package.json, etc.).
You can create CommonJS modules in either JavaScript or Python:
Create a .js file and use the exports object to expose functionality.
Create a .py file and decorate a dictionary named exports with your module's members. This allows the module to be loaded via require() in either JavaScript or Python.
// date-lib.js - require("./date-lib")
const d = new Date();
exports.today = `${d.getFullYear()}-${String(d.getMonth()).padStart(2,'0')}-${String(d.getDay()).padStart(2,'0')}`# date-lib.py - require("./date-lib")
from datetime import date
exports['today'] = date.today()PythonMonkey performs intelligent coercion or wrapping when moving variables between Python and JavaScript.
Fast Transfers (Shared Backing Stores):
PythonMonkey shares memory for ctypes, typed arrays, and strings, making these transfers extremely fast without copying.
Automatic Wrapping: When shared memory isn't possible, PythonMonkey uses wrappers. Updates to an object in JavaScript will reflect in the corresponding Python Dict/List, as JavaScript methods are implemented on Python structures and vice-versa.
Type Mapping Reference:
| Python Type | JavaScript Type | Proxy Type (JS to Python) |
|---|---|---|
String | string | pythonmonkey.JSStringProxy |
Integer | number | pythonmonkey.bigint (for BigInt) |
Bool | boolean | - |
Function | function | pythonmonkey.JSFunctionProxy / JSMethodProxy |
Dict | object | pythonmonkey.JSObjectProxy |
List | Array | pythonmonkey.JSArrayProxy |
datetime | Date | - |
awaitable | Promise | - |
Error | Error | - |
Buffer | ArrayBuffer | - |
A Program Module (or main module) is a special module in the CommonJS subsystem with the following behaviors:
globalThis.arguments variable is an Array containing the program's argument vector (command-line arguments).$ echo "console.log('hello world')" > my-program.js
$ pmjs my-program.js
hello world
$ When writing functions, follow these patterns for parameters and return types:
nullptr.const references for non-optional inputs. Use std::optional for optional by-value inputs.const references for non-optional output/IO parameters. Use non-const pointers for optional output/IO parameters.[[nodiscard]] on their own line.PythonMonkey uses a specific hierarchy for naming and scoping to maintain consistency across C and C++ code:
DCP.include and source directories.DCP_NamespaceName_StructName_macroName.static for definitions that do not need to be referenced outside the current source file (do not use these in header files).using directives to import entire namespaces, do not use namespace aliases at namespace scope in headers, and do not declare anything in the std namespace.#if !defined(DCP_NamespaceName_StructName_)
#define DCP_NamespaceName_StructName_
#define DCP_NamespaceName_StructName_macroName(argumentName) !(argumentName)
#define DCP_NamespaceName_StructName_definitionName 42
namespace DCP::NamespaceName {
struct StructName {
int functionName(int argumentName) {
int variableName = argumentName;
return variableName;
}
int memberName;
};
}
#endifPythonMonkey allows you to run JavaScript code within a Python environment. The core mechanism is the eval function, which accepts JavaScript code and returns values coerced into Python types.
Import pythonmonkey and use eval to run a JS string. The result is automatically converted to the appropriate Python type.
import pythonmonkey as pm
# Evaluate a JS arrow function and call it
hello = pm.eval("() => {return 'Hello from Spidermonkey!'}")
print(hello())
# Output: 'Hello from Spidermonkey!'from pythonmonkey import eval as js_eval
# Evaluate console.log and call it with a string
js_eval("console.log")('hello, world')import pythonmonkey as pm
hello = pm.eval("() => {return 'Hello from Spidermonkey!'}")
hello()The pmjs REPL allows for interactive Python and JavaScript execution.
Key features:
.help: Displays a help menu..python prefix.$1, $2, etc., allowing you to access previous results.$ pmjs
Welcome to PythonMonkey v1.0.0.
Type ".help" for more information.
> .python import sys
> .python sys.path
$1 = { '0': '/home/wes/git/pythonmonkey2', ... }
> $1[3]
'/usr/lib/python3.10/lib-dynload'To ensure compliance with the project's requirement for modern C++ and to prevent the use of non-standard extensions, configure your CMakeLists.txt with the following settings. This enforces C++17 and disables compiler-specific extensions.
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD "17")
set(CMAKE_CXX_STANDARD_REQUIRED ON)To maintain a consistent codebase, adhere to these formatting rules:
if, for, do, while, and switch in braces {}.case statements. Use [[fallthrough]]; to annotate intentional fall-throughs.if (
condition && (
anotherCondition || possiblyAnotherCondition
)
) {
do {
++index;
} while (index < total);
} else {
return false;
}To use setTimeout or leverage Promise <=> awaitable coercion, you must have an event-loop running in Python. This is typically done using asyncio.
import asyncio
import pythonmonkey as pm
async def async_fn():
# Using setTimeout in JS
await pm.eval("""
new Promise((resolve) => setTimeout((...args) => {
console.log(args);
resolve();
}, 1000, 42, "abc")
)
""")
# Using Python awaitable in JS
await pm.eval("async (x) => await x")(asyncio.sleep(0.5))
asyncio.run(async_fn())