let python_obj = Python::attach(|py| self.inner.clone_ref(py));
let caller_ctx = Python::attach(|py| {
    py.import("contextvars")
        .and_then(|m| m.call_method0("copy_context"))
        .map(|c| c.unbind())
});
let cached_name = self.cached_name.clone();

{{ param_cloning }}

tokio::task::spawn_blocking(move || {
    Python::attach(|py| {
        let obj = python_obj.bind(py);
        let ctx = caller_ctx.map_err(|e| {{ error_expr }})?.into_bound(py);

        let bound_method = obj.getattr("{{ method_name }}").map_err(|e| {{ error_expr }})?;
        let py_result = ctx.call_method1("run", ({{ run_args }}))
            .map_err(|e| {{ error_expr }})?;
{%- if native_return_binding %}
        // Native fast-path: the host returned the binding's result object. Extract it and convert
        // via `From<{{ native_return_binding }}>` for the core type, skipping the JSON round-trip. Fall
        // through to the mapping path below when the host returned a plain dict/mapping instead.
        if let Ok(native) = py_result.extract::<{{ native_return_binding }}>() {
            return Ok(<{{ return_type }}>::from(native));
        }
{%- endif %}
        let json_val: String = py
            .import("json")
            .and_then(|m| m.call_method1("dumps", (py_result,)))
            .and_then(|v| v.extract())
            .map_err(|e| {{ json_error_expr }})?;
        serde_json::from_str::<{{ return_type }}>(&json_val).map_err(|e| {{ deserialize_error_expr }})
    })
})
.await
.map_err(|e| {{ spawn_error_expr }})
.flatten()
