Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
7799ea1
pmjs - handle empty .python commands gracefully
wesgarland Jul 13, 2023
11bbfd6
Add eslint configuration; pick lint
wesgarland Jul 13, 2023
c7fd961
console - substitute unprintable ?? characters for non-BMP code-points
wesgarland Jul 13, 2023
c0299cd
Add js2py/string.simple test
wesgarland Jul 13, 2023
1eb8832
Add py2js/string.simple test
wesgarland Jul 13, 2023
0de27f1
Add test coverage for ArrayBuffer <--> Buffer conversion
Jul 13, 2023
59bf36a
Testing: add tests for passing promises between JS and Python
RyanSawe Jul 13, 2023
e3c689d
add integer py2js test
Jul 14, 2023
213512c
add bigint js2py test
Jul 14, 2023
8508fef
cleanup bigint test to just focus on js->python
Jul 14, 2023
932a3ce
Merge branch 'main' into feature/js-test-coverage
wesgarland Jul 14, 2023
69a92c5
clean up integer test and ensure it only tests py->js
Jul 14, 2023
7e9a4bf
Add smoke tests for in/out data types
wesgarland Jul 14, 2023
9bdfa8d
Merge branch 'main' into feature/js-test-coverage
wesgarland Jul 16, 2023
43141aa
Merge pull request #93 from Severn-J-Lortie/feature/js-test-coverage
wesgarland Jul 16, 2023
5255371
Merge pull request #94 from YarnSaw/tests/promise-passing
wesgarland Jul 16, 2023
4633141
peter-jr - make expected/unexpected after known-fail display in grey
wesgarland Jul 16, 2023
52dbeca
Merge pull request #103 from Distributive-Network/tests/passing-bigin…
wesgarland Jul 16, 2023
d8ac331
test(js): add tests for boolean values between JS & PY
bryan-hoang Jul 14, 2023
32f96b6
Add js2py array test for modifying value
wiwichips Jul 17, 2023
6c028f5
Change array test file ext to .simple
wiwichips Jul 17, 2023
9c91963
Change array test file ext to .simple
wiwichips Jul 17, 2023
865891d
Add py2js array modify test
wiwichips Jul 17, 2023
1fc7cdb
Renamed array-change-index extenstion to .simple
wiwichips Jul 17, 2023
cea3c37
Merge pull request #101 from bryan-hoang/test/js-2-py-boolean-bool
wesgarland Jul 17, 2023
9d145ff
Add JS<>Py function tests
wiwichips Jul 18, 2023
f269052
Merge pull request #119 from wiwichips/feature/js-test-coverage
wesgarland Jul 18, 2023
b325ae8
Merge branch 'main' into feature/js-test-coverage
wesgarland Jul 19, 2023
69813cb
Add tests for Date / datetime objects
Jul 13, 2023
b62bd55
Add tests for 21st century, correct test's assumptions WRT dates in r…
Jul 17, 2023
1316c90
add test tests/js/js2py/datetime2.simple.failing
wesgarland Jul 20, 2023
5ffc454
Merge branch 'main' into feature/js-test-coverage
Xmader Jul 20, 2023
fd9e726
the `day` argument to `Date.UTC` starts from 1
Xmader Jul 20, 2023
7e64123
`tests/js/js2py/datetime2.simple` should pass
Xmader Jul 20, 2023
d23a94b
Revert "console - substitute unprintable ?? characters for non-BMP co…
Xmader Jul 20, 2023
622fc38
Update README.md
zollqir Jul 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* @file .eslintrc.js - ESLint configuration file, following the Core Team's JS Style Guide.
* @author Wes Garland <wes@distributive.network>
* @date Mar. 2022, July 2023
*/

/**
* @see {@link https://eslint.org/docs/latest/use/configure/}
* @type {import('eslint').Linter.Config}
*/
module.exports = {
extends: 'eslint:recommended',
globals: {
'python': true
},
env: {
browser: true,
commonjs: true,
es2021: true,
node: true
},
parserOptions: {
ecmaVersion: 13,
sourceType: 'script',
},
rules: {
'indent': [ 'warn', 2, {
SwitchCase: 1,
ignoredNodes: ['CallExpression', 'ForStatement'],
}
],
'linebreak-style': [ 'error', 'unix' ],
'quotes': [ 'warn', 'single' ],
'func-call-spacing': [ 'off', 'never' ],
'no-prototype-builtins': 'off',
'quotes': ['warn', 'single', 'avoid-escape'],
'no-empty': [ 'warn' ],
'no-multi-spaces': [ 'off' ],
'prettier/prettier': [ 'off' ],
'vars-on-top': [ 'error' ],
'no-var': [ 'off' ],
'spaced-comment': [ 'warn' ],
'brace-style': [ 'off' ],
'no-eval': [ 'error' ],
'object-curly-spacing': [ 'warn', 'always' ],
'eqeqeq': [ 'warn', 'always' ],
'no-dupe-keys': [ 'warn' ],
'no-constant-condition': [ 'warn' ],
'no-extra-boolean-cast': [ 'warn' ],
'no-sparse-arrays': [ 'off' ],
'no-inner-declarations': [ 'off' ],
'no-loss-of-precision': [ 'warn' ],
'require-atomic-updates': [ 'warn' ],
'no-dupe-keys': [ 'warn' ],
'no-dupe-class-members': [ 'warn' ],
'no-fallthrough': [ 'warn', { commentPattern: 'fall[ -]*through' }],
'no-invalid-this': [ 'error' ],
'no-return-assign': [ 'error' ],
'no-return-await': [ 'warn' ],
'no-unused-expressions': [ 'warn', { allowShortCircuit: true, allowTernary: true } ],
'prefer-promise-reject-errors': [ 'error' ],
'no-throw-literal': [ 'error' ],
'semi': [ 'off', { omitLastInOneLineBlock: true }], /* does not work right with exports.X = function allmanStyle */
'semi-style': [ 'warn', 'last' ],
'semi-spacing': [ 'error', {'before': false, 'after': true}],
'no-extra-semi': [ 'warn' ],
'no-tabs': [ 'error' ],
'symbol-description': [ 'error' ],
'operator-linebreak': [ 'warn', 'before' ],
'new-cap': [ 'warn' ],
'consistent-this': [ 'error', 'that' ],
'no-use-before-define': [ 'error', { functions: false, classes: false } ],
'no-shadow': [ 'error' ],
'no-label-var': [ 'error' ],
'radix': [ 'error' ],
'no-self-compare': [ 'error' ],
'require-await': [ 'error' ],
'require-yield': [ 'error' ],
'no-promise-executor-return': [ 'off' ],
'no-template-curly-in-string': [ 'warn' ],
'no-unmodified-loop-condition': [ 'warn' ],
'no-unused-private-class-members': [ 'warn' ],
'no-use-before-define': [ 'error', { functions: false, classes: true, variables: true }],
'no-implicit-coercion': [1, {
disallowTemplateShorthand: false,
boolean: true,
number: true,
string: true,
allow: ['!!'] /* really only want to allow if(x) and if(!x) but not if(!!x) */
}],
'no-trailing-spaces': [ 'off', {
skipBlankLines: true,
ignoreComments: true
}
],
'no-unused-vars': ['warn', {
vars: 'all',
args: 'none',
ignoreRestSiblings: false
}],
}
};
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,10 +221,10 @@ that if you update an object in JavaScript, the corresponding Dict in Python wil
|:---------------------|:----------------|
| string | String
| number | Float
| bigint | Integer
| bigint | pythonmonkey.bigint (Integer)
| boolean | Bool
| function | Function
| object - most | JSObjectProxy which inherits from Dict
| object - most | pythonmonkey.JSObjectProxy (Dict)
| object - Date | datetime
| object - Array | List
| object - Promise | awaitable
Expand All @@ -234,14 +234,23 @@ that if you update an object in JavaScript, the corresponding Dict in Python wil

## Tricks
### Integer Type Coercion
You can force a number in JavaScript to be coerced as an integer by casting it to BigInt.
You can force a number in JavaScript to be coerced as an integer by casting it to BigInt:
```javascript
function myFunction(a, b) {
const result = calculate(a, b);
return BigInt(Math.floor(result));
}
```

The `pythonmonkey.bigint` object works like an int in Python, but it will be coerced as a BigInt in JavaScript:
```python
import pythonmonkey

def fn myFunction()
result = 5
return pythonmonkey.bigint(result)
```

### Symbol injection via cross-language IIFE
You can use a JavaScript IIFE to create a scope in which you can inject Python symbols:
```python
Expand Down
4 changes: 2 additions & 2 deletions peter-jr
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,8 @@ findTests \

if [ "${ext}" = "failing" ]; then
knownFail="yes"
PASS="$(bggreen PASS) (unexpected)"
FAIL="$(dred F̶A̶I̶L̶) (expected)"
PASS="$(bggreen PASS) $(grey \(unexpected\))"
FAIL="$(dred F̶A̶I̶L̶) $(grey \(expected\))"
[ ! "${VERBOSE}" ] && thisStderr="/dev/null"
else
knownFail=""
Expand Down
10 changes: 9 additions & 1 deletion python/pythonmonkey/cli/pmjs.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,17 @@
pythonCmd.serial = 0;
return;
}

if (cmd === '')
{
return;
}

try {
if (arguments[0] === 'from' || arguments[0] === 'import')
return python.exec(cmd);
{
return python.exec(cmd);
}

const retval = python.eval(cmd);
}
Expand Down
5 changes: 4 additions & 1 deletion tests/js/console-smoke.simple
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
* @author Wes Garland, wes@distributive.network
* @date June 2023
*/
const console = new (require('console').Console)({stdout: python.stdout, stderr: python.stdout});
const console = new (require('console').Console)({
stdout: python.stdout,
stderr: python.stdout
});
globalThis.console.log('one');
globalThis.console.info('two');
console.debug('three');
Expand Down
29 changes: 29 additions & 0 deletions tests/js/js2py/array-change-index.simple
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* @file js2py/array-chnage-index.simple
* Simple test which demonstrates modifying an array
* passed to Python and returning it.
* @author Will Pringle, will@distributive.network
* @date July 2023
*/
'use strict';

const numbers = [1,2,3,4,5,6,7,8,9];

python.exec(`
def setArrayAtIndex(array, index, new):
array[1] = new # can't index "array" based on "index"... probably because index is a float. So just pass "1"
return array
`);
const setArrayAtIndex = python.eval("setArrayAtIndex")
const numbersBack = setArrayAtIndex(numbers, 1, 999);

// check that the array data was modified by reference in python
if (numbers[1] !== 999)
throw new Error('array not modified by python');

// check that the array we get from python is the same reference as defined in js
if (!Object.is(numbers, numbersBack))
throw new Error('array reference differs between JavaScript and Python');

console.log('pass');

30 changes: 30 additions & 0 deletions tests/js/js2py/arraybuffer.simple
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* @file js2py/arraybuffer.simple
* Simple test which shows that sending ArrayBuffers to Python and getting them back into JS
* works as expected. Also tests mutation.
* @author Severn Lortie, severn@distributive.network
* @date July 2023
*/
'use strict';

const buffer = new ArrayBuffer(5); // 5 bytes
const bufferView = new Uint8Array(buffer);
bufferView[0] = 104;
bufferView[1] = 101;
bufferView[2] = 108;
bufferView[3] = 108;
bufferView[4] = 111;

python.exec(`
def mutate(x):
x[3] = 55;
return x;
`);

const fn = python.eval('mutate');
const modifiedBuff = fn(buffer); // Call the function which mutates the buffer

// Check that both were updated
for (let i = 0; i < bufferView.length; i++)
if (modifiedBuff[i] !== bufferView[i]) throw new Error('The buffer was not changed, or not returned correctly.');

35 changes: 35 additions & 0 deletions tests/js/js2py/bigint.simple
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* @file js2py/bigint.simple
* Simple test which shows that sending bigint to Python and getting them back into JS
* works as expected.
* @author Kirill Kirnichansky, kirill@distributive.network
* @date July 2023
*/
'use strict';

const bigint = BigInt(777);

python.exec(`
def isPythonInt(val):
return isinstance(val, int)

def compare(val):
if val == int(777):
return True
return False
`);
const isPythonInt = python.eval('isPythonInt');
const compare = python.eval('compare');

if (!isPythonInt)
{
console.error(`${bigint} is not instance of int in Python.`);
throw new Error('test failed');
}
if (!compare)
{
console.error(`${bigint} !== 777 in Python`);
throw new Error('test failed');
}

console.log('pass');
27 changes: 27 additions & 0 deletions tests/js/js2py/boolean.simple
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* @file js2py/boolean.simple - Asserts that sending primitive and boxed
* booleans to Python and getting them back into JS as primitive
* booleans retains the correponsing logical values.
*
* @author Bryan Hoang <bryan@distributive.network>
* @date July 2023
*/
'use strict';

const throughPython = python.eval('(lambda x: x)');

const primitiveJsTrue = true;
const maybePrimitiveJsTrue = throughPython(primitiveJsTrue);
if (maybePrimitiveJsTrue !== primitiveJsTrue)
{
console.error('Expected', primitiveJsTrue, 'but got', maybePrimitiveJsTrue);
throw new Error('Test failed');
}

const boxedJsFalse = new Boolean(false);
const maybePrimitiveJsFalse = throughPython(boxedJsFalse);
if (false !== maybePrimitiveJsFalse)
{
console.error('Expected', false, 'but got', maybePrimitiveJsFalse);
throw new Error('Test failed');
}
40 changes: 40 additions & 0 deletions tests/js/js2py/datetime.simple.failing
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* @file js2py/datetime.simple.failing
* Simple test which shows that sending Dates to Python and getting them back into JS
* works as expected
*
* @author Elijah Deluzio, elijah@distributive.network
* @date July 2023
*/
'use strict'

const throughPython = python.eval('(lambda x: x)');
var expectedJsTimestamp;
var jsDate;
var pyDate;

// Test 1: Date from timestamp of 0 (1970 - 01 - 01), timestamp = 0
jsDate = new Date(Date.UTC(1970, 0, 1, 0, 0, 0));
expectedJsTimestamp = jsDate.getTime();
pyDate = throughPython(jsDate);

if (expectedJsTimestamp !== pyDate.getTime())
{
console.error('expected', expectedJsTimestamp, 'but got', pyDate.getTime());
throw new Error('test failed');
}

console.log('pass -', pyDate);

// Test 2: Date from 21st century (2222 - 02 - 03), timestamp = 7955193600000
jsDate = new Date(Date.UTC(2222, 1, 3, 0, 0, 0));
expectedJsTimestamp = jsDate.getTime();
pyDate = throughPython(jsDate);

if (expectedJsTimestamp !== pyDate.getTime())
{
console.error('expected', expectedJsTimestamp, 'but got', pyDate.getTime());
throw new Error('test failed');
}

console.log('pass -', pyDate);
Loading