diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml
index 334ea19..85660f8 100644
--- a/.github/workflows/run-tests.yml
+++ b/.github/workflows/run-tests.yml
@@ -5,10 +5,12 @@ on: [push, pull_request]
jobs:
tests:
name: Run tests
- runs-on: ubuntu-latest
+ runs-on: ${{ matrix.os }}-latest
strategy:
+ fail-fast: false
matrix:
- python-version: ["3.8", "3.9", "3.10", "3.11"]
+ os: ["ubuntu", "macos", "windows"]
+ python-version: ["3.8", "3.9", "3.10", "3.11", "3.12.0-rc.3"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
@@ -18,9 +20,11 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- pip install -U setuptools flake8 pytest
- python setup.py install
+ python -m pip install -U setuptools wheel
+ - name: Install package under test
+ run: |
+ python -m pip install -vv .[test]
- name: Test with pytest
run: |
- python -c 'import pyduktape2; ctx = pyduktape2.DuktapeContext(); ctx.eval_js("print(\"Duktape Version: \" + Duktape.version)")'
- pytest
+ python -c "import pyduktape2; ctx = pyduktape2.DuktapeContext(); ctx.eval_js('''print('Duktape Version: ' + Duktape.version)''')"
+ python -m pytest -vv --tb=long
diff --git a/MANIFEST.in b/MANIFEST.in
index 67672b4..e1d9df7 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -9,3 +9,5 @@ include vendor/duk_print_alert.c
include vendor/duk_print_alert.h
include vendor/AUTHORS.txt
include vendor/LICENSE.txt
+
+recursive-include tests *.js
diff --git a/README.rst b/README.rst
index 12c0d34..ed8a20a 100644
--- a/README.rst
+++ b/README.rst
@@ -1,195 +1,195 @@
-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 pyduktape2
-
-To install the latest version from github::
-
- $ pip install git+https://github.com/phith0n/pyduktape2
-
-Running Javascript code
------------------------
-
-To run Javascript code, you need to create an execution context and
-use the method ``eval_js``::
-
- import pyduktape2
-
- context = pyduktape2.DuktapeContext()
- context.eval_js("print(Duktape.version);")
-
-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 pyduktape2
-
- context = pyduktape2.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 pyduktape2
-
- context = pyduktape2.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 pyduktape2
-
- context = pyduktape2.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 pyduktape2
-
- def say_hello(to):
- print 'Hello, {}!'.format(to)
-
- context = pyduktape2.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 pyduktape2
-
- context = pyduktape2.DuktapeContext()
- context.eval_js("var helloWorld = 'Hello, World!';")
- print context.get_global('helloWorld')
-
-``eval_js`` returns the value of the last expression::
-
- import pyduktape2
-
- context = pyduktape2.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 pyduktape2
-
- class Hello(object):
- def __init__(self, what):
- self.what = what
-
- def say(self):
- print('Hello, {}!'.format(self.what))
-
- context = pyduktape2.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 pyduktape2
-
- context = pyduktape2.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 pyduktape2
-
- context = pyduktape2.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]); }')
+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 pyduktape2
+
+To install the latest version from github::
+
+ $ pip install git+https://github.com/phith0n/pyduktape2
+
+Running Javascript code
+-----------------------
+
+To run Javascript code, you need to create an execution context and
+use the method ``eval_js``::
+
+ import pyduktape2
+
+ context = pyduktape2.DuktapeContext()
+ context.eval_js("print(Duktape.version);")
+
+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 pyduktape2
+
+ context = pyduktape2.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 pyduktape2
+
+ context = pyduktape2.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 pyduktape2
+
+ context = pyduktape2.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 pyduktape2
+
+ def say_hello(to):
+ print 'Hello, {}!'.format(to)
+
+ context = pyduktape2.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 pyduktape2
+
+ context = pyduktape2.DuktapeContext()
+ context.eval_js("var helloWorld = 'Hello, World!';")
+ print context.get_global('helloWorld')
+
+``eval_js`` returns the value of the last expression::
+
+ import pyduktape2
+
+ context = pyduktape2.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 pyduktape2
+
+ class Hello(object):
+ def __init__(self, what):
+ self.what = what
+
+ def say(self):
+ print('Hello, {}!'.format(self.what))
+
+ context = pyduktape2.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 pyduktape2
+
+ context = pyduktape2.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 pyduktape2
+
+ context = pyduktape2.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]); }')
diff --git a/pyduktape2.pyx b/pyduktape2.pyx
index 58b2fbf..03a5805 100644
--- a/pyduktape2.pyx
+++ b/pyduktape2.pyx
@@ -1,3 +1,5 @@
+#cython: language_level=3
+
import contextlib
import os
import threading
diff --git a/setup.py b/setup.py
index 86541f4..d985d87 100644
--- a/setup.py
+++ b/setup.py
@@ -33,6 +33,7 @@
],
packages=find_packages(exclude=['tests']),
setup_requires=['setuptools>=18.0', 'Cython<3'],
+ extras_require={'test': ['pytest']},
test_suite='tests',
ext_modules=extensions,
include_package_data=True,
diff --git a/tests/__init__.py b/tests/__init__.py
deleted file mode 100644
index 83f94a0..0000000
--- a/tests/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import unittest
-
-class TestCase(unittest.TestCase):
- pass
diff --git a/tests/js/test0.js b/tests/js/test0.js
index cde68b9..c05cf75 100644
--- a/tests/js/test0.js
+++ b/tests/js/test0.js
@@ -1 +1 @@
-var res = 1 + 1;
+var res = 1 + 1;
diff --git a/tests/js/test1.js b/tests/js/test1.js
index c1aafd4..59aa6ff 100644
--- a/tests/js/test1.js
+++ b/tests/js/test1.js
@@ -1,2 +1,2 @@
-exports.a = 1;
-exports.b = 2;
+exports.a = 1;
+exports.b = 2;
diff --git a/tests/js/test2.js b/tests/js/test2.js
index c7fc679..dec5733 100644
--- a/tests/js/test2.js
+++ b/tests/js/test2.js
@@ -1,2 +1,2 @@
-var test1 = require('js/test1');
-var res = test1.a + test1.b;
+var test1 = require('js/test1');
+var res = test1.a + test1.b;
diff --git a/tests/test_context.py b/tests/test_context.py
index b115707..eda6ee1 100644
--- a/tests/test_context.py
+++ b/tests/test_context.py
@@ -3,7 +3,7 @@
import unittest
from threading import Thread, Lock
-from tests import TestCase
+from unittest import TestCase
from pyduktape2 import DuktapeContext, JSError, JSProxy, DuktapeThreadError