chalk-remote-call-python 1.6.2__tar.gz → 1.7.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.
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/PKG-INFO +1 -1
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk-remote-call-rs/chalk-remote-call-server/src/python_bridge.rs +39 -8
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk-remote-call-rs/chalk-remote-call-server/src/service.rs +20 -19
- chalk_remote_call_python-1.7.0/chalk_remote_call/_version.py +1 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/servicer.py +30 -6
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call_python.egg-info/PKG-INFO +1 -1
- chalk_remote_call_python-1.6.2/chalk_remote_call/_version.py +0 -1
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/MANIFEST.in +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/README.md +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk-remote-call-rs/Cargo.lock +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk-remote-call-rs/Cargo.toml +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk-remote-call-rs/chalk-remote-call-proto/Cargo.toml +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.auth.v1.rs +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.common.v1.rs +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.runtime.v1.rs +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.runtime.v1.tonic.rs +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.utils.v1.rs +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/descriptor.bin +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/lib.rs +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk-remote-call-rs/chalk-remote-call-server/Cargo.toml +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk-remote-call-rs/chalk-remote-call-server/src/coalesce.rs +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk-remote-call-rs/chalk-remote-call-server/src/lib.rs +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk-remote-call-rs/chalk-remote-call-server/src/server.rs +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk-remote-call-rs/rust-toolchain.toml +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/__init__.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/__main__.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/_gen/__init__.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/_gen/chalk/__init__.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/_gen/chalk/auth/__init__.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/_gen/chalk/auth/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/_gen/chalk/auth/v1/permissions_pb2.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/_gen/chalk/auth/v1/permissions_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/_gen/chalk/common/__init__.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/_gen/chalk/common/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/_gen/chalk/common/v1/chalk_error_pb2.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/_gen/chalk/common/v1/chalk_error_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/_gen/chalk/runtime/__init__.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/_gen/chalk/runtime/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/_gen/chalk/runtime/v1/remote_python_call_pb2.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/_gen/chalk/runtime/v1/remote_python_call_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/_gen/chalk/utils/__init__.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/_gen/chalk/utils/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/_gen/chalk/utils/v1/encoding_pb2.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/_gen/chalk/utils/v1/encoding_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/_gen/chalk/utils/v1/field_change_pb2.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/_gen/chalk/utils/v1/field_change_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/_gen/chalk/utils/v1/sensitive_pb2.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/_gen/chalk/utils/v1/sensitive_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/_native.pyi +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/arrow_utils.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/cli.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/handler_loader.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/input_transform.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/server.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/tracing.py +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call_python.egg-info/SOURCES.txt +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call_python.egg-info/dependency_links.txt +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call_python.egg-info/entry_points.txt +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call_python.egg-info/requires.txt +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call_python.egg-info/top_level.txt +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/pyproject.toml +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/setup.cfg +0 -0
- {chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/setup.py +0 -0
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
use std::collections::HashMap;
|
|
2
2
|
|
|
3
|
+
use chalk_remote_call_proto::chalk::runtime::v1::CallFunctionResponse;
|
|
3
4
|
use pyo3::prelude::*;
|
|
4
5
|
use pyo3::types::{PyBytes, PyDict, PyList};
|
|
6
|
+
use tokio::sync::mpsc;
|
|
7
|
+
use tonic::Status;
|
|
5
8
|
|
|
6
9
|
/// Holds references to the Python handler and bridge function.
|
|
7
10
|
pub struct PythonHandler {
|
|
@@ -34,29 +37,35 @@ impl PythonHandler {
|
|
|
34
37
|
}
|
|
35
38
|
}
|
|
36
39
|
|
|
37
|
-
/// Non-batched
|
|
38
|
-
///
|
|
39
|
-
|
|
40
|
-
/// handler is a generator, one otherwise.
|
|
41
|
-
pub async fn call(
|
|
40
|
+
/// Non-batched streaming dispatch. Calls `process_batches(..., emit)` so
|
|
41
|
+
/// generator results are sent to the gRPC stream as each yield is encoded.
|
|
42
|
+
pub async fn call_streaming(
|
|
42
43
|
&self,
|
|
43
44
|
ipc_bytes: Vec<u8>,
|
|
44
45
|
function_name: String,
|
|
45
46
|
metadata: HashMap<String, String>,
|
|
46
47
|
peer: String,
|
|
47
|
-
|
|
48
|
+
response_tx: mpsc::Sender<Result<CallFunctionResponse, Status>>,
|
|
49
|
+
) -> Result<(), PythonError> {
|
|
48
50
|
let (handler, process_fn) =
|
|
49
51
|
Python::attach(|py| (self.handler.clone_ref(py), self.process_fn.clone_ref(py)));
|
|
50
52
|
let arg_names = self.arg_names.clone();
|
|
51
53
|
|
|
52
54
|
tokio::task::spawn_blocking(move || {
|
|
53
|
-
Python::attach(|py| -> Result<
|
|
55
|
+
Python::attach(|py| -> Result<(), PythonError> {
|
|
54
56
|
let py_bytes = PyBytes::new(py, &ipc_bytes).into_any().unbind();
|
|
55
57
|
let py_ctx = build_context(py, &function_name, &metadata, &peer)?
|
|
56
58
|
.into_any()
|
|
57
59
|
.unbind();
|
|
58
60
|
let py_arg_names = build_arg_names(py, arg_names.as_deref())?;
|
|
59
|
-
|
|
61
|
+
let emitter = Py::new(py, ChunkEmitter { response_tx })
|
|
62
|
+
.map_err(|e| into_python_error(py, e))?
|
|
63
|
+
.into_any();
|
|
64
|
+
|
|
65
|
+
process_fn
|
|
66
|
+
.call1(py, (py_bytes, handler, py_arg_names, py_ctx, emitter))
|
|
67
|
+
.map_err(|e| into_python_error(py, e))?;
|
|
68
|
+
Ok(())
|
|
60
69
|
})
|
|
61
70
|
})
|
|
62
71
|
.await
|
|
@@ -104,6 +113,28 @@ impl PythonHandler {
|
|
|
104
113
|
|
|
105
114
|
// ---- Shared helpers -------------------------------------------------------
|
|
106
115
|
|
|
116
|
+
#[pyclass]
|
|
117
|
+
struct ChunkEmitter {
|
|
118
|
+
response_tx: mpsc::Sender<Result<CallFunctionResponse, Status>>,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
#[pymethods]
|
|
122
|
+
impl ChunkEmitter {
|
|
123
|
+
fn __call__(&self, py: Python<'_>, chunk: &Bound<'_, PyBytes>) -> PyResult<()> {
|
|
124
|
+
// Copy the bytes out of Python memory before releasing the GIL.
|
|
125
|
+
let msg = CallFunctionResponse {
|
|
126
|
+
feather_stream: chunk.as_bytes().to_vec().into(),
|
|
127
|
+
};
|
|
128
|
+
// Release the GIL while parked in blocking_send so a backpressured
|
|
129
|
+
// client can't stall every other request's Python execution. Collapse
|
|
130
|
+
// the (large) SendError to `()` inside the closure so it isn't carried
|
|
131
|
+
// across the detach boundary.
|
|
132
|
+
py.detach(|| self.response_tx.blocking_send(Ok(msg)).map_err(|_| ()))
|
|
133
|
+
.map_err(|()| pyo3::exceptions::PyRuntimeError::new_err("client disconnected"))?;
|
|
134
|
+
Ok(())
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
107
138
|
/// Build a `{"function_name": str, "peer": str, "metadata": dict}` context dict for one caller.
|
|
108
139
|
fn build_context<'py>(
|
|
109
140
|
py: Python<'py>,
|
|
@@ -5,7 +5,7 @@ use chalk_remote_call_proto::chalk::runtime::v1::remote_call_service_server::Rem
|
|
|
5
5
|
use chalk_remote_call_proto::chalk::runtime::v1::{CallFunctionRequest, CallFunctionResponse};
|
|
6
6
|
use tokio_stream::wrappers::ReceiverStream;
|
|
7
7
|
use tonic::{Request, Response, Status, Streaming};
|
|
8
|
-
use tracing::{error, instrument};
|
|
8
|
+
use tracing::{debug, error, instrument};
|
|
9
9
|
|
|
10
10
|
use crate::coalesce::{BufferedCall, CoalescingQueue};
|
|
11
11
|
use crate::python_bridge::{CallerInput, PythonHandler};
|
|
@@ -91,25 +91,26 @@ impl RemoteCallService for RemoteCallServiceImpl {
|
|
|
91
91
|
// Single-request path.
|
|
92
92
|
let handler = self.python_handler.clone();
|
|
93
93
|
tokio::spawn(async move {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if tx.send(Ok(msg)).await.is_err() {
|
|
101
|
-
// Client disconnected
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
94
|
+
let error_tx = tx.clone();
|
|
95
|
+
match handler
|
|
96
|
+
.call_streaming(all_bytes, function_name, metadata, peer, tx)
|
|
97
|
+
.await
|
|
98
|
+
{
|
|
99
|
+
Ok(()) => {}
|
|
106
100
|
Err(e) => {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
.
|
|
110
|
-
|
|
111
|
-
)
|
|
112
|
-
|
|
101
|
+
if error_tx.is_closed() {
|
|
102
|
+
// The only reason blocking_send fails is a dropped
|
|
103
|
+
// receiver — the client went away mid-stream. Not a
|
|
104
|
+
// handler failure; nothing to send.
|
|
105
|
+
debug!("client disconnected during streaming response");
|
|
106
|
+
} else {
|
|
107
|
+
error!("Python handler error: {}", e);
|
|
108
|
+
let _ = error_tx
|
|
109
|
+
.send(Err(Status::unknown(format!(
|
|
110
|
+
"Exception raised during handler execution: {e}"
|
|
111
|
+
))))
|
|
112
|
+
.await;
|
|
113
|
+
}
|
|
113
114
|
}
|
|
114
115
|
}
|
|
115
116
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.7.0"
|
{chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/servicer.py
RENAMED
|
@@ -158,11 +158,22 @@ def _collect_async_gen(agen) -> list:
|
|
|
158
158
|
return _run_async(_collect())
|
|
159
159
|
|
|
160
160
|
|
|
161
|
+
def _drain_async_gen(agen, emit: Callable[[bytes], None]) -> None:
|
|
162
|
+
"""Drain an async generator and emit each encoded item immediately."""
|
|
163
|
+
|
|
164
|
+
async def _drain():
|
|
165
|
+
async for item in agen:
|
|
166
|
+
emit(encode_record_batch(_coerce_to_record_batch(item)))
|
|
167
|
+
|
|
168
|
+
_run_async(_drain())
|
|
169
|
+
|
|
170
|
+
|
|
161
171
|
def process_batches(
|
|
162
172
|
ipc_bytes: bytes,
|
|
163
173
|
handler: Callable[..., Any],
|
|
164
174
|
arg_names: list[str] | None,
|
|
165
175
|
context_metadata: dict[str, Any],
|
|
176
|
+
emit: Callable[[bytes], None] | None = None,
|
|
166
177
|
) -> list[bytes]:
|
|
167
178
|
"""Bridge function called from Rust via PyO3.
|
|
168
179
|
|
|
@@ -170,9 +181,18 @@ def process_batches(
|
|
|
170
181
|
coerces the result, and encodes the response.
|
|
171
182
|
|
|
172
183
|
Returns a list of IPC-encoded response bytes (one per input batch).
|
|
184
|
+
When ``emit`` is provided, each encoded response is emitted immediately and
|
|
185
|
+
the returned list is empty.
|
|
173
186
|
"""
|
|
174
187
|
batches = decode_ipc_stream(ipc_bytes)
|
|
175
188
|
results = []
|
|
189
|
+
|
|
190
|
+
def append_or_emit(chunk: bytes) -> None:
|
|
191
|
+
if emit is None:
|
|
192
|
+
results.append(chunk)
|
|
193
|
+
else:
|
|
194
|
+
emit(chunk)
|
|
195
|
+
|
|
176
196
|
for batch in batches:
|
|
177
197
|
try:
|
|
178
198
|
event = transform(batch, arg_names)
|
|
@@ -193,15 +213,19 @@ def process_batches(
|
|
|
193
213
|
# Sync generator: encode each yielded value as a separate chunk.
|
|
194
214
|
for item in result:
|
|
195
215
|
result_batch = _coerce_to_record_batch(item)
|
|
196
|
-
|
|
216
|
+
append_or_emit(encode_record_batch(result_batch))
|
|
197
217
|
elif inspect.isasyncgen(result):
|
|
198
|
-
# Async generator:
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
218
|
+
# Async generator: stream when possible, otherwise preserve
|
|
219
|
+
# the existing list-returning bridge contract.
|
|
220
|
+
if emit is not None:
|
|
221
|
+
_drain_async_gen(result, emit)
|
|
222
|
+
else:
|
|
223
|
+
for item in _collect_async_gen(result):
|
|
224
|
+
result_batch = _coerce_to_record_batch(item)
|
|
225
|
+
results.append(encode_record_batch(result_batch))
|
|
202
226
|
else:
|
|
203
227
|
result_batch = _coerce_to_record_batch(result)
|
|
204
|
-
|
|
228
|
+
append_or_emit(encode_record_batch(result_batch))
|
|
205
229
|
except Exception as e:
|
|
206
230
|
raise RuntimeError(f"Failed to encode response: {e}") from e
|
|
207
231
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.6.2"
|
|
File without changes
|
|
File without changes
|
{chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk-remote-call-rs/Cargo.lock
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk-remote-call-rs/Cargo.toml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/__init__.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/__main__.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/_gen/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/_native.pyi
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/arrow_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/server.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.6.2 → chalk_remote_call_python-1.7.0}/chalk_remote_call/tracing.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|