chalk-remote-call-python 1.10.8__tar.gz → 1.10.9__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 (74) hide show
  1. {chalk_remote_call_python-1.10.8/chalk_remote_call_python.egg-info → chalk_remote_call_python-1.10.9}/PKG-INFO +1 -1
  2. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk-remote-call-rs/chalk-remote-call-server/src/python_bridge.rs +126 -0
  3. chalk_remote_call_python-1.10.9/chalk_remote_call/_version.py +1 -0
  4. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9/chalk_remote_call_python.egg-info}/PKG-INFO +1 -1
  5. chalk_remote_call_python-1.10.8/chalk_remote_call/_version.py +0 -1
  6. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/MANIFEST.in +0 -0
  7. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/README.md +0 -0
  8. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk-remote-call-rs/Cargo.lock +0 -0
  9. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk-remote-call-rs/Cargo.toml +0 -0
  10. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk-remote-call-rs/chalk-remote-call-proto/Cargo.toml +0 -0
  11. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.auth.v1.rs +0 -0
  12. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.common.v1.rs +0 -0
  13. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.runtime.v1.rs +0 -0
  14. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.runtime.v1.tonic.rs +0 -0
  15. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.utils.v1.rs +0 -0
  16. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/descriptor.bin +0 -0
  17. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk-remote-call-rs/chalk-remote-call-proto/src/lib.rs +0 -0
  18. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk-remote-call-rs/chalk-remote-call-server/Cargo.toml +0 -0
  19. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk-remote-call-rs/chalk-remote-call-server/build.rs +0 -0
  20. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk-remote-call-rs/chalk-remote-call-server/src/async_service.rs +0 -0
  21. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk-remote-call-rs/chalk-remote-call-server/src/coalesce.rs +0 -0
  22. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk-remote-call-rs/chalk-remote-call-server/src/fnq_client.rs +0 -0
  23. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk-remote-call-rs/chalk-remote-call-server/src/lib.rs +0 -0
  24. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk-remote-call-rs/chalk-remote-call-server/src/metrics.rs +0 -0
  25. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk-remote-call-rs/chalk-remote-call-server/src/self_consumer.rs +0 -0
  26. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk-remote-call-rs/chalk-remote-call-server/src/server.rs +0 -0
  27. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk-remote-call-rs/chalk-remote-call-server/src/service.rs +0 -0
  28. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk-remote-call-rs/rust-toolchain.toml +0 -0
  29. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/__init__.py +0 -0
  30. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/__main__.py +0 -0
  31. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/__init__.py +0 -0
  32. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/chalk/__init__.py +0 -0
  33. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/chalk/auth/__init__.py +0 -0
  34. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/chalk/auth/v1/__init__.py +0 -0
  35. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/chalk/auth/v1/permissions_pb2.py +0 -0
  36. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/chalk/auth/v1/permissions_pb2_grpc.py +0 -0
  37. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/chalk/common/__init__.py +0 -0
  38. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/chalk/common/v1/__init__.py +0 -0
  39. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/chalk/common/v1/chalk_error_pb2.py +0 -0
  40. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/chalk/common/v1/chalk_error_pb2_grpc.py +0 -0
  41. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/chalk/runtime/__init__.py +0 -0
  42. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/chalk/runtime/v1/__init__.py +0 -0
  43. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/chalk/runtime/v1/fnq_consumer_pb2.py +0 -0
  44. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/chalk/runtime/v1/fnq_consumer_pb2_grpc.py +0 -0
  45. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/chalk/runtime/v1/remote_python_call_pb2.py +0 -0
  46. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/chalk/runtime/v1/remote_python_call_pb2_grpc.py +0 -0
  47. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/chalk/utils/__init__.py +0 -0
  48. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/chalk/utils/v1/__init__.py +0 -0
  49. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/chalk/utils/v1/encoding_pb2.py +0 -0
  50. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/chalk/utils/v1/encoding_pb2_grpc.py +0 -0
  51. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/chalk/utils/v1/field_change_pb2.py +0 -0
  52. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/chalk/utils/v1/field_change_pb2_grpc.py +0 -0
  53. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/chalk/utils/v1/large_pb2.py +0 -0
  54. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/chalk/utils/v1/large_pb2_grpc.py +0 -0
  55. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/chalk/utils/v1/sensitive_pb2.py +0 -0
  56. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_gen/chalk/utils/v1/sensitive_pb2_grpc.py +0 -0
  57. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/_native.pyi +0 -0
  58. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/arrow_utils.py +0 -0
  59. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/cli.py +0 -0
  60. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/errors.py +0 -0
  61. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/handler_loader.py +0 -0
  62. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/input_transform.py +0 -0
  63. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/logging.py +0 -0
  64. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/server.py +0 -0
  65. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/servicer.py +0 -0
  66. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call/tracing.py +0 -0
  67. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call_python.egg-info/SOURCES.txt +0 -0
  68. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call_python.egg-info/dependency_links.txt +0 -0
  69. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call_python.egg-info/entry_points.txt +0 -0
  70. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call_python.egg-info/requires.txt +0 -0
  71. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/chalk_remote_call_python.egg-info/top_level.txt +0 -0
  72. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/pyproject.toml +0 -0
  73. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/setup.cfg +0 -0
  74. {chalk_remote_call_python-1.10.8 → chalk_remote_call_python-1.10.9}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chalk-remote-call-python
3
- Version: 1.10.8
3
+ Version: 1.10.9
4
4
  Summary: Chalk remote call Python runtime interface client
5
5
  Author: Chalk AI, Inc.
6
6
  Project-URL: Homepage, https://chalk.ai
@@ -1,3 +1,4 @@
1
+ use std::cell::OnceCell;
1
2
  use std::collections::HashMap;
2
3
  use std::sync::{Arc, Mutex};
3
4
 
@@ -8,6 +9,46 @@ use pyo3::types::{PyBytes, PyDict, PyList};
8
9
  use tokio::sync::{mpsc, Notify};
9
10
  use tonic::Status;
10
11
 
12
+ thread_local! {
13
+ // This value belongs only to its OS thread, including its destructor.
14
+ static THREAD_ATTACHMENT: OnceCell<ThreadAttachment> = const { OnceCell::new() };
15
+ }
16
+
17
+ struct ThreadAttachment {
18
+ state: pyo3::ffi::PyGILState_STATE,
19
+ }
20
+
21
+ impl ThreadAttachment {
22
+ fn new(_py: Python<'_>) -> Self {
23
+ // SAFETY: the Python token guarantees an attached thread. This nested
24
+ // Ensure returns LOCKED and pins its state after the outer guard detaches.
25
+ // The outer PyO3 guard still releases the GIL between invocations.
26
+ let state = unsafe { pyo3::ffi::PyGILState_Ensure() };
27
+ Self { state }
28
+ }
29
+ }
30
+
31
+ impl Drop for ThreadAttachment {
32
+ fn drop(&mut self) {
33
+ // Tokio joins its blocking threads while start_server's caller is detached.
34
+ // Reattach before releasing our matching LOCKED token; PyO3 then releases
35
+ // its outer token, deleting the state without retaining the GIL.
36
+ let _ = Python::try_attach(|_| unsafe {
37
+ // SAFETY: creation and destruction run on this same OS thread under
38
+ // the GIL, and this releases exactly the nested Ensure above.
39
+ pyo3::ffi::PyGILState_Release(self.state);
40
+ });
41
+ }
42
+ }
43
+
44
+ fn retain_thread_state(py: Python<'_>) {
45
+ // pybind11 can cache this state in native TLS. Its lifetime must match the
46
+ // blocking worker rather than an individual Python invocation.
47
+ THREAD_ATTACHMENT.with(|attachment| {
48
+ attachment.get_or_init(|| ThreadAttachment::new(py));
49
+ });
50
+ }
51
+
11
52
  /// Holds references to the Python handler and bridge function.
12
53
  pub struct PythonHandler {
13
54
  /// The user's handler callable.
@@ -59,6 +100,7 @@ impl PythonHandler {
59
100
  let owner = Arc::clone(self);
60
101
  tokio::task::spawn_blocking(move || {
61
102
  Python::attach(|py| -> Result<(), PythonError> {
103
+ retain_thread_state(py);
62
104
  let handler = owner.handler.clone_ref(py);
63
105
  let process_fn = &owner.process_fn;
64
106
  let arg_names = &owner.arg_names;
@@ -125,6 +167,7 @@ impl PythonHandler {
125
167
  let owner = Arc::clone(self);
126
168
  tokio::task::spawn_blocking(move || {
127
169
  Python::attach(|py| -> Result<Vec<Vec<u8>>, PythonError> {
170
+ retain_thread_state(py);
128
171
  let handler = &owner.handler;
129
172
  let process_fn = &owner.process_fn;
130
173
  let arg_names = &owner.arg_names;
@@ -491,4 +534,87 @@ mod tests {
491
534
  fn coalesced_dispatch_yields_under_gil_contention() {
492
535
  dispatch_yields_while_gil_is_held(Dispatch::Coalesced);
493
536
  }
537
+ fn contains_thread_state(_py: Python<'_>, id: i64) -> bool {
538
+ // SAFETY: interpreter state iteration and ID access occur under the GIL.
539
+ unsafe {
540
+ let interpreter =
541
+ pyo3::ffi::PyThreadState_GetInterpreter(pyo3::ffi::PyThreadState_Get());
542
+ let mut state = pyo3::ffi::PyInterpreterState_ThreadHead(interpreter);
543
+ while !state.is_null() {
544
+ if pyo3::ffi::PyThreadState_GetID(state) == id {
545
+ return true;
546
+ }
547
+ state = pyo3::ffi::PyThreadState_Next(state);
548
+ }
549
+ }
550
+ false
551
+ }
552
+
553
+ async fn blocking_thread_state_id() -> i64 {
554
+ tokio::task::spawn_blocking(|| {
555
+ Python::attach(|py| {
556
+ retain_thread_state(py);
557
+ // SAFETY: this call runs under the live attachment above.
558
+ unsafe { pyo3::ffi::PyThreadState_GetID(pyo3::ffi::PyThreadState_Get()) }
559
+ })
560
+ })
561
+ .await
562
+ .unwrap()
563
+ }
564
+
565
+ #[test]
566
+ fn blocking_worker_reuses_state_and_releases_it_at_shutdown() {
567
+ Python::initialize();
568
+ let runtime = tokio::runtime::Builder::new_current_thread()
569
+ .max_blocking_threads(1)
570
+ .build()
571
+ .unwrap();
572
+ let ids = runtime.block_on(async {
573
+ let mut ids = Vec::new();
574
+ for _ in 0..40 {
575
+ ids.push(blocking_thread_state_id().await);
576
+ }
577
+ ids
578
+ });
579
+ assert!(ids.iter().all(|id| *id == ids[0]));
580
+ assert!(Python::attach(|py| contains_thread_state(py, ids[0])));
581
+ let failed = runtime.block_on(async {
582
+ tokio::task::spawn_blocking(|| {
583
+ Python::attach(|py| {
584
+ retain_thread_state(py);
585
+ panic!("test blocking job panic");
586
+ });
587
+ })
588
+ .await
589
+ .unwrap_err()
590
+ });
591
+ assert!(failed.is_panic());
592
+ assert_eq!(ids[0], runtime.block_on(blocking_thread_state_id()));
593
+ // Joining must happen with this test thread detached so TLS can clean up.
594
+ drop(runtime);
595
+ assert!(!Python::attach(|py| contains_thread_state(py, ids[0])));
596
+ }
597
+
598
+ #[test]
599
+ fn idle_blocking_worker_releases_state_before_replacement() {
600
+ Python::initialize();
601
+ let runtime = tokio::runtime::Builder::new_current_thread()
602
+ .max_blocking_threads(1)
603
+ .thread_keep_alive(Duration::from_millis(20))
604
+ .build()
605
+ .unwrap();
606
+ let original = runtime.block_on(blocking_thread_state_id());
607
+ let deadline = std::time::Instant::now() + Duration::from_secs(5);
608
+ while Python::attach(|py| contains_thread_state(py, original)) {
609
+ assert!(
610
+ std::time::Instant::now() < deadline,
611
+ "idle worker state was not released"
612
+ );
613
+ std::thread::sleep(Duration::from_millis(5));
614
+ }
615
+ let replacement = runtime.block_on(blocking_thread_state_id());
616
+ assert_ne!(original, replacement);
617
+ drop(runtime);
618
+ assert!(!Python::attach(|py| contains_thread_state(py, replacement)));
619
+ }
494
620
  }
@@ -0,0 +1 @@
1
+ __version__ = "1.10.9"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chalk-remote-call-python
3
- Version: 1.10.8
3
+ Version: 1.10.9
4
4
  Summary: Chalk remote call Python runtime interface client
5
5
  Author: Chalk AI, Inc.
6
6
  Project-URL: Homepage, https://chalk.ai
@@ -1 +0,0 @@
1
- __version__ = "1.10.8"