Embed the Duktape JS interpreter in Python

Overview

Introduction

Pyduktape is a python wrapper around Duktape, an embeddable Javascript interpreter.

On top of the interpreter wrapper, pyduktape offers easy integration between the Python and the Javascript environments. You can pass Python objects to Javascript, call methods on them and access their attributes. Similarly, you can pass Javascript objects to Python.

Objects are never copied or serialized. Instead, they are passed between the two environments using proxy objects. Proxy objects delegate the execution to the original object environment.

Threading

It is possible to invoke Javascript code from multiple threads. Each thread will need to use its own embedded interpreter. Javascript objects returned to the Python environment will only be usable on the same thread that created them. The runtime always checks this condition automatically, and raises a DuktapeThreadError if it's violated.

Getting Started

Installation

To install from pypi:

$ pip install -U setuptools
$ pip install pyduktape

To install the latest version from github:

$ git clone https://github.com/stefano/pyduktape.git
$ cd pyduktape
$ pip install -U setuptools
$ python setup.py install

Running Javascript code

To run Javascript code, you need to create an execution context and use the method eval_js:

import pyduktape

context = pyduktape.DuktapeContext()
context.eval_js("print('Hello, world!');")

Each execution context starts its own interpreter. Each context is independent, and tied to the Python thread that created it. Memory is automatically managed.

To evaluate external Javascript files, use eval_js_file:

// helloWorld.js
print('Hello, World!');

# in the Python interpreter
import pyduktape

context = pyduktape.DuktapeContext()
context.eval_js_file('helloWorld.js')

Pyduktape supports Javascript modules:

// js/helloWorld.js
exports.sayHello = function () {
    print('Hello, World!');
};

// js/main.js
var helloWorld = require('js/helloWorld');
helloWorld.sayHello();

# in the Python interpreter
import pyduktape

context = pyduktape.DuktapeContext()
context.eval_js_file('js/main')

The .js extension is automatically added if missing. Relative paths are relative to the current working directory, but you can change the base path using set_base_path:

# js/helloWorld.js
print('Hello, World!');

# in the Python interpreter
import pyduktape

context = pyduktape.DuktapeContext()
context.set_base_path('js')
context.eval_js_file('helloWorld')

Python and Javascript integration

You can use set_globals to set Javascript global variables:

import pyduktape

def say_hello(to):
    print 'Hello, {}!'.format(to)

context = pyduktape.DuktapeContext()
context.set_globals(sayHello=say_hello, world='World')
context.eval_js("sayHello(world);")

You can use get_global to access Javascript global variables:

import pyduktape

context = pyduktape.DuktapeContext()
context.eval_js("var helloWorld = 'Hello, World!';")
print context.get_global('helloWorld')

eval_js returns the value of the last expression:

import pyduktape

context = pyduktape.DuktapeContext()
hello_world = context.eval_js("var helloWorld = 'Hello, World!'; helloWorld")
print hello_world

You can seamlessly use Python objects and functions within Javascript code. There are some limitations, though: any Python callable can only be used as a function, and other attributes cannot be accessed. Primitive types (int, float, string, None) are converted to equivalent Javascript primitives. The following code shows how to interact with a Python object from Javascript:

import pyduktape

class Hello(object):
    def __init__(self, what):
        self.what = what

    def say(self):
        print 'Hello, {}!'.format(self.what)

context = pyduktape.DuktapeContext()
context.set_globals(Hello=Hello)
context.eval_js("var helloWorld = Hello('World'); helloWorld.say();")

In the same way, you can use Javascript objects in Python. You can use the special method new to instantiate an object:

import pyduktape

context = pyduktape.DuktapeContext()
Hello = context.eval_js("""
function Hello(what) {
    this.what = what;
}

Hello.prototype.say = function () {
    print('Hello, ' + this.what + '!');
};

Hello
""")

hello_world = Hello.new('World')
hello_world.say()

You can use Python lists and dicts from Javascript, and viceversa:

import pyduktape

context = pyduktape.DuktapeContext()
res = context.eval_js('[1, 2, 3]')

for item in res:
    print item

context.set_globals(lst=[4, 5, 6])
context.eval_js('for (var i = 0; i < lst.length; i++) { print(lst[i]); }')

res = context.eval_js('var x = {a: 1, b: 2}; x')
for key, val in res.items():
    print key, '=', val
res.c = 3
context.eval_js('print(x.c);')

context.set_globals(x=dict(a=1, b=2))
context.eval_js("""
var items = x.items();
for (var i = 0; i < items.length; i++) {
    print(items[i][0] + ' = ' + items[i][1]);
}
""")
context.set_globals(x=dict(a=1, b=2))
context.eval_js('for (var k in x) { print(k + ' = ' + x[k]); }')
Comments
  • License clarification and (eventual) re-licensing

    License clarification and (eventual) re-licensing

    Hi,

    We are considering the use of pyduktape for our commercial product but its licensing terms are not very clear, inside this repository we can't find any LICENSE file or Copyright notice for pyduktape.pyx, while the PYPI page [1] lists the package with a GPLv2 license:

    License: GNU General Public License v2 (GPLv2) (GPL)
    Author: Stefano Dissegna
    

    Is GPLv2 the actual license? Eventually, would you consider re-licensing pyduktape under a more commercial-friendly license like BSD, MIT o LGPL?

    We would like to use and contribuite back to it, but as its stands we can't.

    Thanks Michele

    [1] https://pypi.org/project/pyduktape/

    opened by mcella 2
  • Python3 support

    Python3 support

    Unfortunately, it looks like this library doesn't support python3:

    import pyduktape
    pyduktape.DuktapeContext().eval_js('1+2;')
    

    results in

    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "pyduktape.pyx", line 197, in pyduktape.DuktapeContext.eval_js (pyduktape.c:2925)
      File "pyduktape.pyx", line 220, in pyduktape.DuktapeContext._eval_js (pyduktape.c:3656)
      File "pyduktape.pyx", line 195, in pyduktape.DuktapeContext.eval_js.eval_string (pyduktape.c:2795)
    TypeError: expected bytes, str found
    
    opened by p2004a 2
  • FATAL 56: uncaught error - PANIC 56: uncaught error (calling abort)

    FATAL 56: uncaught error - PANIC 56: uncaught error (calling abort)

    Hello.

    I'm using this great library on a Python project named Undying Dusk

    The project consists of some Python code that generates a large PDF file. Its GitHub Actions build pipeline currently fails with this error:

    FATAL 56: uncaught error
    PANIC 56: uncaught error (calling abort)
    

    After reading this article, I suspect that this error message comes from PyDuktape. However, so far I don't quite understand what causes this failure.

    I have opened https://github.com/stefano/pyduktape/pull/9 to try to help debug this issue, and I'll post update of my findings here.

    opened by Lucas-C 1
  • parseInt base 16

    parseInt base 16

    I am testing py_mini_racer(V8) and pyduktape(duktape).

    When i parse string to number base 16 passes the following: V8: n = parsetInt( '09', 16 ); #output = 9 duktape: n = parseInt('09', 16); #output = 0

    I am decrypting and this changes the result. I don't know it is part duktape or pyduktape, greetings.

    opened by aerogelio 1
  • Enabling DUK_OPT_SEGFAULT_ON_PANIC

    Enabling DUK_OPT_SEGFAULT_ON_PANIC

    Quoting: https://usermanual.wiki/Document/Duktape20Programmers20Guide.1419302401/help

    A panic is caused by Duktape assertion code (if included in the build) or by the default fatal error handler. There is no way to induce a panic from user code. The default panic handler writes an error message to stderr and abort()s the process. You can use the DUK_OPT_SEGFAULT_ON_PANIC feature option to cause a deliberate segfault instead of an abort(), which may be useful to get a stack trace from some debugging tools. You can also override the default panic handler entirely with the feature option DUK_OPT_PANIC_HANDLER.

    I'm currently in the process of testing this change

    opened by Lucas-C 0
  • JS print statements & exceptions bypass stdout and stderr

    JS print statements & exceptions bypass stdout and stderr

    I'm trying to use pyduktape to create a JS shell in Python, which means capturing the output from JS calls and printing it to a GUI object in Python. Not sure if this is the intended behaviour, but any output generated by eval_js isn't caught by sys.stdout or sys.stderr - meaning the usual procedure for this sort of thing (using contextlib.rediect_stdout) doesn't work.

    This can be seen with the following code:

    import io
    import pyduktape
    from contextlib import redirect_stdout, redirect_stderr
    
    buffer = io.StringIO()
    context = pyduktape.DuktapeContext()
    
    with redirect_stdout(buffer):
        context.eval_js("print('hello')")
    with redirect_stderr(buffer):
        context.eval_js("print('hello')")
    

    Whether this is me reporting a bug or suggesting a feature depends on how intentional this is, but being able to catch stdout from JS would certainly be handy!

    opened by TEParsons 0
  • Support for Duktape coroutines

    Support for Duktape coroutines

    I had to rewrite the context handling code to allow python callbacks to be invoked from Duktape coroutines. Duktape coroutines share a heap and global object, but each has a distinct context. In the original code, all callbacks were executed inside the context create in the constructor of DuktapeContext(), which led to bugs when using coroutines. I also removed the threading checks, since the same Duktape context can execute code in different threads, just not at the same time.

    opened by bobpepin 0
  • Cannot cleanup globals on a context

    Cannot cleanup globals on a context

    It's not possible to clean all globals on a DuktapeContext. This improvement would help reusing an existing context instead of recreating a new context every time (which seems to lead to a memory leak, but this is something for another issue)

    opened by huubbouma 1
Owner
Stefano
Stefano
Make a command interpreter that manages AirBnb objects

AirBnB Clone Project Description This is part 1 of our AirBnb Clone project. The purpose of this project is to make a command interpreter that manages

Firdaus H. Salim 1 Nov 14, 2021
Official Python client for the MonkeyLearn API. Build and consume machine learning models for language processing from your Python apps.

MonkeyLearn API for Python Official Python client for the MonkeyLearn API. Build and run machine learning models for language processing from your Pyt

MonkeyLearn 157 Nov 22, 2022
PRAW, an acronym for "Python Reddit API Wrapper", is a python package that allows for simple access to Reddit's API.

PRAW: The Python Reddit API Wrapper PRAW, an acronym for "Python Reddit API Wrapper", is a Python package that allows for simple access to Reddit's AP

Python Reddit API Wrapper Development 3k Dec 29, 2022
PRAW, an acronym for "Python Reddit API Wrapper", is a python package that allows for simple access to Reddit's API.

PRAW: The Python Reddit API Wrapper PRAW, an acronym for "Python Reddit API Wrapper", is a Python package that allows for simple access to Reddit's AP

Python Reddit API Wrapper Development 3k Dec 29, 2022
alpaca-trade-api-python is a python library for the Alpaca Commission Free Trading API.

alpaca-trade-api-python is a python library for the Alpaca Commission Free Trading API. It allows rapid trading algo development easily, with support for both REST and streaming data interfaces

Alpaca 1.5k Jan 9, 2023
🖥️ Python - P1 Monitor API Asynchronous Python Client

??️ Asynchronous Python client for the P1 Monitor

Klaas Schoute 9 Dec 12, 2022
Volt is yet another discord api wrapper for Python. It supports python 3.8 +

Volt Volt is yet another discord api wrapper for Python. It supports python 3.8 + How to install [Currently Not Supported.] pip install volt.py Speed

Minjun Kim (Lapis0875) 11 Nov 21, 2022
Bagas Mirror&Leech Bot is a multipurpose Telegram Bot written in Python for mirroring files on the Internet to our beloved Google Drive. Based on python-aria-mirror-bot

- [ MAYBE UPDATE & ADD MORE MODULE ] Bagas Mirror&Leech Bot Bagas Mirror&Leech Bot is a multipurpose Telegram Bot written in Python for mirroring file

null 4 Nov 23, 2021
A python Discord wrapper made in well, python.

discord.why A python Discord wrapper made in well, python. Made to be used by devs who want something a bit more, general. Basic Examples Sending a me

HellSec 6 Mar 26, 2022
A wrapper for aqquiring Choice Coin directly through a Python Terminal. Leverages the TinyMan Python-SDK.

CHOICE_TinyMan_Wrapper A wrapper that allows users to acquire Choice Coin directly through their Terminal using ALGO and various Algorand Standard Ass

Choice Coin 16 Sep 24, 2022
Python On WhatsApp - Run your python codes on whatsapp along with talking to a chatbot

Python On WhatsApp Run your python codes on whatsapp along with talking to a chatbot This is a small python project to run python on whatsapp. and i c

Prajjwal Pathak 32 Dec 30, 2022
Get-Phone-Number-Details-using-Python - To get the details of any number, we can use an amazing Python module known as phonenumbers.

Get-Phone-Number-Details-using-Python To get the details of any number, we can use an amazing Python module known as phonenumbers. We can use the amaz

Coding Taggers 1 Jan 1, 2022
Python-random-quote - A file-based quote bot written in Python

Let's Write a Python Quote Bot! This repository will get you started with building a quote bot in Python. It's meant to be used along with the Learnin

amir mohammad fateh 1 Jan 2, 2022
Student-Management-System-in-Python - Student Management System in Python

Student-Management-System-in-Python Student Management System in Python

G.Niruthian 3 Jan 1, 2022
WhatsApp Api Python - This documentation aims to exemplify the use of Moorse Whatsapp API in Python

WhatsApp API Python ChatBot Este repositório contém uma aplicação que se utiliza

Moorse.io 3 Jan 8, 2022
OpenSea-Python-Bot - OpenSea Python Bot can be used in 2 modes

OpenSea-Python-Bot OpenSea Python Bot can be used in 2 modes. When --nft paramet

null 49 Feb 10, 2022
Cdk-python-crud-app - CDK Python CRUD App

Welcome to your CDK Python project! You should explore the contents of this proj

Shapon Sheikh 1 Jan 12, 2022