concurrent-c-node 0.23.2__tar.gz → 0.23.4__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.
Files changed (19) hide show
  1. {concurrent_c_node-0.23.2 → concurrent_c_node-0.23.4}/PKG-INFO +3 -3
  2. {concurrent_c_node-0.23.2 → concurrent_c_node-0.23.4}/README.md +2 -2
  3. {concurrent_c_node-0.23.2 → concurrent_c_node-0.23.4}/cc_node/__init__.py +70 -18
  4. {concurrent_c_node-0.23.2 → concurrent_c_node-0.23.4}/concurrent_c_node.egg-info/PKG-INFO +3 -3
  5. {concurrent_c_node-0.23.2 → concurrent_c_node-0.23.4}/pyproject.toml +1 -1
  6. {concurrent_c_node-0.23.2 → concurrent_c_node-0.23.4}/cc_node/benchmarks/__init__.py +0 -0
  7. {concurrent_c_node-0.23.2 → concurrent_c_node-0.23.4}/cc_node/benchmarks/multi_domain.py +0 -0
  8. {concurrent_c_node-0.23.2 → concurrent_c_node-0.23.4}/cc_node/benchmarks/vs_alts.py +0 -0
  9. {concurrent_c_node-0.23.2 → concurrent_c_node-0.23.4}/cc_node/broker.cjs +0 -0
  10. {concurrent_c_node-0.23.2 → concurrent_c_node-0.23.4}/cc_node/examples/__init__.py +0 -0
  11. {concurrent_c_node-0.23.2 → concurrent_c_node-0.23.4}/cc_node/examples/bench_wire.py +0 -0
  12. {concurrent_c_node-0.23.2 → concurrent_c_node-0.23.4}/cc_node/examples/use_node.py +0 -0
  13. {concurrent_c_node-0.23.2 → concurrent_c_node-0.23.4}/cc_node/magics.py +0 -0
  14. {concurrent_c_node-0.23.2 → concurrent_c_node-0.23.4}/cc_node/stdio_line.cjs +0 -0
  15. {concurrent_c_node-0.23.2 → concurrent_c_node-0.23.4}/concurrent_c_node.egg-info/SOURCES.txt +0 -0
  16. {concurrent_c_node-0.23.2 → concurrent_c_node-0.23.4}/concurrent_c_node.egg-info/dependency_links.txt +0 -0
  17. {concurrent_c_node-0.23.2 → concurrent_c_node-0.23.4}/concurrent_c_node.egg-info/requires.txt +0 -0
  18. {concurrent_c_node-0.23.2 → concurrent_c_node-0.23.4}/concurrent_c_node.egg-info/top_level.txt +0 -0
  19. {concurrent_c_node-0.23.2 → concurrent_c_node-0.23.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: concurrent-c-node
3
- Version: 0.23.2
3
+ Version: 0.23.4
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
@@ -288,8 +288,8 @@ domains here).
288
288
 
289
289
  Packages: cwd `node_modules`, same as Node itself.
290
290
 
291
- From Concurrent-C (not Python): `cc_js_new(false, &a)` hosted
292
- (libnode), or `cc_js_new(true, &a)` for this wire —
291
+ From Concurrent-C (not Python): `cc_js_new(false, a)` hosted
292
+ (libnode), or `cc_js_new(true, a)` for this wire —
293
293
  [`jsdemo.shcc`](https://github.com/sreekotay/concurrent-c/blob/main/examples/js/jsdemo.shcc).
294
294
 
295
295
  ## Publishing
@@ -273,8 +273,8 @@ domains here).
273
273
 
274
274
  Packages: cwd `node_modules`, same as Node itself.
275
275
 
276
- From Concurrent-C (not Python): `cc_js_new(false, &a)` hosted
277
- (libnode), or `cc_js_new(true, &a)` for this wire —
276
+ From Concurrent-C (not Python): `cc_js_new(false, a)` hosted
277
+ (libnode), or `cc_js_new(true, a)` for this wire —
278
278
  [`jsdemo.shcc`](https://github.com/sreekotay/concurrent-c/blob/main/examples/js/jsdemo.shcc).
279
279
 
280
280
  ## Publishing
@@ -36,6 +36,7 @@ import shutil
36
36
  import subprocess
37
37
  import sys
38
38
  import tempfile
39
+ import time
39
40
 
40
41
  __all__ = [
41
42
  "create", "get", "reset", "kernel", "reset_kernel",
@@ -43,7 +44,7 @@ __all__ = [
43
44
  "JsError", "JsHandle",
44
45
  "load_ipython_extension", "unload_ipython_extension", "__version__",
45
46
  ]
46
- __version__ = "0.23.2"
47
+ __version__ = "0.23.4"
47
48
 
48
49
  _NO_NODE = (
49
50
  "cc-node: no node executable (install Node, or set CC_NODE_BIN). "
@@ -136,8 +137,9 @@ class JsHandle:
136
137
  p.text(repr(self))
137
138
 
138
139
  def __del__(self):
139
- # Never nest a sync wire op from GC into an in-flight _req — that
140
- # steals the outer reply (e.g. returning a callback arg handle).
140
+ # Queue only never flush from GC. Pass-through handles (returned
141
+ # from a callback back to JS) defer release until the outer wire
142
+ # op completes; see Bridge._passthrough_h.
141
143
  try:
142
144
  if not self._d.closed:
143
145
  self._d._queue_release(self._h)
@@ -197,6 +199,7 @@ class Bridge:
197
199
  self._depth = 0
198
200
  self._parked = {}
199
201
  self._pending_release = []
202
+ self._passthrough_h = set()
200
203
  self._close_pending = False
201
204
  self._destroy_done = False
202
205
  self._cell_require_ready = False
@@ -217,18 +220,51 @@ class Bridge:
217
220
  raise JsError("cc-node: the node child exited") from None
218
221
 
219
222
  def _queue_release(self, hid):
223
+ if hid in self._passthrough_h:
224
+ return
220
225
  self._pending_release.append(hid)
221
- if self._depth == 0:
222
- self._flush_releases()
223
226
 
224
- def _flush_releases(self):
227
+ def _drain_pending_releases(self):
228
+ """Send queued releases (must run with depth==0, not from __del__)."""
225
229
  while self._pending_release and not self.closed:
226
230
  hid = self._pending_release.pop(0)
227
231
  try:
228
- self._req("release", h=hid)
232
+ self._wire_call("release", h=hid)
229
233
  except Exception:
230
234
  pass
231
235
 
236
+ def _on_depth_zero(self):
237
+ if self._passthrough_h:
238
+ self._pending_release.extend(self._passthrough_h)
239
+ self._passthrough_h.clear()
240
+ # One proxy drop can queue the same id more than once; the child
241
+ # deletes on first release — dedupe before draining.
242
+ seen = set()
243
+ uniq = []
244
+ for hid in self._pending_release:
245
+ if hid not in seen:
246
+ seen.add(hid)
247
+ uniq.append(hid)
248
+ self._pending_release = uniq
249
+ self._flush_shm()
250
+ if self._close_pending:
251
+ self._finish_destroy()
252
+ else:
253
+ self._drain_pending_releases()
254
+
255
+ def _wire_call(self, op, **kw):
256
+ """One wire round-trip without depth / release side effects."""
257
+ rid = self._nid
258
+ self._nid += 1
259
+ kw["id"] = rid
260
+ kw["op"] = op
261
+ try:
262
+ self._send(kw)
263
+ except (BrokenPipeError, ValueError):
264
+ self.closed = True
265
+ raise JsError("cc-node: the node child exited") from None
266
+ return self._wait_reply(rid)
267
+
232
268
  def _shm_write(self, raw):
233
269
  _shm_seq[0] += 1
234
270
  path = os.path.join(self._shm_dir, "s%d" % _shm_seq[0])
@@ -349,6 +385,10 @@ class Bridge:
349
385
  def _req(self, op, **kw):
350
386
  if self.closed:
351
387
  raise JsError("cc-node: bridge is closed")
388
+ # Drop-driven releases queue outside wire ops; drain before the
389
+ # next op so stats()/teardown see a settled ledger.
390
+ if self._depth == 0:
391
+ self._drain_pending_releases()
352
392
  rid = self._nid
353
393
  self._nid += 1
354
394
  kw["id"] = rid
@@ -364,11 +404,7 @@ class Bridge:
364
404
  finally:
365
405
  self._depth -= 1
366
406
  if self._depth == 0:
367
- self._flush_shm()
368
- if self._close_pending:
369
- self._finish_destroy()
370
- else:
371
- self._flush_releases()
407
+ self._on_depth_zero()
372
408
 
373
409
  def _serve_callback(self, msg):
374
410
  fn = self._cbs.get(msg["cb"])
@@ -376,7 +412,12 @@ class Bridge:
376
412
  if fn is None:
377
413
  raise JsError("cc-node: unknown callback")
378
414
  args = [self._decode_result(a) for a in msg["args"]]
379
- self._send({"cbr": msg["cbid"], "v": self._encode(fn(*args))})
415
+ ret = fn(*args)
416
+ if isinstance(ret, JsHandle):
417
+ # Python was a pass-through; JS re-owns the handle for the
418
+ # rest of this wire op — do not release from __del__ mid-flight.
419
+ self._passthrough_h.add(ret._h)
420
+ self._send({"cbr": msg["cbid"], "v": self._encode(ret)})
380
421
  except Exception as e: # crosses back as a JS error, message intact
381
422
  try:
382
423
  self._send({"cbr": msg["cbid"], "e": str(e)})
@@ -600,6 +641,10 @@ class Bridge:
600
641
  self._destroy_done = True
601
642
  self._close_pending = False
602
643
  self.closed = True
644
+ try:
645
+ self._drain_pending_releases()
646
+ except Exception:
647
+ pass
603
648
  try:
604
649
  if self._p.poll() is None and not self._wire_w.closed:
605
650
  rid = self._nid
@@ -611,12 +656,19 @@ class Bridge:
611
656
  self._wire_w.close()
612
657
  except Exception:
613
658
  pass
614
- # Drain to EOF so the broker's farewell reply has somewhere to
615
- # land closing the reply fd under its write is an EPIPE crash
616
- # in the child.
659
+ # Drain the farewell reply (bounded an idle child must not
660
+ # block teardown forever).
617
661
  try:
618
- while self._read_line():
619
- pass
662
+ deadline = time.monotonic() + 2.0
663
+ fd = self._resp_fd
664
+ while fd is not None and time.monotonic() < deadline:
665
+ if self._p.poll() is not None:
666
+ break
667
+ ready, _, _ = select.select([fd], [], [], 0.1)
668
+ if not ready:
669
+ continue
670
+ if not self._read_line():
671
+ break
620
672
  except Exception:
621
673
  pass
622
674
  try:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: concurrent-c-node
3
- Version: 0.23.2
3
+ Version: 0.23.4
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
@@ -288,8 +288,8 @@ domains here).
288
288
 
289
289
  Packages: cwd `node_modules`, same as Node itself.
290
290
 
291
- From Concurrent-C (not Python): `cc_js_new(false, &a)` hosted
292
- (libnode), or `cc_js_new(true, &a)` for this wire —
291
+ From Concurrent-C (not Python): `cc_js_new(false, a)` hosted
292
+ (libnode), or `cc_js_new(true, a)` for this wire —
293
293
  [`jsdemo.shcc`](https://github.com/sreekotay/concurrent-c/blob/main/examples/js/jsdemo.shcc).
294
294
 
295
295
  ## Publishing
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "concurrent-c-node"
7
- version = "0.23.2"
7
+ version = "0.23.4"
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"