chalk-remote-call-python 1.5.1__tar.gz → 1.6.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.5.1 → chalk_remote_call_python-1.6.0}/PKG-INFO +1 -1
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk-remote-call-rs/chalk-remote-call-server/src/python_bridge.rs +11 -4
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk-remote-call-rs/chalk-remote-call-server/src/service.rs +6 -1
- chalk_remote_call_python-1.6.0/chalk_remote_call/_version.py +1 -0
- chalk_remote_call_python-1.6.0/chalk_remote_call/tracing.py +314 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call_python.egg-info/PKG-INFO +1 -1
- chalk_remote_call_python-1.5.1/chalk_remote_call/_version.py +0 -1
- chalk_remote_call_python-1.5.1/chalk_remote_call/tracing.py +0 -150
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/MANIFEST.in +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/README.md +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk-remote-call-rs/Cargo.lock +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk-remote-call-rs/Cargo.toml +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk-remote-call-rs/chalk-remote-call-proto/Cargo.toml +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.auth.v1.rs +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.common.v1.rs +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.runtime.v1.rs +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.runtime.v1.tonic.rs +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.utils.v1.rs +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/descriptor.bin +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/lib.rs +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk-remote-call-rs/chalk-remote-call-server/Cargo.toml +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk-remote-call-rs/chalk-remote-call-server/src/coalesce.rs +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk-remote-call-rs/chalk-remote-call-server/src/lib.rs +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk-remote-call-rs/chalk-remote-call-server/src/server.rs +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk-remote-call-rs/rust-toolchain.toml +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/__init__.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/__main__.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/_gen/__init__.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/_gen/chalk/__init__.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/_gen/chalk/auth/__init__.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/_gen/chalk/auth/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/_gen/chalk/auth/v1/permissions_pb2.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/_gen/chalk/auth/v1/permissions_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/_gen/chalk/common/__init__.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/_gen/chalk/common/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/_gen/chalk/common/v1/chalk_error_pb2.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/_gen/chalk/common/v1/chalk_error_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/_gen/chalk/runtime/__init__.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/_gen/chalk/runtime/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/_gen/chalk/runtime/v1/remote_python_call_pb2.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/_gen/chalk/runtime/v1/remote_python_call_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/_gen/chalk/utils/__init__.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/_gen/chalk/utils/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/_gen/chalk/utils/v1/encoding_pb2.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/_gen/chalk/utils/v1/encoding_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/_gen/chalk/utils/v1/field_change_pb2.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/_gen/chalk/utils/v1/field_change_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/_gen/chalk/utils/v1/sensitive_pb2.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/_gen/chalk/utils/v1/sensitive_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/_native.pyi +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/arrow_utils.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/cli.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/handler_loader.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/input_transform.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/server.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/servicer.py +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call_python.egg-info/SOURCES.txt +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call_python.egg-info/dependency_links.txt +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call_python.egg-info/entry_points.txt +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call_python.egg-info/requires.txt +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call_python.egg-info/top_level.txt +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/pyproject.toml +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/setup.cfg +0 -0
- {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/setup.py +0 -0
|
@@ -15,11 +15,12 @@ pub struct PythonHandler {
|
|
|
15
15
|
arg_names: Option<Vec<String>>,
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
/// One caller's input: request bytes + gRPC metadata + peer address.
|
|
18
|
+
/// One caller's input: request bytes + function name + gRPC metadata + peer address.
|
|
19
19
|
/// Used identically in both the single-request and coalesced paths; the
|
|
20
20
|
/// coalesced path just has N of them.
|
|
21
21
|
pub struct CallerInput {
|
|
22
22
|
pub ipc_bytes: Vec<u8>,
|
|
23
|
+
pub function_name: String,
|
|
23
24
|
pub metadata: HashMap<String, String>,
|
|
24
25
|
pub peer: String,
|
|
25
26
|
}
|
|
@@ -40,6 +41,7 @@ impl PythonHandler {
|
|
|
40
41
|
pub async fn call(
|
|
41
42
|
&self,
|
|
42
43
|
ipc_bytes: Vec<u8>,
|
|
44
|
+
function_name: String,
|
|
43
45
|
metadata: HashMap<String, String>,
|
|
44
46
|
peer: String,
|
|
45
47
|
) -> Result<Vec<Vec<u8>>, PythonError> {
|
|
@@ -50,7 +52,9 @@ impl PythonHandler {
|
|
|
50
52
|
tokio::task::spawn_blocking(move || {
|
|
51
53
|
Python::attach(|py| -> Result<Vec<Vec<u8>>, PythonError> {
|
|
52
54
|
let py_bytes = PyBytes::new(py, &ipc_bytes).into_any().unbind();
|
|
53
|
-
let py_ctx = build_context(py, &metadata, &peer)
|
|
55
|
+
let py_ctx = build_context(py, &function_name, &metadata, &peer)?
|
|
56
|
+
.into_any()
|
|
57
|
+
.unbind();
|
|
54
58
|
let py_arg_names = build_arg_names(py, arg_names.as_deref())?;
|
|
55
59
|
invoke_process_fn(py, &process_fn, &handler, py_bytes, py_arg_names, py_ctx)
|
|
56
60
|
})
|
|
@@ -79,7 +83,7 @@ impl PythonHandler {
|
|
|
79
83
|
.append(PyBytes::new(py, &c.ipc_bytes))
|
|
80
84
|
.map_err(|e| into_python_error(py, e))?;
|
|
81
85
|
py_contexts
|
|
82
|
-
.append(build_context(py, &c.metadata, &c.peer)?)
|
|
86
|
+
.append(build_context(py, &c.function_name, &c.metadata, &c.peer)?)
|
|
83
87
|
.map_err(|e| into_python_error(py, e))?;
|
|
84
88
|
}
|
|
85
89
|
let py_arg_names = build_arg_names(py, arg_names.as_deref())?;
|
|
@@ -100,9 +104,10 @@ impl PythonHandler {
|
|
|
100
104
|
|
|
101
105
|
// ---- Shared helpers -------------------------------------------------------
|
|
102
106
|
|
|
103
|
-
/// Build a `{"peer": str, "metadata": dict}` context dict for one caller.
|
|
107
|
+
/// Build a `{"function_name": str, "peer": str, "metadata": dict}` context dict for one caller.
|
|
104
108
|
fn build_context<'py>(
|
|
105
109
|
py: Python<'py>,
|
|
110
|
+
function_name: &str,
|
|
106
111
|
metadata: &HashMap<String, String>,
|
|
107
112
|
peer: &str,
|
|
108
113
|
) -> Result<Bound<'py, PyDict>, PythonError> {
|
|
@@ -113,6 +118,8 @@ fn build_context<'py>(
|
|
|
113
118
|
.map_err(|e| into_python_error(py, e))?;
|
|
114
119
|
}
|
|
115
120
|
let ctx = PyDict::new(py);
|
|
121
|
+
ctx.set_item("function_name", function_name)
|
|
122
|
+
.map_err(|e| into_python_error(py, e))?;
|
|
116
123
|
ctx.set_item("peer", peer)
|
|
117
124
|
.map_err(|e| into_python_error(py, e))?;
|
|
118
125
|
ctx.set_item("metadata", meta_dict)
|
|
@@ -40,7 +40,11 @@ impl RemoteCallService for RemoteCallServiceImpl {
|
|
|
40
40
|
|
|
41
41
|
// Accumulate all feather_stream bytes from the request stream
|
|
42
42
|
let mut all_bytes: Vec<u8> = Vec::new();
|
|
43
|
+
let mut function_name = String::new();
|
|
43
44
|
while let Some(req) = stream.message().await? {
|
|
45
|
+
if function_name.is_empty() && !req.name.is_empty() {
|
|
46
|
+
function_name = req.name.clone();
|
|
47
|
+
}
|
|
44
48
|
all_bytes.extend_from_slice(&req.feather_stream);
|
|
45
49
|
if all_bytes.len() > MAX_REQUEST_SIZE {
|
|
46
50
|
return Err(Status::resource_exhausted(format!(
|
|
@@ -74,6 +78,7 @@ impl RemoteCallService for RemoteCallServiceImpl {
|
|
|
74
78
|
.submit(BufferedCall {
|
|
75
79
|
input: CallerInput {
|
|
76
80
|
ipc_bytes: all_bytes,
|
|
81
|
+
function_name,
|
|
77
82
|
metadata,
|
|
78
83
|
peer,
|
|
79
84
|
},
|
|
@@ -86,7 +91,7 @@ impl RemoteCallService for RemoteCallServiceImpl {
|
|
|
86
91
|
// Single-request path.
|
|
87
92
|
let handler = self.python_handler.clone();
|
|
88
93
|
tokio::spawn(async move {
|
|
89
|
-
match handler.call(all_bytes, metadata, peer).await {
|
|
94
|
+
match handler.call(all_bytes, function_name, metadata, peer).await {
|
|
90
95
|
Ok(response_chunks) => {
|
|
91
96
|
for chunk in response_chunks {
|
|
92
97
|
let msg = CallFunctionResponse {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.6.0"
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import contextlib
|
|
4
|
+
import importlib
|
|
5
|
+
import os
|
|
6
|
+
from collections.abc import Iterator, Mapping, Sequence
|
|
7
|
+
from typing import Any, cast
|
|
8
|
+
|
|
9
|
+
_TRACER_NAME = "chalk_remote_call"
|
|
10
|
+
_TRACE_CONTEXT_METADATA_KEYS = ("traceparent", "tracestate", "baggage")
|
|
11
|
+
_REMOTE_FUNCTION_TRACE_POLICY_ENV_VAR = "CHALK_REMOTE_FUNCTION_TRACE_POLICY"
|
|
12
|
+
_REMOTE_FUNCTION_TRACE_SAMPLE_RATE_ENV_VAR = "CHALK_REMOTE_FUNCTION_TRACE_SAMPLE_RATE"
|
|
13
|
+
_TRACE_POLICY_PARENT_BASED_ALWAYS_OFF = "parentbased_always_off"
|
|
14
|
+
_TRACE_POLICY_PARENT_BASED_TRACE_ID_RATIO = "parentbased_traceidratio"
|
|
15
|
+
_TRACE_POLICY_ALWAYS_OFF = "always_off"
|
|
16
|
+
_TRACE_POLICIES = frozenset(
|
|
17
|
+
{
|
|
18
|
+
_TRACE_POLICY_PARENT_BASED_ALWAYS_OFF,
|
|
19
|
+
_TRACE_POLICY_PARENT_BASED_TRACE_ID_RATIO,
|
|
20
|
+
_TRACE_POLICY_ALWAYS_OFF,
|
|
21
|
+
}
|
|
22
|
+
)
|
|
23
|
+
_missing_otel_modules = object()
|
|
24
|
+
_missing_always_off_tracer = object()
|
|
25
|
+
_otel_modules: tuple[Any, Any, Any] | object | None = None
|
|
26
|
+
_always_off_tracer: Any | object | None = None
|
|
27
|
+
_runtime_tracing_configured = False
|
|
28
|
+
_runtime_tracer_provider: Any | None = None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _raw_metadata(context_metadata: Any) -> Mapping[str, Any] | None:
|
|
32
|
+
if not isinstance(context_metadata, Mapping):
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
raw = context_metadata.get("metadata", context_metadata)
|
|
36
|
+
if isinstance(raw, Mapping):
|
|
37
|
+
return raw
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _collect_trace_metadata(
|
|
42
|
+
context_metadata: Any,
|
|
43
|
+
) -> tuple[Mapping[str, Any] | None, Mapping[str, Any] | None]:
|
|
44
|
+
if isinstance(context_metadata, Mapping):
|
|
45
|
+
first = _raw_metadata(context_metadata)
|
|
46
|
+
if first is not None and first.get("traceparent"):
|
|
47
|
+
return first, first
|
|
48
|
+
return first, None
|
|
49
|
+
|
|
50
|
+
first: Mapping[str, Any] | None = None
|
|
51
|
+
if isinstance(context_metadata, Sequence) and not isinstance(context_metadata, str | bytes):
|
|
52
|
+
for item in context_metadata:
|
|
53
|
+
metadata = _raw_metadata(item)
|
|
54
|
+
if metadata is None:
|
|
55
|
+
continue
|
|
56
|
+
|
|
57
|
+
if first is None:
|
|
58
|
+
first = metadata
|
|
59
|
+
if metadata.get("traceparent"):
|
|
60
|
+
return first, metadata
|
|
61
|
+
|
|
62
|
+
return first, None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _trace_policy() -> str | None:
|
|
66
|
+
raw_policy = os.environ.get(_REMOTE_FUNCTION_TRACE_POLICY_ENV_VAR)
|
|
67
|
+
if raw_policy is None:
|
|
68
|
+
return None
|
|
69
|
+
|
|
70
|
+
policy = raw_policy.strip().lower()
|
|
71
|
+
if policy in _TRACE_POLICIES:
|
|
72
|
+
return policy
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _trace_sample_rate() -> float | None:
|
|
77
|
+
raw_rate = os.environ.get(_REMOTE_FUNCTION_TRACE_SAMPLE_RATE_ENV_VAR)
|
|
78
|
+
if raw_rate is None:
|
|
79
|
+
return None
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
rate = float(raw_rate)
|
|
83
|
+
except ValueError:
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
if rate < 0.0 or rate > 1.0:
|
|
87
|
+
return None
|
|
88
|
+
return rate
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _otel_exporter_configured() -> bool:
|
|
92
|
+
return bool(os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT") or os.environ.get("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"))
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _configure_runtime_tracing(sample_rate: float) -> None:
|
|
96
|
+
global _runtime_tracing_configured, _runtime_tracer_provider
|
|
97
|
+
if _runtime_tracing_configured:
|
|
98
|
+
return
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
resources = importlib.import_module("opentelemetry.sdk.resources")
|
|
102
|
+
sdk_trace = importlib.import_module("opentelemetry.sdk.trace")
|
|
103
|
+
sdk_sampling = importlib.import_module("opentelemetry.sdk.trace.sampling")
|
|
104
|
+
trace_api = importlib.import_module("opentelemetry.trace")
|
|
105
|
+
except Exception:
|
|
106
|
+
_runtime_tracing_configured = True
|
|
107
|
+
return
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
provider = sdk_trace.TracerProvider(
|
|
111
|
+
resource=resources.Resource.create({"service.name": os.environ.get("CHALK_SERVICE") or _TRACER_NAME}),
|
|
112
|
+
sampler=sdk_sampling.ParentBased(sdk_sampling.TraceIdRatioBased(sample_rate)),
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
if _otel_exporter_configured():
|
|
116
|
+
try:
|
|
117
|
+
otlp_exporter = importlib.import_module("opentelemetry.exporter.otlp.proto.grpc.trace_exporter")
|
|
118
|
+
sdk_export = importlib.import_module("opentelemetry.sdk.trace.export")
|
|
119
|
+
provider.add_span_processor(sdk_export.BatchSpanProcessor(otlp_exporter.OTLPSpanExporter()))
|
|
120
|
+
except Exception:
|
|
121
|
+
pass
|
|
122
|
+
|
|
123
|
+
trace_api.set_tracer_provider(provider)
|
|
124
|
+
_runtime_tracer_provider = provider
|
|
125
|
+
except Exception:
|
|
126
|
+
pass
|
|
127
|
+
_runtime_tracing_configured = True
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _span_metadata(
|
|
131
|
+
first_metadata: Mapping[str, Any] | None,
|
|
132
|
+
traceparent_metadata: Mapping[str, Any] | None,
|
|
133
|
+
policy: str,
|
|
134
|
+
) -> tuple[Mapping[str, Any] | None, Any | None, Any]:
|
|
135
|
+
if traceparent_metadata is not None:
|
|
136
|
+
carrier = _trace_carrier(traceparent_metadata)
|
|
137
|
+
if not carrier:
|
|
138
|
+
return None, None, None
|
|
139
|
+
|
|
140
|
+
otel_modules = _get_otel_modules()
|
|
141
|
+
if otel_modules is None:
|
|
142
|
+
return None, None, None
|
|
143
|
+
_, propagate, trace = otel_modules
|
|
144
|
+
|
|
145
|
+
parent_context = propagate.extract(carrier)
|
|
146
|
+
parent_span_context = trace.get_current_span(parent_context).get_span_context()
|
|
147
|
+
if not parent_span_context.is_valid:
|
|
148
|
+
return None, None, None
|
|
149
|
+
return traceparent_metadata, parent_context, otel_modules
|
|
150
|
+
|
|
151
|
+
if policy != _TRACE_POLICY_PARENT_BASED_TRACE_ID_RATIO:
|
|
152
|
+
return None, None, None
|
|
153
|
+
|
|
154
|
+
sample_rate = _trace_sample_rate()
|
|
155
|
+
if sample_rate is None:
|
|
156
|
+
return None, None, None
|
|
157
|
+
|
|
158
|
+
_configure_runtime_tracing(sample_rate)
|
|
159
|
+
otel_modules = _get_otel_modules()
|
|
160
|
+
if otel_modules is None:
|
|
161
|
+
return None, None, None
|
|
162
|
+
return first_metadata, None, otel_modules
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _always_off_span_metadata(
|
|
166
|
+
first_metadata: Mapping[str, Any] | None,
|
|
167
|
+
traceparent_metadata: Mapping[str, Any] | None,
|
|
168
|
+
) -> tuple[Mapping[str, Any] | None, Any | None, Any | None, Any | None]:
|
|
169
|
+
otel_modules = _get_otel_modules()
|
|
170
|
+
if otel_modules is None:
|
|
171
|
+
return None, None, None, None
|
|
172
|
+
_, propagate, trace = otel_modules
|
|
173
|
+
|
|
174
|
+
parent_context = None
|
|
175
|
+
if traceparent_metadata is not None:
|
|
176
|
+
carrier = _trace_carrier(traceparent_metadata)
|
|
177
|
+
if carrier:
|
|
178
|
+
extracted_context = propagate.extract(carrier)
|
|
179
|
+
parent_span_context = trace.get_current_span(extracted_context).get_span_context()
|
|
180
|
+
if parent_span_context.is_valid:
|
|
181
|
+
parent_context = extracted_context
|
|
182
|
+
|
|
183
|
+
tracer = _get_always_off_tracer()
|
|
184
|
+
if tracer is None:
|
|
185
|
+
return None, None, None, None
|
|
186
|
+
return traceparent_metadata or first_metadata, parent_context, otel_modules, tracer
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _trace_carrier(metadata: Mapping[str, Any]) -> dict[str, str]:
|
|
190
|
+
return {
|
|
191
|
+
str(key).lower(): str(value)
|
|
192
|
+
for key, value in metadata.items()
|
|
193
|
+
if str(key).lower() in _TRACE_CONTEXT_METADATA_KEYS and value
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _function_name(context_metadata: Any) -> str:
|
|
198
|
+
if isinstance(context_metadata, Mapping):
|
|
199
|
+
return str(context_metadata.get("function_name") or "") or "unknown"
|
|
200
|
+
|
|
201
|
+
if isinstance(context_metadata, Sequence) and not isinstance(context_metadata, str | bytes):
|
|
202
|
+
for item in context_metadata:
|
|
203
|
+
if isinstance(item, Mapping):
|
|
204
|
+
function_name = str(item.get("function_name") or "")
|
|
205
|
+
if function_name:
|
|
206
|
+
return function_name
|
|
207
|
+
|
|
208
|
+
return "unknown"
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def _get_tracer(trace_api: Any) -> Any:
|
|
212
|
+
return trace_api.get_tracer(_TRACER_NAME)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _get_otel_modules() -> tuple[Any, Any, Any] | None:
|
|
216
|
+
global _otel_modules
|
|
217
|
+
if _otel_modules is None:
|
|
218
|
+
try:
|
|
219
|
+
_otel_modules = (
|
|
220
|
+
importlib.import_module("opentelemetry.context"),
|
|
221
|
+
importlib.import_module("opentelemetry.propagate"),
|
|
222
|
+
importlib.import_module("opentelemetry.trace"),
|
|
223
|
+
)
|
|
224
|
+
except ImportError:
|
|
225
|
+
_otel_modules = _missing_otel_modules
|
|
226
|
+
|
|
227
|
+
if _otel_modules is _missing_otel_modules:
|
|
228
|
+
return None
|
|
229
|
+
return cast(tuple[Any, Any, Any], _otel_modules)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _get_always_off_tracer() -> Any | None:
|
|
233
|
+
global _always_off_tracer
|
|
234
|
+
if _always_off_tracer is None:
|
|
235
|
+
try:
|
|
236
|
+
sdk_trace = importlib.import_module("opentelemetry.sdk.trace")
|
|
237
|
+
sdk_sampling = importlib.import_module("opentelemetry.sdk.trace.sampling")
|
|
238
|
+
except ImportError:
|
|
239
|
+
_always_off_tracer = _missing_always_off_tracer
|
|
240
|
+
else:
|
|
241
|
+
# Keep always-off local to this invocation instead of replacing the
|
|
242
|
+
# process-wide tracer provider.
|
|
243
|
+
provider = sdk_trace.TracerProvider(sampler=sdk_sampling.ALWAYS_OFF)
|
|
244
|
+
_always_off_tracer = provider.get_tracer(_TRACER_NAME)
|
|
245
|
+
|
|
246
|
+
if _always_off_tracer is _missing_always_off_tracer:
|
|
247
|
+
return None
|
|
248
|
+
return _always_off_tracer
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
@contextlib.contextmanager
|
|
252
|
+
def remote_function_invocation_span(
|
|
253
|
+
context_metadata: Any,
|
|
254
|
+
coalesced_count: int | None = None,
|
|
255
|
+
) -> Iterator[None]:
|
|
256
|
+
policy = _trace_policy()
|
|
257
|
+
if policy is None:
|
|
258
|
+
yield
|
|
259
|
+
return
|
|
260
|
+
|
|
261
|
+
first_metadata, traceparent_metadata = _collect_trace_metadata(context_metadata)
|
|
262
|
+
if policy == _TRACE_POLICY_PARENT_BASED_ALWAYS_OFF and traceparent_metadata is None:
|
|
263
|
+
yield
|
|
264
|
+
return
|
|
265
|
+
|
|
266
|
+
if policy == _TRACE_POLICY_ALWAYS_OFF:
|
|
267
|
+
_metadata, parent_context, otel_modules, tracer = _always_off_span_metadata(
|
|
268
|
+
first_metadata,
|
|
269
|
+
traceparent_metadata,
|
|
270
|
+
)
|
|
271
|
+
else:
|
|
272
|
+
_metadata, parent_context, otel_modules = _span_metadata(
|
|
273
|
+
first_metadata,
|
|
274
|
+
traceparent_metadata,
|
|
275
|
+
policy,
|
|
276
|
+
)
|
|
277
|
+
tracer = _get_tracer(otel_modules[2]) if otel_modules is not None else None
|
|
278
|
+
|
|
279
|
+
if otel_modules is None:
|
|
280
|
+
yield
|
|
281
|
+
return
|
|
282
|
+
if tracer is None:
|
|
283
|
+
yield
|
|
284
|
+
return
|
|
285
|
+
otel_context, _, trace = otel_modules
|
|
286
|
+
|
|
287
|
+
SpanKind = trace.SpanKind
|
|
288
|
+
Status = trace.Status
|
|
289
|
+
StatusCode = trace.StatusCode
|
|
290
|
+
function_name = _function_name(context_metadata)
|
|
291
|
+
span_kwargs: dict[str, Any] = {"kind": SpanKind.SERVER}
|
|
292
|
+
if parent_context is not None:
|
|
293
|
+
span_kwargs["context"] = parent_context
|
|
294
|
+
|
|
295
|
+
with tracer.start_as_current_span("chalkcompute.remote_function.invoke", **span_kwargs) as span:
|
|
296
|
+
span.set_attribute("chalk.remote_function.name", function_name)
|
|
297
|
+
if coalesced_count is not None:
|
|
298
|
+
span.set_attribute(
|
|
299
|
+
"chalk.remote_function.coalesced_count",
|
|
300
|
+
coalesced_count,
|
|
301
|
+
)
|
|
302
|
+
if parent_context is not None and not span.get_span_context().is_valid:
|
|
303
|
+
token = otel_context.attach(parent_context)
|
|
304
|
+
try:
|
|
305
|
+
yield
|
|
306
|
+
finally:
|
|
307
|
+
otel_context.detach(token)
|
|
308
|
+
return
|
|
309
|
+
try:
|
|
310
|
+
yield
|
|
311
|
+
except Exception as exc:
|
|
312
|
+
span.record_exception(exc)
|
|
313
|
+
span.set_status(Status(StatusCode.ERROR, str(exc)))
|
|
314
|
+
raise
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.5.1"
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import contextlib
|
|
4
|
-
import importlib
|
|
5
|
-
import os
|
|
6
|
-
from collections.abc import Callable, Iterator, Mapping, Sequence
|
|
7
|
-
from typing import Any, cast
|
|
8
|
-
|
|
9
|
-
_TRACER_NAME = "chalk_remote_call"
|
|
10
|
-
_TRACE_CONTEXT_METADATA_KEYS = ("traceparent", "tracestate", "baggage")
|
|
11
|
-
_REMOTE_FUNCTION_NAME_METADATA = "x-chalk-function-name"
|
|
12
|
-
_REMOTE_FUNCTION_TRACING_ENV_VAR = "CHALK_ENABLE_REMOTE_FUNCTION_TRACING"
|
|
13
|
-
_missing_sdk_tracer = object()
|
|
14
|
-
_missing_otel_modules = object()
|
|
15
|
-
_sdk_get_tracer: Callable[[], Any] | object | None = None
|
|
16
|
-
_otel_modules: tuple[Any, Any, Any] | object | None = None
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def _raw_metadata(context_metadata: Any) -> Mapping[str, Any] | None:
|
|
20
|
-
if not isinstance(context_metadata, Mapping):
|
|
21
|
-
return None
|
|
22
|
-
|
|
23
|
-
raw = context_metadata.get("metadata", context_metadata)
|
|
24
|
-
if isinstance(raw, Mapping):
|
|
25
|
-
return raw
|
|
26
|
-
return None
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def _first_context_with_traceparent(context_metadata: Any) -> Mapping[str, Any] | None:
|
|
30
|
-
if isinstance(context_metadata, Sequence) and not isinstance(context_metadata, str | bytes):
|
|
31
|
-
for item in context_metadata:
|
|
32
|
-
metadata = _raw_metadata(item)
|
|
33
|
-
if metadata is not None and metadata.get("traceparent"):
|
|
34
|
-
return metadata
|
|
35
|
-
return None
|
|
36
|
-
|
|
37
|
-
metadata = _raw_metadata(context_metadata)
|
|
38
|
-
if metadata is not None and metadata.get("traceparent"):
|
|
39
|
-
return metadata
|
|
40
|
-
return None
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def _trace_carrier(metadata: Mapping[str, Any]) -> dict[str, str]:
|
|
44
|
-
return {
|
|
45
|
-
str(key).lower(): str(value)
|
|
46
|
-
for key, value in metadata.items()
|
|
47
|
-
if str(key).lower() in _TRACE_CONTEXT_METADATA_KEYS and value
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def _function_name(metadata: Mapping[str, Any]) -> str:
|
|
52
|
-
return str(metadata.get(_REMOTE_FUNCTION_NAME_METADATA) or "") or "unknown"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def _get_tracer(trace_api: Any) -> Any:
|
|
56
|
-
global _sdk_get_tracer
|
|
57
|
-
if _sdk_get_tracer is None:
|
|
58
|
-
try:
|
|
59
|
-
sdk_tracing = importlib.import_module("chalkcompute._tracing")
|
|
60
|
-
except ImportError:
|
|
61
|
-
_sdk_get_tracer = _missing_sdk_tracer
|
|
62
|
-
else:
|
|
63
|
-
_sdk_get_tracer = sdk_tracing.get_tracer
|
|
64
|
-
|
|
65
|
-
sdk_get_tracer = _sdk_get_tracer
|
|
66
|
-
if callable(sdk_get_tracer):
|
|
67
|
-
try:
|
|
68
|
-
return sdk_get_tracer()
|
|
69
|
-
except ImportError:
|
|
70
|
-
return trace_api.get_tracer(_TRACER_NAME)
|
|
71
|
-
return trace_api.get_tracer(_TRACER_NAME)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def _get_otel_modules() -> tuple[Any, Any, Any] | None:
|
|
75
|
-
global _otel_modules
|
|
76
|
-
if _otel_modules is None:
|
|
77
|
-
try:
|
|
78
|
-
_otel_modules = (
|
|
79
|
-
importlib.import_module("opentelemetry.context"),
|
|
80
|
-
importlib.import_module("opentelemetry.propagate"),
|
|
81
|
-
importlib.import_module("opentelemetry.trace"),
|
|
82
|
-
)
|
|
83
|
-
except ImportError:
|
|
84
|
-
_otel_modules = _missing_otel_modules
|
|
85
|
-
|
|
86
|
-
if _otel_modules is _missing_otel_modules:
|
|
87
|
-
return None
|
|
88
|
-
return cast(tuple[Any, Any, Any], _otel_modules)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
@contextlib.contextmanager
|
|
92
|
-
def remote_function_invocation_span(
|
|
93
|
-
context_metadata: Any,
|
|
94
|
-
coalesced_count: int | None = None,
|
|
95
|
-
) -> Iterator[None]:
|
|
96
|
-
if os.environ.get(_REMOTE_FUNCTION_TRACING_ENV_VAR, "") != "1":
|
|
97
|
-
yield
|
|
98
|
-
return
|
|
99
|
-
|
|
100
|
-
metadata = _first_context_with_traceparent(context_metadata)
|
|
101
|
-
if metadata is None:
|
|
102
|
-
yield
|
|
103
|
-
return
|
|
104
|
-
|
|
105
|
-
carrier = _trace_carrier(metadata)
|
|
106
|
-
if not carrier:
|
|
107
|
-
yield
|
|
108
|
-
return
|
|
109
|
-
|
|
110
|
-
otel_modules = _get_otel_modules()
|
|
111
|
-
if otel_modules is None:
|
|
112
|
-
yield
|
|
113
|
-
return
|
|
114
|
-
otel_context, propagate, trace = otel_modules
|
|
115
|
-
|
|
116
|
-
parent_context = propagate.extract(carrier)
|
|
117
|
-
parent_span_context = trace.get_current_span(parent_context).get_span_context()
|
|
118
|
-
if not parent_span_context.is_valid:
|
|
119
|
-
yield
|
|
120
|
-
return
|
|
121
|
-
|
|
122
|
-
SpanKind = trace.SpanKind
|
|
123
|
-
Status = trace.Status
|
|
124
|
-
StatusCode = trace.StatusCode
|
|
125
|
-
function_name = _function_name(metadata)
|
|
126
|
-
tracer = _get_tracer(trace)
|
|
127
|
-
with tracer.start_as_current_span(
|
|
128
|
-
"chalkcompute.remote_function.invoke",
|
|
129
|
-
context=parent_context,
|
|
130
|
-
kind=SpanKind.SERVER,
|
|
131
|
-
) as span:
|
|
132
|
-
span.set_attribute("chalk.remote_function.name", function_name)
|
|
133
|
-
if coalesced_count is not None:
|
|
134
|
-
span.set_attribute(
|
|
135
|
-
"chalk.remote_function.coalesced_count",
|
|
136
|
-
coalesced_count,
|
|
137
|
-
)
|
|
138
|
-
if not span.get_span_context().is_valid:
|
|
139
|
-
token = otel_context.attach(parent_context)
|
|
140
|
-
try:
|
|
141
|
-
yield
|
|
142
|
-
finally:
|
|
143
|
-
otel_context.detach(token)
|
|
144
|
-
return
|
|
145
|
-
try:
|
|
146
|
-
yield
|
|
147
|
-
except Exception as exc:
|
|
148
|
-
span.record_exception(exc)
|
|
149
|
-
span.set_status(Status(StatusCode.ERROR, str(exc)))
|
|
150
|
-
raise
|
|
File without changes
|
|
File without changes
|
{chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk-remote-call-rs/Cargo.lock
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.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.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/__init__.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/__main__.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.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.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/_native.pyi
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.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.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/server.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.0}/chalk_remote_call/servicer.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
|