New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@pipcook/boa

Package Overview
Dependencies
Maintainers
6
Versions
104
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@pipcook/boa - npm Package Compare versions

Comparing version 1.2.0 to 1.2.1-1b50e4e-beta.0

lib/factory.js

428

lib/index.js

@@ -1,33 +0,24 @@

const fs = require('fs');
'use strict';
const vm = require('vm');
const util = require('util');
const path = require('path');
const native = require('bindings')('boa');
const debug = require('debug')('boa');
const utils = require('./utils');
const DelegatorLoader = require('./delegator-loader');
// internal symbols
const IterIdxForSeqSymbol = Symbol('The iteration index for sequence');
const {
notEmpty,
getIndent,
removeIndent,
asHandleObject,
GetOwnershipSymbol,
PyGetAttrSymbol,
PySetAttrSymbol,
PyGetItemSymbol,
PySetItemSymbol,
} = require('./utils');
const { SharedPythonObject } = require('./worker');
const { condaPath, pyInst, globals, builtins } = require('./factory');
const { wrap, _internalWrap } = require('./proxy');
// read the conda path from the .CONDA_INSTALL_DIR
// eslint-disable-next-line no-sync
const condaPath = fs.readFileSync(path.join(__dirname, '../.CONDA_INSTALL_DIR'), 'utf8');
if (!process.env.PYTHONHOME) {
process.env.PYTHONHOME = condaPath;
}
// create the global-scoped instance
let pyInst = global.__pipcook_boa_pyinst__;
if (pyInst == null) {
pyInst = new native.Python(process.argv.slice(1));
global.__pipcook_boa_pyinst__ = pyInst;
}
const importedNames = [];
// FIXME(Yorkie): move to costa or daemon?
const sharedModules = ['sys', 'torch'];
const globals = pyInst.globals();
const builtins = pyInst.builtins();
const delegators = DelegatorLoader.load();
let defaultSysPath = [];

@@ -38,14 +29,2 @@

function getTypeInfo(T) {
const typeo = builtins.__getitem__('type').invoke(asHandleObject(T));
const tinfo = { module: null, name: null };
if (typeo.__hasattr__('__module__')) {
tinfo.module = typeo.__getattr__('__module__').toString();
}
if (typeo.__hasattr__('__name__')) {
tinfo.name = typeo.__getattr__('__name__').toString();
}
return tinfo;
}
function setenv(externalSearchPath) {

@@ -81,74 +60,2 @@ const sys = pyInst.import('sys');

function dump(T) {
return pyInst.import('json')
.__getattr__('dumps')
.invoke(asHandleObject(T), {
// use str method to serialize those fields which cannot be serialized by default
default: _internalWrap(builtins).str,
[native.NODE_PYTHON_KWARGS_NAME]: true,
});
}
function getDelegator(type) {
if (typeof type === 'string') {
return delegators[type];
}
const { module, name } = type;
if (Object.prototype.hasOwnProperty.call(delegators, module)) {
return delegators[module][name];
}
return undefined;
}
// The function `wrap(T)` is used to return an object or value for using.
// It depends on the type of `T` in Python world, usually it returns a
// `Proxy` object that's based on `T`, when the type could be represented
// as number/boolean/string/null, the return value would be converted to
// corresponding JS primative.
function wrap(T) {
// if `T` is null or undefined, returning itself by default.
if (T === null || T == undefined) {
return T;
}
const type = getTypeInfo(T);
debug(`start wrapping an object, and its type is "${type.module}.${type.name}"`);
// if `type` is "NoneType", returning the null.
if (type.module === 'builtins' && type.name === 'NoneType') {
return null;
}
// FIXME(Yorkie): directly returning the primitive value on the
// following conditions.
if ([
/** python types convert to primitive values. */
'int', /** Number */
'int64', /** BigInt */
'float', /** Number */
'float64', /** BigDecimal(depends on new tc39 proposal) */
'bool', /** Boolean */
'str', /** String */
/** except for null and undefined */
].includes(type.name)) {
return T.toPrimitive();
}
let fn = getDelegator(T.isCallable() ? 'callee' : type);
if (typeof fn !== 'function') {
fn = getDelegator('default');
}
// use internalWrap to generate proxy object with corresponding delegator.
const wrapped = _internalWrap(T, fn(T, wrap));
T[native.NODE_PYTHON_WRAPPED_NAME] = wrapped;
return wrapped;
}
function asHandleObject(T) {
return {
// namely shortcut for Python object.
[native.NODE_PYTHON_HANDLE_NAME]: T
};
}
function asBytesObject(str) {

@@ -161,265 +68,2 @@ return {

function _internalWrap(T, src={}) {
Object.defineProperties(src, {
/**
* @property native.NODE_PYTHON_WRAPPED_NAME
* @private
*/
[native.NODE_PYTHON_HANDLE_NAME]: {
enumerable: true,
writable: false,
value: T,
},
/**
* @method native.NODE_PYTHON_JS_DISPATCH
* @private
*/
[native.NODE_PYTHON_JS_DISPATCH]: {
enumerable: true,
// FIXME(Yorkie): temporarily set `configurable` to false here.
// See https://github.com/v8/v8/blob/7.9.317/src/objects/objects.cc#L1176-L1179
//
// The proxy object's get trap handler is inconsistent with this descriptor when
// the value is a function, which means the `inconsistent` to be true, then throwing
// a `kProxyGetNonConfigurableData` error.
//
// In order to better solve, we need to define both `get` and `has` traps in the
// proxy object, and move descriptors to the trap handler.
configurable: true,
writable: false,
value: function(fn, isClassMethod, ...args) {
if (isClassMethod) {
return fn.apply(wrap(args[0]), args.slice(1).map(wrap));
} else {
return fn.apply(this, args.map(wrap));
}
},
},
/**
* @method invoke
* @param {object} args
* @private
*/
invoke: {
enumerable: false,
writable: false,
value: args => {
return T.invoke.apply(T, args);
},
},
/**
* @method toString
* @public
*/
toString: {
configurable: true,
enumerable: false,
writable: false,
value: () => T.toString(),
},
/**
* @method toJSON
* @public
*/
toJSON: {
configurable: true,
enumerable: false,
writable: false,
value: () => {
const type = getTypeInfo(T);
let str;
if (type.module === 'numpy') {
str = dump(T.__getattr__('tolist').invoke());
} else {
str = dump(T);
}
// TODO(Yorkie): more performant way to serialize objects?
return JSON.parse(wrap(str));
},
},
/**
* Shortcut for slicing object.
* @method slice
* @public
*/
slice: {
configurable: true,
enumerable: false,
writable: false,
value: (start, end, step) => {
// slice(start, end, step)
const slice = builtins.__getitem__('slice')
.invoke(start, end, step);
// use the slice object as the key for s[x:y:z]
return wrap(T.__getitem__(asHandleObject(slice)));
},
},
/**
* This is used to cusom the console.log output by calling toString().
* @method util.inspect.custom
* @public
*/
[util.inspect.custom]: {
configurable: true,
enumerable: false,
writable: false,
value: () => T.toString(),
},
/**
* @method Symbol.toPrimitive
* @param {string} hint
* @public
*/
// Forward compatible with newer `toPrimitive` spec
// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toPrimitive
[Symbol.toPrimitive]: {
configurable: true,
enumerable: false,
writable: false,
value: () => T.toString(),
},
/**
* Implementation of ES iterator protocol, See:
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
*
* @method Symbol.iterator
* @public
*/
[Symbol.iterator]: {
configurable: true,
enumerable: false,
writable: false,
value: () => {
if (T.isIterator()) {
return {
next: () => {
const curr = T.next();
return {
done: curr.done,
value: wrap(curr.value),
};
},
};
}
if (T.isSequence()) {
return {
next: function next() {
if (typeof this[IterIdxForSeqSymbol] === 'number') {
this[IterIdxForSeqSymbol] += 1;
} else {
this[IterIdxForSeqSymbol] = 0;
}
const lengthOfSeq = builtins.__getitem__('len')
.invoke(asHandleObject(T)).toPrimitive();
const index = this[IterIdxForSeqSymbol];
if (index >= lengthOfSeq) {
return { done: true, value: undefined };
}
return {
done: false,
value: wrap(T.__getitem__(index)),
};
}
};
}
throw new TypeError('object is not iteratable or sequence.');
},
},
/**
* @method __hash__
* @public
*/
__hash__: {
configurable: true,
enumerable: true,
writable: false,
value: () => T.__hash__(),
},
});
// Create the proxy object for handlers
let newTarget;
return (newTarget = new Proxy(src, {
'get'(target, name) {
debug(`get property on "${target.constructor.name}", ` +
`name is "${name.toString()}"`);
const { hasOwnProperty } = Object.prototype;
const constructProto = target.constructor.prototype;
if (hasOwnProperty.call(target, name) /* check for own property */ ||
hasOwnProperty.call(constructProto, name) /* check for inherited one-level */
) {
const value = target[name];
debug(`found "${name.toString()}" from object own properties ` +
`or one-level properties.`);
if (typeof value === 'function') {
// FIXME(Yorkie): make sure the function's this is correct.
return value.bind(newTarget);
} else {
return value;
}
}
/** Enter the Python world. */
if (typeof name === 'string') {
if (/^[0-9]+$/.test(name)) {
debug('name is detected to be an index.');
const n = parseInt(name, 10);
return wrap(T.__getitem__(n));
}
if (T.__hasattr__(name)) {
debug(`found "${name}" as python attr`);
return wrap(T.__getattr__(name));
}
}
try {
const r = T.__getitem__(name);
if (r != null) {
debug(`found "${name.toString()}" as python item`);
return wrap(r);
}
} catch (e) {
debug(`accessing the item["${name.toString()}"] failed ` +
`with ${e.message}`);
}
},
'set'(target, name, value) {
if (typeof name === 'string') {
if (/^[0-9]+$/.test(name)) {
return T.__setitem__(parseInt(name, 10), value) !== -1;
}
if (T.__hasattr__(name)) {
return T.__setattr__(name, value) !== -1;
}
}
let r = T.__setitem__(name, value);
if (r === -1) {
r = T.__setattr__(name, value);
}
return r !== -1;
},
'apply'(target, thisArg, argumentsList) {
return wrap(target.invoke(argumentsList));
},
'construct'(target, argumentsList, newClass) {
if (newClass.name === 'PythonCallable') {
return wrap(target.invoke(argumentsList));
}
if (!newClass.prototype.$pyclass) {
const pyclass = T.createClass(newClass.name, target);
Object.getOwnPropertyNames(newClass.prototype)
.filter(name => name !== 'constructor' || name !== '__init__')
.forEach(name => {
pyclass.setClassMethod(name, newClass.prototype[name]);
});
newClass.prototype.$pyclass = wrap(pyclass);
}
// return the instance
return newClass.prototype.$pyclass.apply(null, argumentsList);
},
}));
}
module.exports = {

@@ -432,2 +76,6 @@ /**

setenv,
/**
* @class SharedPythonObject
*/
SharedPythonObject,
/*

@@ -509,2 +157,6 @@ * Import a Python module.

},
/**
* Evaluate a Python expression.
* @param {string} strs the Python exprs.
*/
'eval': (strs, ...params) => {

@@ -534,9 +186,35 @@ let src = '';

// for multiline executing.
const lines = src.split('\n').filter(utils.notEmpty);
const indent = utils.getIndent(lines);
const lines = src.split('\n').filter(notEmpty);
const indent = getIndent(lines);
return wrap(pyInst.eval(
lines.map(utils.removeIndent(indent)).join('\n'),
lines.map(removeIndent(indent)).join('\n'),
{ globals: env, locals: env }
));
},
/**
* Symbols
*/
symbols: {
/**
* The symbol is used to get the ownership value on an object.
*/
GetOwnershipSymbol,
/**
* __getattr__
*/
PyGetAttrSymbol,
/**
* __setattr__
*/
PySetAttrSymbol,
/**
* __getitem__
*/
PyGetItemSymbol,
/**
* __setitem__
*/
PySetItemSymbol,
},
};

@@ -0,3 +1,11 @@

'use strict';
const { NODE_PYTHON_HANDLE_NAME } = require('bindings')('boa');
const GetOwnershipSymbol = Symbol('GET_OWNERSHIP');
const PyGetAttrSymbol = Symbol('PYTHON_GETATTR_SYMBOL');
const PySetAttrSymbol = Symbol('PYTHON_SETATTR_SYMBOL');
const PyGetItemSymbol = Symbol('PYTHON_GETITEM_SYMBOL');
const PySetItemSymbol = Symbol('PYTHON_SETITEM_SYMBOL');
function notEmpty(line) {

@@ -23,4 +31,20 @@ return /^\s*$/.test(line) === false;

exports.notEmpty = notEmpty;
exports.getIndent = getIndent;
exports.removeIndent = removeIndent;
function asHandleObject(T) {
return {
// namely shortcut for Python object.
[NODE_PYTHON_HANDLE_NAME]: T
};
}
module.exports = {
notEmpty,
getIndent,
removeIndent,
asHandleObject,
// symbols
GetOwnershipSymbol,
PyGetAttrSymbol,
PySetAttrSymbol,
PyGetItemSymbol,
PySetItemSymbol,
};
{
"name": "@pipcook/boa",
"version": "1.2.0",
"version": "1.2.1-1b50e4e-beta.0",
"description": "Use Python modules seamlessly in Node.js",

@@ -50,3 +50,3 @@ "main": "lib/index.js",

},
"gitHead": "a586283035a5d3493a19ef0018a35506a402eb0e"
"gitHead": "61e33debd9e022508b644d167512b393dd18def8"
}
const test = require('tape');
const boa = require('../../');
const builtins = boa.builtins();
const { PyGetAttrSymbol, PySetAttrSymbol, PyGetItemSymbol, PySetItemSymbol } = boa.symbols;

@@ -20,2 +21,3 @@ test('keyword arguments throws', t => {

'{"foobar":[1,3,5]}');
mlist[0] = 2;

@@ -30,2 +32,23 @@ mlist[1] = 4;

test('getattr and setattr with symbols', t => {
// test for getattr and setattr
const pybasic = boa.import('tests.base.basic');
const f = new pybasic.Foobar();
t.strictEqual(f[PyGetAttrSymbol]('test'), 'pythonworld', 'getattr is ok');
f[PySetAttrSymbol]('test', 'updated');
t.strictEqual(f[PyGetAttrSymbol]('test'), 'updated', 'setattr is ok');
t.end();
});
test('getitem and setitem with symbols', t => {
const mlist = builtins.list([1, 3]);
// test for getitemm and setitem
t.strictEqual(mlist[PyGetItemSymbol](0), 1, 'mlist[0] = 1');
t.strictEqual(mlist[PyGetItemSymbol](1), 3, 'mlist[1] = 3');
mlist[PySetItemSymbol](0, 100);
t.strictEqual(mlist[PyGetItemSymbol](0), 100, 'setitem is ok');
t.strictEqual(mlist[PyGetAttrSymbol]('__len__')(), 2, 'use getattr to check mlist length');
t.end();
});
test('define a class extending python class', t => {

@@ -32,0 +55,0 @@ class EmptyDict extends builtins.dict {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc