Git Product home page Git Product logo

jspybridge's Introduction

JSPyBridge

NPM version PyPI Build Status Gitpod ready-to-code

Interoperate Node.js and Python. You can run Python from Node.js, or run Node.js from Python. Work in progress.

Requires Node.js 18 and Python 3.8 or newer.

Key Features

  • Ability to call async and sync functions and get object properties with a native feel
  • Built-in garbage collection
  • Bidirectional callbacks with arbitrary arguments
  • Iteration and exception handling support
  • Object inspection allows you to easily console.log or print() any foreign objects
  • (Bridge to call Python from JS) Python class extension and inheritance. See pytorch and tensorflow examples.
  • (Bridge to call JS from Python) Native decorator-based event emitter support
  • (Bridge to call JS from Python) First-class Jupyter Notebook/Google Colab support. See some Google Colab uses below.

Basic usage example

See some examples here. See documentation below and in here.

Access JavaScript from Python

pip3 install javascript
from javascript import require, globalThis

chalk, fs = require("chalk"), require("fs")

print("Hello", chalk.red("world!"), "it's", globalThis.Date().toLocaleString())
fs.writeFileSync("HelloWorld.txt", "hi!")

Access Python from JavaScript

Make sure to have the dependencies installed before hand!

npm i pythonia
import { python } from 'pythonia'
// Import tkinter
const tk = await python('tkinter')
// All Python API access must be prefixed with await
const root = await tk.Tk()
// A function call with a $ suffix will treat the last argument as a kwarg dict
const a = await tk.Label$(root, { text: 'Hello World' })
await a.pack()
await root.mainloop()
python.exit() // Make sure to exit Python in the end to allow node to exit. You can also use process.exit.

Examples

Gitpod ready-to-code

Check out some cool examples below! Try them on Gitpod! Click the Open in Gitpod link above, and then open the examples folder.

PyTorch numpy tensorflow mineflayer

Bridge feature comparison

Unlike other bridges, you may notice you're not just writing Python code in JavaScript, or vice-versa. You can operate on objects on the other side of the bridge as if the objects existed on your side. This is achieved through real interop support: you can call callbacks, and do loss-less function calls with any arguments you like (with the exception of floating points percision of course).

python(ia) bridge javascript bridge npm:python-bridge
Garbage collection โœ” โœ” โŒ
Class extension support โœ” Not built-in (rare use case), can be manually done with custom proxy โŒ
Passthrough stdin โŒ (Standard input is not piped to bridge processes. Instead, listen to standard input then expose an API on the other side of the bridge recieve the data.) โŒ โœ”
Passthrough stdout, stderr โœ” โœ” โœ”
Long-running sync calls โœ” โœ” โœ”
Long-running async calls โŒ (need to manually create new thread) โœ” (AsyncTask) โŒ (need to manually create new thread)
Callbacks โœ” โœ” โŒ
Call classes โœ” โœ”
Iterators โœ” โœ” โŒ
Inline eval โœ” โœ”
Dependency Management โŒ โœ” โŒ
Local File Imports โœ” โœ” โŒ
Error Management โœ” โœ” โœ”
Object inspection โœ” โœ” โŒ

Who's using it

Documentation

From Python

You can import the bridge module with

from javascript import require

This will import the require function which you can use just like in Node.js. This is a slightly modified require function which does dependency management for you. The first paramater is the name or location of the file to import. Internally, this calls the ES6 dynamic import() function. Which supports both CommonJS and ES6 modules.

If you are passing a module name (does not start with / or include a .) such as 'chalk', it will search for the dependency in the internal node_module folder and if not found, install it automatically. This install will only happen once, it won't impact startup afterwards.

The second paramater to the built-in require function is the version of the package you want, for example require('chalk', '^3') to get a version greater than major version 3. Just like you would if you were using npm install. It's reccomended to only use the major version as the name and version will be internally treated as a unique package, for example 'chalk--^3'. If you leave this empty, we will install latest version instead, or use the version that may already be installed globally.

Usage

  • All function calls to JavaScript are thread synchronous
  • ES6 classes can be constructed without new
  • ES5 classes can be constructed with the .new psuedo method
  • Use @On decorator when binding event listeners. Use off() to disable it.
  • All callbacks run on a dedicated callback thread. DO NOT BLOCK in a callback or all other events will be blocked. Instead:
  • Use the @AsyncTask decorator when you need to spawn a new thread for an async JS task.

For more, see docs/python.md.

Usage

๐Ÿ‘‰ Click here to see some code usage examples ๐Ÿ‘ˆ

Basic import

Let's say we have a file in JS like this called time.js ...

function whatTimeIsIt() {
    return (new Date()).toLocaleString()
}
module.exports = { whatTimeIsIt }

Then we can call it from Python !

from javascript import require
time = require('./time.js')
print(time.whatTimeIsIt())

Event emitter

You must use the provided On, Once, decorator and off function over the normal dot methods.

emitter.js

const { EventEmitter } = require('events')
class MyEmitter extends EventEmitter {
    counter = 0
    inc() {
        this.emit('increment', ++this.counter)
    }
}
module.exports = { MyEmitter }

listener.py

from javascript import require, On, off
MyEmitter = require('./emitter.js')
# New class instance
myEmitter = MyEmitter()
# Decorator usage
@On(myEmitter, 'increment')
def handleIncrement(this, counter):
    print("Incremented", counter)
    # Stop listening. `this` is the this variable in JS.
    off(myEmitter, 'increment', handleIncrement)
# Trigger the event handler
myEmitter.inc()

ES5 class

es5.js

function MyClass(num) {
    this.getNum = () => num
}
module.exports = { MyClass }

es5.py

MyEmitter = require('./es5.js')
myClass = MyClass.new(3)
print(myClass.getNum())

Iteration

items.js

module.exports = { items: [5, 6, 7, 8] }

items.py

items = require('./items.js')
for item in items:
    print(item)

Callback

callback.js

export function method(cb, salt) {
    cb(42 + salt)
}

callback.py

method = require('./callback').method
# Example with a lambda, but you can also pass a function ref
method(lambda v: print(v), 2) # Prints 44

From JavaScript

  • All the Python APIs are async. You must await them all.
  • Use python.exit() or process.exit() at the end to quit the Python process.
  • This library doesn't manage the packaging.
    • Right now you need to install all the deps from pip globally, but later on we may allow loading from pip-envs.
  • When you do a normal Python function call, you can supply "positional" arguments, which must be in the correct order to what the Python function expects.
  • Some Python objects accept arbitrary keyword arguments. You can call these functions by using the special $ function syntax.
    • When you do a function call with a $ before the parenthesis, such as await some.pythonCall$(), the final argument is evaluated as a kwarg dictionary. You can supply named arguments this way.
  • Property access with a $ at the end acts as a error suppression operator.
    • Any errors will be ignored and instead undefined will be returned
  • See docs/javascript.md for more docs, and the examples for more info

Usage

๐Ÿ‘‰ Click here to see some code usage examples ๐Ÿ‘ˆ

Basic import

Let's say we have a file in Python like this called time.py ...

import datetime
def what_time_is_it():
  return str(datetime.datetime.now())

Then we can call it from JavaScript !

import { python } from 'pythonia'
const time = await python('./time.py')
console.log("It's", await time.what_time_is_it())
python.exit()

Iterating

  • When iterating a Python object, you must use a for await loop instead of a normal for-of loop.

iter.py

import os
def get_files():
  for f in os.listdir():
    yield f

iter.js

const iter = await python('./iter.py')
const files = await iter.get_files()
for await (const file of files) {
  console.log(file)
}

Extra details

  • When doing a function call, any returned foreign objects will be sent to you as a reference. For example, if you're in JavaScript and do a function call to Python that returns an array, you won't get a JS array back, but you will get a reference to the Python array. You can still access the array normally with the [] notation, as long as you use await.

  • This behavior makes it very fast to pass objects directly between same-language functions, avoiding costly cross-language data transfers.

  • However, this does not apply with callbacks or non-native function input parameters. The bridge will try to serialize what it can, and will give you a foreign reference if it's unable to serialize something. So if you pass a JS object, you'll get a Python dict, but if the dict contains something like a class, you'll get a reference in its place.

  • (On the bridge to call JavaScript from Python) If you would like the bridge to turn a foreign reference to something native, you can use .valueOf() to transfer an object via JSON serialization, or .blobValueOf() to write an object into the communication pipe directly.

    • .valueOf() can be used on any JSON-serializable object, but may be very slow for big data.
    • .blobValueOf() can be used on any pipe-writeable object implementing the length property (e.g. Buffer). It can be massively faster by circumventing the JSON+UTF8 encode/decode layer, which is inept for large byte arrays.
  • You can use custom Node.js/Python binary paths by setting the NODE_BIN or PYTHON_BIN enviornment variables before importing the library. Otherwise, the node and python3 or python binaries will be called relative to your PATH enviornment variable.

  • The inter-process communication can be inspected by setting the DEBUG env var to jspybridge.

Limitations

  • The ffid keyword is reserved. You cannot use it in variable names, object keys or values as this is used to internlly track objects.

  • On the bridge to call JavaScript from Python, due to the limiatations of Python and cross-platform IPC, we currently communicate over standard error which means that specific output in JS standard error can interfere with the bridge (as of this writing, the prefices {"r" and blob! are reserved). A similar issue exists on Windows with Python. You are however very unlikely to have issues with this.

  • Function calls will timeout after 100000 ms and throw a BridgeException error. That default value can be overridden by defining the new value of REQ_TIMEOUT in an environment variable, and setting it to 0 will disable timeout checks.

jspybridge's People

Contributors

ableytner avatar extremeheat avatar heath123 avatar import-bot avatar jc-roman avatar mara004 avatar marcusds avatar mmerce avatar mrdanpsmith avatar peticali avatar pickleboyonline avatar rom1504 avatar tales-carvalho avatar thearchitector avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jspybridge's Issues

Local python module not loadable via javascript

I copied a python module in my folder and not installed via pip
With a python script this import ist working but not via pythonia in javasript

Folder:

module_name
     __init__.py
    code.py
python.py
javascript.js

python.py:

from module_name import connect
connection = connect(
    account =user,
    password=pass,
)

On javascript this is not working
javasript.js

import { python } from "pythonia";
const module_name = await python("module_name");

const connection = await module_name.connect({
    account: "mail",
    password: "pass",
});
python.exit(); // Make sure to exit Python in the end to allow node to exit. You can also use process.exit.
... across the bridge ...

> return _bootstrap._gcd_import(name[level:], package, level)
  at import_module (/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/importlib/__init__.py:127)
  at _gcd_import (<frozen importlib._bootstrap>:1014)
  at _find_and_load (<frozen importlib._bootstrap>:991)
  at _find_and_load_unlocked (<frozen importlib._bootstrap>:973)
๐ŸŒ‰ ModuleNotFoundError: No module named 'module_name']
 % Exception ignored in: <function Proxy.__del__ at 0x7fb21a10dca0>
Traceback (most recent call last):
  File "node_modules/pythonia/src/pythonia/proxy.py", line 226, in __del__
  File "node_modules/pythonia/src/pythonia/proxy.py", line 111, in free
  File "node_modules/pythonia/src/pythonia/Bridge.py", line 273, in queue_request
  File "node_modules/pythonia/src/pythonia/interface.py", line 19, in queue
SystemExit: 1

Used module:
https://github.com/nbogojevic/midea-beautiful-air/tree/main/midea_beautiful

Attempting to load pdfjs library fails with syntax error

I would like to use the pdf.js library from Python. This is how I tried to load it with JSPyBridge:

>>> import javascript
>>> pdfjsLib = javascript.require("pdfjs-dist")

It fails with the following error:

Error in exception handler
[Errno 2] No such file or directory: 'internal/modules/cjs/loader.js'
** JavaScript Stacktrace **
/home/mara/node_modules/pdfjs-dist/build/pdf.js:2440
    return this._jsActionsPromise ||= this._transport.getPageJSActions(this._pageIndex);
                                  ^^^

SyntaxError: Unexpected token '||='
    at wrapSafe (internal/modules/cjs/loader.js:1001:16)
    at Module._compile (internal/modules/cjs/loader.js:1049:27)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
    at Module.load (internal/modules/cjs/loader.js:950:32)
    at Function.Module._load (internal/modules/cjs/loader.js:790:12)
    at ModuleWrap.<anonymous> (internal/modules/esm/translators.js:199:29)
    at ModuleJob.run (internal/modules/esm/module_job.js:183:25)
    at async Loader.import (internal/modules/esm/loader.js:178:24)
    at async Object.$require [as require] (/home/mara/.local/lib/python3.8/site-packages/javascript/js/deps.js:128:15)
    at async Bridge.call (/home/mara/.local/lib/python3.8/site-packages/javascript/js/bridge.js:136:17)
** Python Stacktrace **
  File "<stdin>", line 1, in <module>

  File "/home/mara/.local/lib/python3.8/site-packages/javascript/__init__.py", line 37, in require
    return config.global_jsi.require(name, version, calling_dir, timeout=900)

  File "/home/mara/.local/lib/python3.8/site-packages/javascript/proxy.py", line 213, in __call__
    else self._exe.callProp(

  File "/home/mara/.local/lib/python3.8/site-packages/javascript/proxy.py", line 153, in callProp
    resp = self.pcall(ffid, "call", method, args, timeout=timeout, forceRefs=forceRefs)

  File "/home/mara/.local/lib/python3.8/site-packages/javascript/proxy.py", line 141, in pcall
    raise JavaScriptError(attr, res["error"])

Create the python package too

https://github.com/rom1504/taming-transformers/blob/master/.github/workflows/python-publish.yml is a good way to release when a GitHub release is created (put __token__ as user secret, and get a token in pypi for the password)

https://github.com/PrismarineJS/mineflayer/blob/master/.github/workflows/npm-publish.yml if you also have something like this then the release procedure will be to create a commit named "Release x.y.z" containing version update to package.json and setup.py (and maybe a change log) and then everything will be automatic:

  • npm publish will publish to npm and create a GitHub release
  • that will trigger pypi release which will release to pypi

(For the npm one you also need to put some npm token in secret)

How does exec work?

Looks like an amazing project. Thanks for creating it!

I was playing around with it a little bit but could not figure out how exec works.

The following works fine:

await py.exec('print("test")');

but this:

await py.exec('print(a)', { a: 2 });

results in the following error:
NameError: name 'p__0rint' is not defined]

So I assume I use it wrong but can not figure out or find an example of how to do it correctly. Thanks a lot!

How to load a class in Python from a Typescript file ?

Hi there!

I was just trying out JSPyBridge and I wanted to load a class in my Python file from a Typescript file. But I got an error "Unknown file extension '.tsx' ".

I just wanted to know if that's normal, or if there were some workaround ?

Node version : v16.14.2

My Typescript file :

class Animal {
    name: string;
    age: number;

    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }

    makeSound(): void {
        console.log(`${this.name} makes a sound.`);
    }
}

My Python file :

from javascript import require
animal = require('./animal.tsx')
x = animal.Animal(name="xxx", age=8)
print(x)

And here's the stacktrace :

โ˜•  JavaScript Error  Call to 'require' failed:
> animal = require('./animal.tsx')
  at <module> (C:\Users\Utilisateur\PycharmProjects\bridge\main.py:2)
> 

... across the bridge ...

  at processTicksAndRejections (node:internal/process/task_queues:96:5)
  at ESMLoader.getModuleJob (node:internal/modules/esm/loader:261:34)
  at ESMLoader.#createModuleJob (node:internal/modules/esm/loader:297:17)
  at new ModuleJob (node:internal/modules/esm/module_job:66:26)
  at ESMLoader.moduleProvider (node:internal/modules/esm/loader:280:58)
  at ESMLoader.load (node:internal/modules/esm/loader:359:26)
  at defaultLoad (node:internal/modules/esm/load:21:14)
  at defaultGetFormat (node:internal/modules/esm/get_format:102:38)
  at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:87:11)
  at new NodeError (node:internal/errors:371:5)
  TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".tsx" for C:\Users\Utilisateur\PycharmProjects\bridge\animal.tsx
> ^
๐ŸŒ‰ TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".tsx" for C:\Users\Utilisateur\PycharmProjects\bridge\animal.tsx

Process finished with exit code 1

Thank you !

using pandas and DataFrame from JS

const pd = await python('pandas')
const df = await pd.DataFrame([{a:1,b:2},{a:123,b:234}])
    
await df.apply$(pd.to_numeric);

I get:

UnhandledPromiseRejectionWarning: Error: Attempt to access 'apply' failed. 
Python didn't respond in time (100000ms)
...
(node:32) UnhandledPromiseRejectionWarning: 
*** PY ***  Python Error  Call to 'to_numeric.$timeout' failed:
>     if (resp.key === 'error') throw new PythonException(stack, resp.sig)
  at Bridge.value (..\node_modules\pythonia\src\pythonia\Bridge.js:274:37)
  at processTicksAndRejections (internal/process/task_queues.js:93:5)

... across the bridge ...

*** JS *** TypeError: 'function' object is not subscriptable

Not sure how to approach this? Not a bug as such, but others may run into this so I thought it worth understanding.

Custom python installations

I'm planning on using this to make a python plugin api but I noticed you have to use python from the windows store. Is there anyway to set a path to a python installation, so I can bundle python with my project? Thanks.

BridgeException (timeout) during long-running async JS call

TLDR: Is there any way to dynamically set the call timeout (i.e. REQ_TIMEOUT in Bridge.js) to a different value, perhaps when instantiating a subclass of PyClass?

Here's my use case:

  • I've created a PDF toolkit in Python (using PyPDF2)
  • I've created a Javascript class (which extends PyClass) to wrap the Python class exactly as instructed in your docs/examples
  • Functionally, everything works perfectly on a small scale, but I'm currently using my JS class on a Node server that has to deal with merging & obtaining page counts for very large PDF files
  • The long-running async calls to my PDF toolkit class for these large files often incur a BridgeException due to the Python API call timing out (the call isn't actually hung, it's just taking longer than 100 seconds)

All problems are fixed if I increase REQ_TIMEOUT to something like 300_000 in my local node_modules, but I would love if I didn't have to change this value manually every time I provision a server w/ my project code.

Is this possible? Or can you suggest any alternative workarounds for long-running async calls from Node?

Question: Why does this library require Node >= 16.x.x?

Tl;Dr - why does this library need Node >= 16.x.x?.

Hi,

I am currently working on a VS Code extension for DVC. To give you some context DVC is a CLI for (not just) Data Version Control in Machine Learning Projects. The CLI is written in Python and the extension is written in Typescript then bundled and run inside of VS Code (which is an Electron app). I am currently looking to improve the integration between the CLI and the extension.

At the moment we use execa to create child_processes which run the CLI as a black box and return JSON. We then parse that JSON and feed it through the extension.

Not being able to access the internals of the Python project has started to become an issue so we have been looking for alternatives. This looks like a solid library but unfortunately, the VS Code Electron app runs on Node 14.17.0 and as far as I am aware there are no plans to change this in the short to medium term.

What would need to be done to make this library compatible with an earlier version of Node? Is that even possible?

Thanks,
Matt

Mac OS call to 'require' fails

When trying to run on Mac the following error occurs. The same code works on Windows, so we are unsure what the issue is. The package trying to be used is found in a subfolder called pokemon-showdown, but specifying location does not seem to be necessary on Windows:

  JavaScript Error  Call to 'require' failed:
> javascript.require('pokemon-showdown')
  at <module> (/Users/xxx/Documents/Twogithub/PokemonLearning/main.py:10)
> 

... across the bridge ...

  at importModuleDynamicallyWrapper (node:internal/vm/module:438:21)
  at importModuleDynamically (node:internal/modules/cjs/loader:1151:29)
  at ESMLoader.import (node:internal/modules/esm/loader:525:22)
  at ESMLoader.getModuleJob (node:internal/modules/esm/loader:424:18)
  at ESMLoader.resolve (node:internal/modules/esm/loader:842:30)
  at nextResolve (node:internal/modules/esm/loader:163:28)
  at defaultResolve (node:internal/modules/esm/resolve:1153:11)
  at moduleResolve (node:internal/modules/esm/resolve:938:20)
  at packageResolve (node:internal/modules/esm/resolve:889:9)
  at new NodeError (node:internal/errors:400:5)
  Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'pokemon-showdown' imported from /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/javascript/js/deps.js
> ^
๐ŸŒ‰ Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'pokemon-showdown' imported from /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/javascript/js/deps.js

Process finished with exit code 1

Syntax error on Bridge.js

I'm getting this error on the terminal when I call pythonia.
Details:

const { python } = require('pythonia')
C:\Users\david\node_modules\pythonia\src\pythonia\Bridge.js:57
  async #init (bridge = globalThis.__pythonBridge) {
              ^

Uncaught SyntaxError: Unexpected token '('

JS call from Python doesn't respect .toString() when coercing, can't call static member variables

Doing a JS call from python does not call to string:

class Vec3 {
 x = 0; y = 1; z = 2;
 toString() { return `${x},${y},${z}` }
}

prints as if no toString is present


Also:

class SomeClass {
  static hello() { return "world!" }
}

cannot be called in python as

SomeClass.hello()

this likely is because of https://github.com/extremeheat/JSPyBridge/blob/master/src/JSPyBridge/proxy.py#L158 -- what we could do is extend Proxy in here so normal property access continues to work, or add a isClass parameter to Proxy

when used with http throws SyntaxError at first line (even this is #coding)

so, here is my webserver:

let routeDestination = require(dynPath);
	  console.log(`dynamic load ${dynPath}`);
        if (dynPath.endsWith('.js')) {
            await routeDestination.define(req, res, postData, ctx);
        } else if (dynPath.endsWith('.py')) {
            python(dynPath).then((x) => {
                x.requested(req, res, postData, ctx);
            });
            
        } else {
              // my code }

test.js:

module.exports.define = function(req, res, postData, ctx) {
	res.writeHead(200);
	res.end("Hello world 1");
};

(works fine)

test.py:

	res.writeHead(200)
	res.end('Hello world 200')

throws error at first line
tried with no webserver, everything worked

using python.exit() with import statement

First of all, thank you for creating a nice library. I'm using it well.

However, I face unexpected error when I use JSPyBridge in typescript.

here is code

import { py, python } from 'pythonia'
// const { py, python } = require('pythonia')

export async function getSkewNorm() {
	const ag = await python('antigravity')
	const lorem = await py`print('testing ')`
	python.exit() // error : Property 'exit' does not exist on type '(fileName: string) => any'.ts(2339)
}

this error goes down If I use require instead of import. but I don't want to ruin unity.

can you give me an advice for this?? thanks

Bridged JS Module does not work when placed inside a function

I believe this is more related to my lack of knowledge in Python rather than an issue with JSPyBridge and I would be more than happy to delete my comment if this is out of scope for submitting an issue.

I am trying to use a JS module inside python and to my relief that it works perfectly fine. So first of all thank you for that. My question is that when I place the module inside a function it does not behave accordingly. For example this:

a = web3JS.eth.abi.encodeFunctionCall({
    "name": "name",
    "type": "function",
    "inputs": []
}, [])

works perfectly fine while this:

def encodeBasicFunction(funcName):
    web3JS.eth.abi.encodeFunctionCall({
        "name": funcName,
        "type": "function",
        "inputs": []
    }, [])
a = encodeBasicFunction("name")

does not work.

Like I've said I'm happy to remove my issue if this is too basic/unrelated to JsPyBridge and more of a StackOverflow question.

Cheers!

Question: Periodic BrokenBarrierError

I have an application where I periodically call an async function that lives in JS land, poll().
It calls it from the main runtime of Python, not an async context.
This makes various API calls and returns a promise for the results of those calls.

It works for the most part, but occasionally I need to restart the application because the call to poll results in BrokenBarrierError.
I read through a bit of stdlib.d.ts, and it looks like the cases where this can happen is when something calls reset() or when something calls abort(). Is there a smarter way that I should be handling this?

Should I make sure that all promises on the JS side have returned before trying to inspect them in Python?
Should I have something running in the async Python context to interact with async javascript objects?
How do I minimize my exposure to this issue?

Any guidance you can provide is appreciated!

TypeError: 'set' on proxy: trap returned falsish for property

Say I have a simple Python class like such:

from PyPDF2 import PdfReader

class PdfToolkit:
	def pageCount(self, file: str) -> int:
		return len(PdfReader(file).pages)

And a JS class to wrap it:

import { python, PyClass } from 'pythonia';
const pdfTK = await python('./PdfToolkit.py');

export class PdfToolkit extends PyClass {
	constructor() {
		super(pdfTK.PdfToolkit, [], {});
		this.isOpen = true;
	}

	async pageCount(file) {
		return await this.parent.pageCount(file);
	}

	close() {
		if (this.isOpen) {
			this.isOpen = false;
			python.exit();
		}
	}
}

When I run the test script (testPageCount.js) below:

(async () => {
	const { PdfToolkit } = await import('./PdfToolkit.js');
	const pdfTk = await PdfToolkit.init();
	try {
		const pageCount = await pdfTk.pageCount('tests/inputs/sample1.pdf');
		console.log(`Page count for sample PDF: ${pageCount}`);  // Prints 6
	}
	catch (err) {
		console.error(`Page count failed: ${err}`);
	}
	finally {
		console.debug(`[ PdfToolkit ] ${pdfTk.isOpen ? 'open' : 'closed'}`);  // Prints 'open'
		pdfTk.close();  // Calls `python.exit()`
		console.debug(`[ PdfToolkit ] ${pdfTk.isOpen ? 'open' : 'closed'}`);  // Never reached
	}
})();

This will throw a TypeError where the this.isOpen flag is being set to false in the PdfToolkit.close instance method. Here's the terminal output from running the script:

Page count for sample PDF: 6
[ PdfToolkit ] open
file:///C:/Development/js-py-pdf/PdfToolkit.js:16
                        this.isOpen = false;
                                    ^

TypeError: 'set' on proxy: trap returned falsish for property 'isOpen'
    at Proxy.close (file:///C:/Development/js-py-pdf/PdfToolkit.js:16:16)
    at file:///C:/Development/js-py-pdf/testPageCount.js:14:9
    at processTicksAndRejections (node:internal/process/task_queues:96:5)

Any idea why the error from the Proxy class? Are instance variables not supported for JS classes wrapping Python classes, or perhaps the underlying Python class needs to have the isOpen flag as well?

Is it possible to return value from exec?

This is related to my question in issue #64 but creating a new issue seems cleaner.

Is it possible to return a value from exec? Think in Python you can normally do it by changing a local variable and then accessing it outside of eval but that does not seem to work here.

Calling function with large string parameter blocks forever

To demonstrate this problem, the following code is used:

# saying you have a large html file called "target.html"
from javascript import require
JSDOM = require("jsdom").JSDOM
filepath = "target.html"

with open(filepath,"r") as f:
    html = f.read()
    dom = JSDOM(html) # the step taking forever
    print("dom loaded?", dom)

I can only get this working by writing this in javascript and read the file in the js script, only return the final result to python.

[Question] How to pass a javascript callback correctly?

Hi,

Is it possible to pass a javascript function as callback? (the feature table suggests it)
Somehow my script hangs when doing this:

cat.py

class Cat:
    def __init__(self, name):
        self.name = name  

    def meow(self, x):
        return x(self.name)

test.ts

const t = await python('../cat.py');

const cat = await t.Cat('test');
console.log(await cat.name); // outputs test
const cb = async (s: string) => s.split('').reverse().join('');
console.log(await cat.meow(cb)); // hangs

I'm using typescript.
I couldn't find an example how to do it right. I tried different variations auf using async / await. Sorry if I have overseen it.

Thanks

rgds Sven

Convert Python Object in Javascript/JSON

I'm struggling at the moment to convert python object in a javascript usable object or json

With json dumps I have no idea how I can realize the custom BytesEncoder in Javascript

class BytesEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, bytes):
            return obj.decode('utf-8')
        return json.JSONEncoder.default(self, obj)

json.dumps(states.__dict__, cls=BytesEncoder)

The option via dir(states) is not working because dir is not available and not working via eval

    const states = {};
    const keys = await py`dir(${states})`;
    for await (let [index, key] of await py.enumerate(keys)) {
         if (key.startsWith("__")) {
             continue;
         }
        state[key] = await appliance_state[key]
    }

Is there any example or recommendation?

Python bridge doesn't exit on its own, JS bridge doesn't accept Ctrl-C when waiting for a call


js: The process doesn't exit on its own, and the user needs to manually call python.exit(). Instead, in the GC loop we can check the Node.js event loop if we're the only thing preventing the program from exiting and kill the child process accordingly.

We can also add a python.wait() method to not exit python on its own, just in case the user has a need to keep the process going.

Difficult iteration from Python

Non-array values are hard to iterate in Python.

eval_js(' global.myVar = { a: 3, b: 2, c: 3 } ')

for key in global.myVar:
  print(key)

Throws a big error

/usr/local/lib/python3.7/dist-packages/javascript/proxy.py in __next__(self)
    230 
    231     def __next__(self):
--> 232         if self._ix < self.length:
    233             result = self[self._ix]
    234             self._ix += 1

TypeError: '<' not supported between instances of 'int' and 'NoneType'

This can be fixed by using .valueOf() from python to get a dict.


Some fix considerations:

  • use generator on JS side
  • grab property descriptors on iterator obtain if self.length is undefined and iterate from that in Python

The latter seems fastest. Arrays are likely to have many keys, however for non-arrays it shouldn't be an issue.

What Python is Pythonia Running

I keep getting ModuleNotFoundError: No module named 'geopandas'] even though it is installed on my current version of Python. How do I check what pythonia is using so I can change where it points to?

I tried to make a .env file and change the PYTHON_BIN to where I want it, but still nothing.

If I do:

const os = await python('os')
await os.system('which python')

I get the exact same result as I do in cmd, so I think they are pointing at the same place, however it cannot find that module geopandas.

Then I thought, let me uninstall numpy and see if it will give the same error after the uninstall. However after uninstalling, pythonia still found numpy even though it should have been deleted- so my conclusion is that it is running another version of Python installed somewhere else.

Think how to make this used/known

Packages are better when used by many people.
I think there's a lot of potential for this one.
My ideas (after publishing is done)

  • Make it known by PrismarineJs people
    • have one mineflayer example here
    • have a few python mineflayer examples in mineflayer examples folder directly, and mention that mineflayer can also be used in python
    • announce in discord announcement room that this is a thing
  • Make it known also for other use cases
    • add a keras or pytorch example use in javascript, and list it in readme
    • think about other places where it could be useful and add example here / directly in their repos

I think the PrismarineJs part can be done easily enough. For the "others", it could be interesting too but may need more effort and more ideas.

Respond with JavaScript function result

Hello,

I am sadly stuck again. I have to give access to quite a lot of data to the code in Python. So it is made available via special methods. It seems to work fine in Python. If I execute the code below, it will print in Python correctly { b: 123 }.
When I however directly return it, then I get an error. For some reason I get another error when I follow what the first one recommends

Thanks a lot for your help!

Here is the code to easily reproduce the issue I am facing:

const { python, builtins } = require('pythonia');

async function main() {
	const testData = {
		a: () => {
			return {
				b: 123,
			};
		},
	};

	let response = undefined;

	const callback = (data) => {
		response = data;
	};

	await builtins.exec(
		`def main():
	print(testData['a']());
	return testData['a']()
callback(main())
`,
		{
			callback,
			testData,
		},
	);

	console.log('test1');
	console.log(response);
	// Expected:
	//    { b: 123 }
	// Actual response:
	//    (Some Python object) Use `await object.toString()` to get this object's repr().

	console.log('test2');
	console.log(await response.toString());
	// ERROR:
	// Python Error  Call to '' failed:
}
main().then(() => python.exit());

Uncaught DOMException: Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.

Hi @extremeheat ,

I faced an error while running the code as shown below:

###########
const { py, python } = require("pythonia");
const np = await python("numpy");
const plot = await python("matplotlib.pyplot");

// Fixing random state for reproducibility
await np.random.seed(19680801);
const [mu, sigma] = [100, 15];
const x = await py${mu} + ${sigma} * ${np.random.randn(10000)};

// the histogram of the data
const [n, bins, patches] = await plot.hist$(x, 50, {
density: true,
facecolor: "g",
alpha: 0.75,
});

await plot.xlabel("Smarts");
await plot.ylabel("Probability");
await plot.title("Histogram of IQ");
await plot.text(60, 0.025, "$\mu=100, \sigma=15$");
await plot.xlim(40, 160);
await plot.ylim(0, 0.03);
await plot.grid(true);
await plot.show();
python.exit();
#############

The error shown on console window is as shown below:

Uncaught DOMException: Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.
at StdioCom.writeRaw (C:\directory\node_modules\pythonia\WebsocketCom.js:75:20)
at C:\directory\node_modules\pythonia\Bridge.js:233:52
at C:\directory\node_modules\pythonia\Bridge.js:119:28
at new Promise ()
at waitFor (C:\directory\node_modules\pythonia\Bridge.js:119:5)
at Bridge.call (C:\directory\node_modules\pythonia\Bridge.js:233:24)
at Object.apply (C:\directory\node_modules\pythonia\Bridge.js:411:26)
at python (C:\directory\node_modules\pythonia\index.js:85:17)

May I know the cause of this error? is the socket ws://127.0.0.1:8768/ not working in my computer?
or did I forget to do something at the background?

the error occurred when executing the first line of the code.

[Suggestion] Use the the default stdout `sys.__stdout__` in connection.py in case `sys.stdout` is overwritten

Heya I think this is a super great tool for me to call JS in python! It really helps me a lot.

I encountered a corner case about the stdout when I used the python package javascript. If the sys.stdout is somehow overwritten, an io.UnsupportedOperation: fileno error could occur. Currently, I fixed this by rewriting the sys.stdout to the default stdout before importing:

def load_javascript():
    import sys
    old_stdout = sys.stdout
    sys.stdout = sys.__stdout__
    from javascript import require
    sys.stdout = old_stdout
    return require

So just a little suggestion here: I think the package could be more robust if we replace sys.stdout by std.__sysout__ by in connection.py:

# Special handling for IPython jupyter notebooks
stdout = sys.stdout
notebook = False
NODE_BIN = getattr(os.environ, "NODE_BIN") if hasattr(os.environ, "NODE_BIN") else "node"

The following is the detail about my use case:

If I import the require inside a function or class rather than globally in a python file, like in a.py:

# a.py
def func():
    from javascript import require
    js_mod = require('xxx')

If I run a unit test by unittest calling func in PyCharm:

import unittest
from src.a import func

class TestA(unittest.TestCase):

    def test_a(self):
        func()

There would be an error occurred like:

--====--	--====--

Bridge failed to spawn JS process!

Do you have Node.js 16 or newer installed? Get it at https://nodejs.org/

--====--	--====--
Exception in thread Thread-2:
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/threading.py", line 954, in _bootstrap_inner
    self.run()
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/threading.py", line 892, in run
    self._target(*self._args, **self._kwargs)
  File "venv/lib/python3.9/site-packages/javascript/connection.py", line 122, in com_io
    raise e
  File "venv/lib/python3.9/site-packages/javascript/connection.py", line 111, in com_io
    proc = subprocess.Popen(
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/subprocess.py", line 825, in __init__
    errread, errwrite) = self._get_handles(stdin, stdout, stderr)
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/subprocess.py", line 1596, in _get_handles
    c2pwrite = stdout.fileno()
io.UnsupportedOperation: fileno
Timed out get 0 needsNodePatches <Thread(Thread-3, started daemon 123145607168000)>

And I believe the reason could be that sys.stdout is somehow overwritten to be a FlushingStringIO (which don't have a method fileno()) by either unittest or PyCharm.

Regex expresions

When calling a javascript function that requires a regex expression, it returns an error stating ๐ŸŒ‰ Error: Pattern parameter should be of type RegExp when I put in a r'regex' (python regex).

Namespace name cannot be 'export'

Hi,

I am looking into JSPyBridge because I would like to use Jovo to write skills for voice assistants, but I really don't know anything about Javascript and would like to write most of the logic in Python.

I have added import { python } from 'pythonia'; to the Jovo tutorial example. When I call jovo run, this compiles the Typescript in the Jovo project and its dependencies, including Pythonia:

tsc-watch --onSuccess "node ./dist/app.dev.js --jovo-webhook" --noClear

This call to tsc-watch fails with the following error:

----------------------
8:01:58 PM - Starting compilation in watch mode...

node_modules/pythonia/src/pythonia/py.stdlib.d.ts(1232,16): error TS2819: Namespace name cannot be 'export'.
node_modules/pythonia/src/pythonia/py.stdlib.d.ts(1233,6): error TS1005: ',' expected.
node_modules/pythonia/src/pythonia/py.stdlib.d.ts(1234,2): error TS1005: ',' expected.
node_modules/pythonia/src/pythonia/py.stdlib.d.ts(1234,11): error TS1005: ',' expected.
node_modules/pythonia/src/pythonia/py.stdlib.d.ts(1234,22): error TS1005: ',' expected.
node_modules/pythonia/src/pythonia/py.stdlib.d.ts(1234,43): error TS1109: Expression expected.
node_modules/pythonia/src/pythonia/py.stdlib.d.ts(1234,53): error TS1005: ';' expected.
node_modules/pythonia/src/pythonia/py.stdlib.d.ts(1236,2): error TS1005: '{' expected.
node_modules/pythonia/src/pythonia/py.stdlib.d.ts(1257,1): error TS1128: Declaration or statement expected.

8:02:02 PM - Found 9 errors. Watching for file changes.

I understand that you are probably not familiar with Jovo, but perhaps this error message is already informative for you? It looks like "export" is a reserved keyword in Typescript, but you are using it as a module name in py.stdlib.d.ts.

I apologize for the slightly messy issue - I really don't understand what I'm doing on the Javascript side.

I use Node 14.15.2, [email protected], and [email protected].

cannot import name 'require' from 'javascript'

When I import the javascript module in python, there is no require function.

The only objects without a '_' before their name are: choice, null, sys, and undefined. choice is the choice function from the random module from python, and sys is the sys module from python. Attempting to access null raises a ValueError saying 'Null is not empty'.

I suspect that undefined is merely an accurate representation of the undefined object from JavaScript.

How to use require to import multiple objects

I'm trying to create a WhatsApp webserver using Python and Baileys.
In Baileys documentation for initializing the socket is necessary to import useSingleFileAuthState
For example:
const { delay, DisconnectReason, useSingleFileAuthState } = require('@adiwajshing/baileys')

Captura de Tela 2022-02-03 aฬ€s 10 00 45
Trying to import using useSingleFileAuthState = require('@adiwajshing/baileys') gives this.m[ffid] is not a function
and with delay, DisconnectReason, useSingleFileAuthState = require('@adiwajshing/baileys') too many values to unpack (expected 3) I searched for some examples but didn't find one with multiple imports using JSPyBridge.

Multiple await python calls throw exception

Hello!
I'm actually using Pythonia as bridge to include some python libraries and functions inside my production js server which has a RestFull API for quering db data.

First i'm loading all the python modules i need, then i export all the async functions to distribute them over my application.
When i call pythonia functions in a foreach loop, i got this behaviour i can't solve. (i've also tried with a sync loop, same result)
Some data will be returned by python function and printed, then it stops.

The error:
Schermata 2022-02-16 alle 14 01 59

Here my sample code:

In my Express api
Schermata 2022-02-16 alle 13 59 40

My Pythonia API
Schermata 2022-02-16 alle 13 59 27

Thanks for your attention

UnicodeDecodeError and its possible effects

Greetings, and thank you for this terrific library.

I've lately hit one, perhaps two, problems with Pythonia.

From JavaScript I am calling a function in a dynamically loaded Python script. I pass an array of strings argument to Python. I get this result:

Error: Attempt to access 'gen_enrichment_object' failed. Python didn't respond in time (100000ms), look above for any Python errors. If no errors, the API call hung

Looking 'above' for any errors, I see this:

Traceback (most recent call last):
  File "/data/hermes/node_modules/pythonia/src/pythonia/interface.py", line 50, in <module>
    com_io()
  File "/data/hermes/node_modules/pythonia/src/pythonia/interface.py", line 39, in com_io
    data = apiin.readline()
  File "/usr/lib64/python3.6/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 202: ordinal not in range(128)

But please note that this 'above' error happens exactly once, but I continue to see the first error ("...didn't respond time...") in subsequent calls to the Python function. That is, it seems as if the one "can't decode" error might be affecting all subsequent calls despite the fact that the input to the function is different with each call.

Thank you for your guidance on this problem.

@AsyncTask(start=True)

Hi !, someone to provide a tiny working sample on this feature - @AsyncTask - , looks great , but I can't see the whole picture .
Working with IOT projects , and this library helps me a lot , great job !
Thanks in advance !

py: Not possible to install `require` dep from git

When calling require from python, it's very difficult to install packages from git. The second version parameter appends some npm things, these should not be appended if the version includes a "/" or a ":".

require('minecraft-packets', 'PrismarineJS/node-minecraft-packets#fix-ram-issues')

Should be parsed as : npm install minecraft-packets-<hash>@PrismarineJS/node-minecraft-packets#fix-ram-issues

When, why and how to use `valueOf()`?

I'm using pythonia, and my library code makes use of a handful of callbacks that pass arbitrary data between them. To get pythonia to work, I've had to use this code in a few places:

const ret = await python_function()
try {
  return await ret.valueOf()
} catch(e) {
  return ret
}

and on the flipside:

orig_cb = js_obj['callback']
def cb(x):
  ret = orig_cb(x)
  try:
    return ret.valueOf()
  except:
    return ret
js_obj['callback'] = cb

I was hoping to grok why it works this way, and if there's a better solution I should be using?

Also I am passing a nodejs Buffer through to a python function, but it's coming through as a dict with keys type and data - so the python code needs to buf = bytes(buf['data']), is that something that should also be deserialized to bytes by python before invoking the function?

Cheers!

Error in exception handler (pythonia)

In the src/pythonia/errors.js::processJSStacktrace function, I see that there is an attempt to call fs.readFileSync on the source file which incurred the error.

This works fine and well unless the Javascript call is made from within a top-level or anonymous async function (e.g. (async () => { ... })();), or if the call is made from the REPL (and yes, async is supported in the REPL if run with the option --experimental-repl-await).

In these cases, the fpath var in that processJSStacktrace function is detected as "async myScript.js" or "async repl.js", respectively.

It goes without saying that fs.readFileSync("async myScript.js") would fail due to the invalid file path ascertained for the calling script "myScript.js". If reading the source file is entirely necessary for building the stacktrace output here, then perhaps that function in the errors.js module could be ammended like so?

function processJSStacktrace (stack, allowInternal) {
  const jsTraceLines = []
  let jsErrorline
  let foundMainLine = false
  for (const line of stack.split('\n')) {
    if (!(line.includes('pythonia') && !allowInternal) && !foundMainLine) {
      const absPath = line.match(/\((.*):(\d+):(\d+)\)/)
      const filePath = line.match(/(file:\/\/.*):(\d+):(\d+)/)
      const barePath = line.match(/at (.*):(\d+):(\d+)$/)
      const path = absPath || filePath || barePath
      if (path) {
        let [fpath, errline, char] = path.slice(1)
        if (fpath.startsWith('node:') || fpath.startsWith('internal/')) continue
        else if (fpath.startsWith('async ')) fpath = fpath.slice(6)    // <= ADDED LINE HERE
        const file = fs.readFileSync(fpath.startsWith('file:') ? new URL(fpath) : fpath, 'utf-8')
        const flines = file.split('\n')
        jsErrorline = flines[errline - 1]
        jsTraceLines.push(line.trim())
        foundMainLine = true
      }
    } else if (foundMainLine) {
      jsTraceLines.push(line.trim())
    }
  }
  return jsErrorline ? [jsErrorline, jsTraceLines] : null
}

Random object gets printed to the console and python freezes

The problem

Console randomly prints an object
The object in question:
{"r": 23, "c": "jsi", "p": 1, "action": "call", "ffid": 11, "key": "", "args": [12, {"ffid": 34}, {"ffid": 35}]}
This object can very each run, sometimes the problem doesn't even happen!
After this is printed python gets held up and after 100000ms pythonia throws an error:

node:internal/event_target:1011
 process.nextTick(() => { throw err; });
                          ^
Error: Attempt to access '__call__' failed. Python didn't respond in time (100000ms), look above for any Python errors. If no errors, the API call hung.
   at C:\Users\AirplaneGobrr\Documents\GitHub\ai-gen\node_modules\.pnpm\[email protected][email protected]\node_modules\pythonia\src\pythonia\Bridge.js:248:13
   at waitFor (C:\Users\AirplaneGobrr\Documents\GitHub\ai-gen\node_modules\.pnpm\[email protected][email protected]\node_modules\pythonia\src\pythonia\Bridge.js:123:26)        
   at async Bridge.call (C:\Users\AirplaneGobrr\Documents\GitHub\ai-gen\node_modules\.pnpm\[email protected][email protected]\node_modules\pythonia\src\pythonia\Bridge.js:233:18)
   at async t2i (C:\Users\AirplaneGobrr\Documents\GitHub\ai-gen\aiGen\worker.js:81:29)
Emitted 'error' event on Worker instance at:
   at Worker.[kOnErrorMessage] (node:internal/worker:298:10)
   at Worker.[kOnMessage] (node:internal/worker:309:37)
   at MessagePort.<anonymous> (node:internal/worker:205:57)
   at MessagePort.[nodejs.internal.kHybridDispatch] (node:internal/event_target:736:20)
   at MessagePort.exports.emitMessage (node:internal/per_context/messageport:23:28)

image

I think maybe this issue might also be related to #68 ?
I tried to debug it on my own bug have no clue what the problem is or where to even begin looking

Code

Bit of my code:

var images = await (await main.__call__$(prompt, {
    negative_prompt: negPrompt,
    callback: progress,
    guidance_scale: scale,
    num_inference_steps: steps,
    num_images_per_prompt: images,
    height: height,
    width: width
})).images

"Main" is from a StableDiffusionPipeline pretrained pipe AKA:

const pipe = await dif.StableDiffusionPipeline.from_pretrained$(moduleToLoad, { torch_dtype: await torch.float16 })
const main = await pipe.to("cuda")

Dif being the diffusers and torch being torch

Add support for Yarn 3

Getting this error when running latest yarn versions:

$ yarn --version
3.2.1
const { python } = require('pythonia')

python3: can't open file '/home/pomatti/nodejs-python-interop/.yarn/__virtual__/pythonia-virtual-8228fabc54/0/cache/pythonia-npm-1.0.0-bf636a6ffb-96a24ebdbf.zip/node_modules/pythonia/src/pythonia/interface.py': [Errno 2] No such file or directory

Calling function returned from complex object

Hello,

sadly have one last problem for which I can not figure out what is going on. Would be amazing if you could help me with finding a solution. Thanks a lot for your help!

Here is the code to easily reproduce the issue I am facing:

const { python, builtins } = require('pythonia');

async function main() {
	const proxy = {
		getValue: (parameter1) => {
			return new Proxy(
				{},
				{
					ownKeys(target) {
						return ['first', 'second'];
					},
					getOwnPropertyDescriptor(k) {
						return {
							enumerable: true,
							configurable: true,
						};
					},
					get(target, property, receiver) {
						if (property === 'first') {
							return 'VALUE-first';
						}
						if (property === 'second') {
							return () => {
								return 'VALUE-second';
							};
						}
						return Reflect.get(target, property, receiver);
					},
				},
			);
		},
	};

	await builtins.exec("print(proxy['getValue']('some-key').first)", {
		proxy,
	});
	// Returns as expected:
	//     VALUE-first

	await builtins.exec("print(proxy['getValue']('some-key').second)", {
		proxy,
	});
	// Returns as expected:
	//     [Function (anonymous)]

	await builtins.exec("print(proxy['getValue']('some-key').second())", {
		proxy,
	});
	// Expected:
	//     VALUE-second
	// Actual response:
	//     ERROR
	//     Python Error  Call to 'exec' failed:
	//         await builtins.exec("print(proxy['getValue']('some-key').second())", {
	//      at async main (/data/n8n-python/packages/nodes-base/nodes/PythonFunction/test-node-reference-issue-post.js:44:2)
	// ...     across the bridge ...
}
main().then(() => python.exit());

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.