concurrent-c-node 0.12.0__tar.gz → 0.14.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {concurrent_c_node-0.12.0 → concurrent_c_node-0.14.0}/PKG-INFO +36 -5
- {concurrent_c_node-0.12.0 → concurrent_c_node-0.14.0}/README.md +35 -4
- {concurrent_c_node-0.12.0 → concurrent_c_node-0.14.0}/cc_node/__init__.py +12 -6
- {concurrent_c_node-0.12.0 → concurrent_c_node-0.14.0}/cc_node/broker.cjs +45 -9
- {concurrent_c_node-0.12.0 → concurrent_c_node-0.14.0}/concurrent_c_node.egg-info/PKG-INFO +36 -5
- {concurrent_c_node-0.12.0 → concurrent_c_node-0.14.0}/pyproject.toml +1 -1
- {concurrent_c_node-0.12.0 → concurrent_c_node-0.14.0}/cc_node/examples/__init__.py +0 -0
- {concurrent_c_node-0.12.0 → concurrent_c_node-0.14.0}/cc_node/examples/bench_wire.py +0 -0
- {concurrent_c_node-0.12.0 → concurrent_c_node-0.14.0}/cc_node/examples/use_node.py +0 -0
- {concurrent_c_node-0.12.0 → concurrent_c_node-0.14.0}/concurrent_c_node.egg-info/SOURCES.txt +0 -0
- {concurrent_c_node-0.12.0 → concurrent_c_node-0.14.0}/concurrent_c_node.egg-info/dependency_links.txt +0 -0
- {concurrent_c_node-0.12.0 → concurrent_c_node-0.14.0}/concurrent_c_node.egg-info/top_level.txt +0 -0
- {concurrent_c_node-0.12.0 → concurrent_c_node-0.14.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: concurrent-c-node
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.14.0
|
|
4
4
|
Summary: JavaScript and npm packages from Python over the Concurrent-C bridge: one spawned Node child per domain, host-controlled lifetime.
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Repository, https://github.com/sreekotay/concurrent-c
|
|
@@ -99,10 +99,12 @@ Examples ship in the wheel. Same domain model and materialization
|
|
|
99
99
|
rules as the npm sibling, pointed the other way:
|
|
100
100
|
|
|
101
101
|
- **Values**: plain data (finite numbers, strings, booleans, `None`,
|
|
102
|
-
lists/
|
|
103
|
-
handle
|
|
104
|
-
|
|
105
|
-
|
|
102
|
+
lists and non-empty dicts/objects of the same) crosses by value; an
|
|
103
|
+
empty `{}` stays a live handle (so bags you mint in JS keep property
|
|
104
|
+
access). Everything else is a live handle owned by the domain —
|
|
105
|
+
attribute access is property lookup (methods arrive bound), calls are
|
|
106
|
+
calls, `str()` is `String()`. Non-finite floats cross tagged, never
|
|
107
|
+
silently nulled.
|
|
106
108
|
- **The domain rules hold**: handles never cross bridges; `stats()` is
|
|
107
109
|
the handle ledger and `release()` drops one early; `destroy()` is
|
|
108
110
|
idempotent, every door answers `bridge is closed` after, and the
|
|
@@ -161,6 +163,35 @@ memcpy per side, the receiver consumes the spill file, and the sender
|
|
|
161
163
|
sweeps it if the child died first. Nothing strays, and nothing is
|
|
162
164
|
silently truncated: an unsupported type is an articulate error.
|
|
163
165
|
|
|
166
|
+
## Common issues
|
|
167
|
+
|
|
168
|
+
**`Cannot find module '…'`.** `require` / `import` resolve from the
|
|
169
|
+
**Python process cwd** (`node_modules` next to your program), not from
|
|
170
|
+
the site-packages install of this wheel. `npm install lodash` in the
|
|
171
|
+
project directory is the fix; or pass `create(node='/path/to/node')` /
|
|
172
|
+
`CC_NODE_BIN` when the wrong Node is on `PATH`. Missing-module errors
|
|
173
|
+
name that cwd rule.
|
|
174
|
+
|
|
175
|
+
**Empty `{}` is a live handle.** `js.eval('({})')` stays a `JsHandle`
|
|
176
|
+
so later property use matches Node. Non-empty plain objects still cross
|
|
177
|
+
as Python `dict`s (data returns). Same-domain handles chain
|
|
178
|
+
(`h.update(…).digest(…)`); foreign-domain handles do not.
|
|
179
|
+
|
|
180
|
+
**Thenables are awaited in the child.** Promise-based npm APIs need no
|
|
181
|
+
`async`/`await` on the Python side — the call blocks until settle (or
|
|
182
|
+
raises `JsError` on reject). That is the opposite of
|
|
183
|
+
`concurrent-c-python`'s isolated surface, where every call is already a
|
|
184
|
+
JS Promise you must await.
|
|
185
|
+
|
|
186
|
+
**Wire cost vs tiny work.** Round trip is ~100µs class; a one-line JS
|
|
187
|
+
helper on three numbers loses to pure Python. Prefer Python (or a native
|
|
188
|
+
CC module) for small/hot work; use the bridge when Node/npm owns the
|
|
189
|
+
kernel (crypto, parsers, large buffers via shm).
|
|
190
|
+
|
|
191
|
+
**Crash isolation, not a sandbox.** The child inherits your environment
|
|
192
|
+
and privileges — do not evaluate untrusted JavaScript. `destroy()` is
|
|
193
|
+
cooperative; CPU-bound JS is not preemptible (wait or kill + new domain).
|
|
194
|
+
|
|
164
195
|
## Choosing the node
|
|
165
196
|
|
|
166
197
|
Same ambient-first rule as the rest of the family: the domain runs
|
|
@@ -88,10 +88,12 @@ Examples ship in the wheel. Same domain model and materialization
|
|
|
88
88
|
rules as the npm sibling, pointed the other way:
|
|
89
89
|
|
|
90
90
|
- **Values**: plain data (finite numbers, strings, booleans, `None`,
|
|
91
|
-
lists/
|
|
92
|
-
handle
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
lists and non-empty dicts/objects of the same) crosses by value; an
|
|
92
|
+
empty `{}` stays a live handle (so bags you mint in JS keep property
|
|
93
|
+
access). Everything else is a live handle owned by the domain —
|
|
94
|
+
attribute access is property lookup (methods arrive bound), calls are
|
|
95
|
+
calls, `str()` is `String()`. Non-finite floats cross tagged, never
|
|
96
|
+
silently nulled.
|
|
95
97
|
- **The domain rules hold**: handles never cross bridges; `stats()` is
|
|
96
98
|
the handle ledger and `release()` drops one early; `destroy()` is
|
|
97
99
|
idempotent, every door answers `bridge is closed` after, and the
|
|
@@ -150,6 +152,35 @@ memcpy per side, the receiver consumes the spill file, and the sender
|
|
|
150
152
|
sweeps it if the child died first. Nothing strays, and nothing is
|
|
151
153
|
silently truncated: an unsupported type is an articulate error.
|
|
152
154
|
|
|
155
|
+
## Common issues
|
|
156
|
+
|
|
157
|
+
**`Cannot find module '…'`.** `require` / `import` resolve from the
|
|
158
|
+
**Python process cwd** (`node_modules` next to your program), not from
|
|
159
|
+
the site-packages install of this wheel. `npm install lodash` in the
|
|
160
|
+
project directory is the fix; or pass `create(node='/path/to/node')` /
|
|
161
|
+
`CC_NODE_BIN` when the wrong Node is on `PATH`. Missing-module errors
|
|
162
|
+
name that cwd rule.
|
|
163
|
+
|
|
164
|
+
**Empty `{}` is a live handle.** `js.eval('({})')` stays a `JsHandle`
|
|
165
|
+
so later property use matches Node. Non-empty plain objects still cross
|
|
166
|
+
as Python `dict`s (data returns). Same-domain handles chain
|
|
167
|
+
(`h.update(…).digest(…)`); foreign-domain handles do not.
|
|
168
|
+
|
|
169
|
+
**Thenables are awaited in the child.** Promise-based npm APIs need no
|
|
170
|
+
`async`/`await` on the Python side — the call blocks until settle (or
|
|
171
|
+
raises `JsError` on reject). That is the opposite of
|
|
172
|
+
`concurrent-c-python`'s isolated surface, where every call is already a
|
|
173
|
+
JS Promise you must await.
|
|
174
|
+
|
|
175
|
+
**Wire cost vs tiny work.** Round trip is ~100µs class; a one-line JS
|
|
176
|
+
helper on three numbers loses to pure Python. Prefer Python (or a native
|
|
177
|
+
CC module) for small/hot work; use the bridge when Node/npm owns the
|
|
178
|
+
kernel (crypto, parsers, large buffers via shm).
|
|
179
|
+
|
|
180
|
+
**Crash isolation, not a sandbox.** The child inherits your environment
|
|
181
|
+
and privileges — do not evaluate untrusted JavaScript. `destroy()` is
|
|
182
|
+
cooperative; CPU-bound JS is not preemptible (wait or kill + new domain).
|
|
183
|
+
|
|
153
184
|
## Choosing the node
|
|
154
185
|
|
|
155
186
|
Same ambient-first rule as the rest of the family: the domain runs
|
|
@@ -9,10 +9,11 @@
|
|
|
9
9
|
The mirror of the cc-python bridge, same rules pointed the other way:
|
|
10
10
|
attribute access is property lookup (methods arrive bound), a call is a
|
|
11
11
|
call, plain data (finite numbers, strings, booleans, None, lists and
|
|
12
|
-
dicts of the same) crosses by value
|
|
13
|
-
handle
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
non-empty dicts of the same) crosses by value; an empty dict/object
|
|
13
|
+
stays a live handle (bags keep property access). Everything else stays
|
|
14
|
+
a live handle owned by the domain. A thenable result is awaited in the
|
|
15
|
+
child before the reply, so async package APIs work with nothing extra.
|
|
16
|
+
A Python callable passed as an argument becomes a JS function; its
|
|
16
17
|
exceptions cross back as JS errors and vice versa, messages intact.
|
|
17
18
|
Handles never cross domains; every door after destroy() answers
|
|
18
19
|
articulately; destroy is idempotent and `with cc_node.create() as js:`
|
|
@@ -29,7 +30,7 @@ import subprocess
|
|
|
29
30
|
import tempfile
|
|
30
31
|
|
|
31
32
|
__all__ = ["create", "JsError", "JsHandle", "__version__"]
|
|
32
|
-
__version__ = "0.
|
|
33
|
+
__version__ = "0.12.1"
|
|
33
34
|
|
|
34
35
|
# Typed buffers cross as typed arrays; big ones spill through shared
|
|
35
36
|
# memory (tmpfs where available) — one memcpy per side, receiver
|
|
@@ -304,7 +305,12 @@ class Bridge:
|
|
|
304
305
|
enc = self._encode_buffer(a)
|
|
305
306
|
if enc is not None:
|
|
306
307
|
return enc
|
|
307
|
-
raise JsError(
|
|
308
|
+
raise JsError(
|
|
309
|
+
"cc-node: unsupported argument type: %r "
|
|
310
|
+
"(numbers, str, bool, None, list/tuple, dict with str keys, "
|
|
311
|
+
"bytes/array.array/1-D numpy, callables, or this domain's "
|
|
312
|
+
"JsHandle — same-domain handles chain)"
|
|
313
|
+
% (type(a),))
|
|
308
314
|
|
|
309
315
|
def _encode_buffer(self, a):
|
|
310
316
|
# Typed buffers cross as typed arrays: bytes/array.array/1-D
|
|
@@ -9,13 +9,15 @@
|
|
|
9
9
|
* numbering) says which via CC_WIRE_IN / CC_WIRE_OUT. Handles are
|
|
10
10
|
* integers into one table; results follow the bridge materialization
|
|
11
11
|
* rule — plain data (finite numbers, strings, booleans, null, arrays
|
|
12
|
-
* and plain objects of the same) crosses as a value
|
|
13
|
-
* stays a handle
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
12
|
+
* and non-empty plain objects of the same) crosses as a value; an
|
|
13
|
+
* empty plain object stays a handle (so `eval('({})')` remains a live
|
|
14
|
+
* JS object — materializing it to Python `{}` dropped property access).
|
|
15
|
+
* A thenable result is awaited before the reply, so async package APIs
|
|
16
|
+
* need nothing special from the Python side. A Python callable crosses
|
|
17
|
+
* as {$f: id}; invoking it sends a nested `cb` request and BLOCKS on a
|
|
18
|
+
* synchronous read for the answer — legal because the protocol is
|
|
19
|
+
* strictly alternating, so nothing else can be in flight. EOF on the
|
|
20
|
+
* request fd is the host vanishing: exit.
|
|
19
21
|
*
|
|
20
22
|
* cc/include/ccc/script/js.cch embeds this file verbatim (the CC
|
|
21
23
|
* isolated tier speaks the same wire); js_iso_smoke pins the two
|
|
@@ -151,6 +153,14 @@ function encodeResult(v) {
|
|
|
151
153
|
return encodeBuffer(kind,
|
|
152
154
|
Buffer.from(v.buffer, v.byteOffset, v.byteLength));
|
|
153
155
|
if (Buffer.isBuffer(v)) return encodeBuffer('u8', v);
|
|
156
|
+
// Empty plain {} / Object.create(null) stay handles — callers mint
|
|
157
|
+
// bags for later property use. Non-empty plain objects still cross
|
|
158
|
+
// by value (data returns).
|
|
159
|
+
const proto = Object.getPrototypeOf(v);
|
|
160
|
+
if (!Array.isArray(v) &&
|
|
161
|
+
(proto === Object.prototype || proto === null) &&
|
|
162
|
+
Object.keys(v).length === 0)
|
|
163
|
+
return { h: put(v) };
|
|
154
164
|
}
|
|
155
165
|
if (v === null || isPlain(v, 0)) return { v };
|
|
156
166
|
return { h: put(v) };
|
|
@@ -215,8 +225,34 @@ async function main() {
|
|
|
215
225
|
try {
|
|
216
226
|
let r;
|
|
217
227
|
switch (req.op) {
|
|
218
|
-
case 'require':
|
|
219
|
-
|
|
228
|
+
case 'require':
|
|
229
|
+
try {
|
|
230
|
+
r = requireCwd(req.name);
|
|
231
|
+
} catch (e) {
|
|
232
|
+
if (e && e.code === 'MODULE_NOT_FOUND') {
|
|
233
|
+
throw new Error(
|
|
234
|
+
String(e.message) +
|
|
235
|
+
' — npm install into this working directory\'s ' +
|
|
236
|
+
'node_modules (require resolves from cwd), or pass ' +
|
|
237
|
+
'create(node=...) for a different Node');
|
|
238
|
+
}
|
|
239
|
+
throw e;
|
|
240
|
+
}
|
|
241
|
+
break;
|
|
242
|
+
case 'import':
|
|
243
|
+
try {
|
|
244
|
+
r = await import(req.name);
|
|
245
|
+
} catch (e) {
|
|
246
|
+
const msg = String(e && e.message !== undefined ? e.message : e);
|
|
247
|
+
if (/Cannot find module|ERR_MODULE_NOT_FOUND/i.test(msg)) {
|
|
248
|
+
throw new Error(
|
|
249
|
+
msg +
|
|
250
|
+
' — install the package for this Node (cwd node_modules ' +
|
|
251
|
+
'or a path import), or pass create(node=...)');
|
|
252
|
+
}
|
|
253
|
+
throw e;
|
|
254
|
+
}
|
|
255
|
+
break;
|
|
220
256
|
case 'eval': r = (0, eval)(req.src); break;
|
|
221
257
|
case 'get': {
|
|
222
258
|
const o = getH(req.h);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: concurrent-c-node
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.14.0
|
|
4
4
|
Summary: JavaScript and npm packages from Python over the Concurrent-C bridge: one spawned Node child per domain, host-controlled lifetime.
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Repository, https://github.com/sreekotay/concurrent-c
|
|
@@ -99,10 +99,12 @@ Examples ship in the wheel. Same domain model and materialization
|
|
|
99
99
|
rules as the npm sibling, pointed the other way:
|
|
100
100
|
|
|
101
101
|
- **Values**: plain data (finite numbers, strings, booleans, `None`,
|
|
102
|
-
lists/
|
|
103
|
-
handle
|
|
104
|
-
|
|
105
|
-
|
|
102
|
+
lists and non-empty dicts/objects of the same) crosses by value; an
|
|
103
|
+
empty `{}` stays a live handle (so bags you mint in JS keep property
|
|
104
|
+
access). Everything else is a live handle owned by the domain —
|
|
105
|
+
attribute access is property lookup (methods arrive bound), calls are
|
|
106
|
+
calls, `str()` is `String()`. Non-finite floats cross tagged, never
|
|
107
|
+
silently nulled.
|
|
106
108
|
- **The domain rules hold**: handles never cross bridges; `stats()` is
|
|
107
109
|
the handle ledger and `release()` drops one early; `destroy()` is
|
|
108
110
|
idempotent, every door answers `bridge is closed` after, and the
|
|
@@ -161,6 +163,35 @@ memcpy per side, the receiver consumes the spill file, and the sender
|
|
|
161
163
|
sweeps it if the child died first. Nothing strays, and nothing is
|
|
162
164
|
silently truncated: an unsupported type is an articulate error.
|
|
163
165
|
|
|
166
|
+
## Common issues
|
|
167
|
+
|
|
168
|
+
**`Cannot find module '…'`.** `require` / `import` resolve from the
|
|
169
|
+
**Python process cwd** (`node_modules` next to your program), not from
|
|
170
|
+
the site-packages install of this wheel. `npm install lodash` in the
|
|
171
|
+
project directory is the fix; or pass `create(node='/path/to/node')` /
|
|
172
|
+
`CC_NODE_BIN` when the wrong Node is on `PATH`. Missing-module errors
|
|
173
|
+
name that cwd rule.
|
|
174
|
+
|
|
175
|
+
**Empty `{}` is a live handle.** `js.eval('({})')` stays a `JsHandle`
|
|
176
|
+
so later property use matches Node. Non-empty plain objects still cross
|
|
177
|
+
as Python `dict`s (data returns). Same-domain handles chain
|
|
178
|
+
(`h.update(…).digest(…)`); foreign-domain handles do not.
|
|
179
|
+
|
|
180
|
+
**Thenables are awaited in the child.** Promise-based npm APIs need no
|
|
181
|
+
`async`/`await` on the Python side — the call blocks until settle (or
|
|
182
|
+
raises `JsError` on reject). That is the opposite of
|
|
183
|
+
`concurrent-c-python`'s isolated surface, where every call is already a
|
|
184
|
+
JS Promise you must await.
|
|
185
|
+
|
|
186
|
+
**Wire cost vs tiny work.** Round trip is ~100µs class; a one-line JS
|
|
187
|
+
helper on three numbers loses to pure Python. Prefer Python (or a native
|
|
188
|
+
CC module) for small/hot work; use the bridge when Node/npm owns the
|
|
189
|
+
kernel (crypto, parsers, large buffers via shm).
|
|
190
|
+
|
|
191
|
+
**Crash isolation, not a sandbox.** The child inherits your environment
|
|
192
|
+
and privileges — do not evaluate untrusted JavaScript. `destroy()` is
|
|
193
|
+
cooperative; CPU-bound JS is not preemptible (wait or kill + new domain).
|
|
194
|
+
|
|
164
195
|
## Choosing the node
|
|
165
196
|
|
|
166
197
|
Same ambient-first rule as the rest of the family: the domain runs
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "concurrent-c-node"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.14.0"
|
|
8
8
|
description = "JavaScript and npm packages from Python over the Concurrent-C bridge: one spawned Node child per domain, host-controlled lifetime."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{concurrent_c_node-0.12.0 → concurrent_c_node-0.14.0}/concurrent_c_node.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{concurrent_c_node-0.12.0 → concurrent_c_node-0.14.0}/concurrent_c_node.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|