diff --git a/engine/Cargo.lock b/engine/Cargo.lock index 72ad264f2..b094dc9eb 100644 --- a/engine/Cargo.lock +++ b/engine/Cargo.lock @@ -901,8 +901,8 @@ dependencies = [ "internal-baml-codegen", "log", "pyo3", - "pyo3-asyncio", - "pyo3-build-config", + "pyo3-async-runtimes", + "pyo3-build-config 0.21.2", "pythonize", "regex", "serde", @@ -3743,17 +3743,17 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.21.2" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e00b96a521718e08e03b1a622f01c8a8deb50719335de3f60b3b3950f069d8" +checksum = "e484fd2c8b4cb67ab05a318f1fd6fa8f199fcc30819f08f07d200809dba26c15" dependencies = [ "cfg-if", "indoc", "libc", "memoffset", - "parking_lot", + "once_cell", "portable-atomic", - "pyo3-build-config", + "pyo3-build-config 0.23.3", "pyo3-ffi", "pyo3-macros", "serde", @@ -3761,22 +3761,45 @@ dependencies = [ ] [[package]] -name = "pyo3-asyncio" -version = "0.21.0" -source = "git+https://github.com/BoundaryML/pyo3-asyncio.git?branch=migration-pyo3-0.21#d1ec64076dd1b5c797db4b7b811f588466956d20" +name = "pyo3-async-runtimes" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "977dc837525cfd22919ba6a831413854beb7c99a256c03bf8624ad707e45810e" dependencies = [ "futures", "once_cell", "pin-project-lite", "pyo3", + "pyo3-async-runtimes-macros", "tokio", ] +[[package]] +name = "pyo3-async-runtimes-macros" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2df2884957d2476731f987673befac5d521dff10abb0a7cbe12015bc7702fe9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "pyo3-build-config" version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7883df5835fafdad87c0d888b266c8ec0f4c9ca48a5bed6bbb592e8dedee1b50" +dependencies = [ + "once_cell", + "target-lexicon", +] + +[[package]] +name = "pyo3-build-config" +version = "0.23.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0e0469a84f208e20044b98965e1561028180219e35352a2afaf2b942beff3b" dependencies = [ "once_cell", "python3-dll-a", @@ -3785,19 +3808,19 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.21.2" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01be5843dc60b916ab4dad1dca6d20b9b4e6ddc8e15f50c47fe6d85f1fb97403" +checksum = "eb1547a7f9966f6f1a0f0227564a9945fe36b90da5a93b3933fc3dc03fae372d" dependencies = [ "libc", - "pyo3-build-config", + "pyo3-build-config 0.23.3", ] [[package]] name = "pyo3-macros" -version = "0.21.2" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77b34069fc0682e11b31dbd10321cbf94808394c56fd996796ce45217dfac53c" +checksum = "fdb6da8ec6fa5cedd1626c886fc8749bdcbb09424a86461eb8cdf096b7c33257" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -3807,31 +3830,31 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.21.2" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08260721f32db5e1a5beae69a55553f56b99bd0e1c3e6e0a5e8851a9d0f5a85c" +checksum = "38a385202ff5a92791168b1136afae5059d3ac118457bb7bc304c197c2d33e7d" dependencies = [ - "heck 0.4.1", + "heck 0.5.0", "proc-macro2", - "pyo3-build-config", + "pyo3-build-config 0.23.3", "quote", "syn 2.0.87", ] [[package]] name = "python3-dll-a" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0b78171a90d808b319acfad166c4790d9e9759bbc14ac8273fe133673dd41b" +checksum = "9b9e268ee1be609e93a13eb06839f68f67e5fe0fb4049834d261c2d5091c1b6d" dependencies = [ "cc", ] [[package]] name = "pythonize" -version = "0.21.1" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0664248812c38cc55a4ed07f88e4df516ce82604b93b1ffdc041aa77a6cb3c" +checksum = "91a6ee7a084f913f98d70cdc3ebec07e852b735ae3059a1500db2661265da9ff" dependencies = [ "pyo3", "serde", diff --git a/engine/language_client_python/Cargo.toml b/engine/language_client_python/Cargo.toml index 4c0ab7c76..47f417678 100644 --- a/engine/language_client_python/Cargo.toml +++ b/engine/language_client_python/Cargo.toml @@ -28,20 +28,18 @@ env_logger.workspace = true futures.workspace = true indexmap.workspace = true log.workspace = true -pyo3 = { version = "0.21.2", default-features = false, features = [ +# Consult https://pyo3.rs/main/migration for migration instructions +pyo3 = { version = "0.23.3", default-features = false, features = [ "abi3-py38", "extension-module", "generate-import-lib", "serde", ] } -# pyo3-asyncio head is still on 0.20.0; someone's done the work of updating it to 0.21 and Bound<>, -# but that work hasn't been merged yet. it builds though, and looks good to me! -# https://github.com/awestlake87/pyo3-asyncio/pull/121 -pyo3-asyncio = { git = "https://github.com/BoundaryML/pyo3-asyncio.git", branch = "migration-pyo3-0.21", features = [ +pyo3-async-runtimes = { version = "0.23", features = [ + "attributes", "tokio-runtime", ] } -#pyo3-asyncio = { version = "0.20.0", features = ["tokio-runtime"] } -pythonize = "0.21.1" +pythonize = "0.23" regex.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/engine/language_client_python/src/errors.rs b/engine/language_client_python/src/errors.rs index 3912da6b0..f169c8a19 100644 --- a/engine/language_client_python/src/errors.rs +++ b/engine/language_client_python/src/errors.rs @@ -1,7 +1,7 @@ use baml_runtime::{ errors::ExposedError, internal::llm_client::LLMResponse, scope_diagnostics::ScopeStack, }; -use pyo3::types::{PyAnyMethods, PyModule}; +use pyo3::types::{PyAnyMethods, PyModule, PyModuleMethods}; use pyo3::{create_exception, pymodule, Bound, PyErr, PyResult, Python}; create_exception!(baml_py, BamlError, pyo3::exceptions::PyException); @@ -17,19 +17,26 @@ create_exception!(baml_py, BamlClientHttpError, BamlClientError); #[allow(non_snake_case)] fn raise_baml_validation_error(prompt: String, message: String, raw_output: String) -> PyErr { Python::with_gil(|py| { - let internal_monkeypatch = py.import_bound("baml_py.internal_monkeypatch").unwrap(); + let internal_monkeypatch = py.import("baml_py.internal_monkeypatch").unwrap(); let exception = internal_monkeypatch.getattr("BamlValidationError").unwrap(); let args = (prompt, message, raw_output); let inst = exception.call1(args).unwrap(); - PyErr::from_value_bound(inst) + PyErr::from_value(inst) }) } #[allow(non_snake_case)] -fn raise_baml_client_finish_reason_error(prompt: String, raw_output: String, message: String, finish_reason: Option) -> PyErr { +fn raise_baml_client_finish_reason_error( + prompt: String, + raw_output: String, + message: String, + finish_reason: Option, +) -> PyErr { Python::with_gil(|py| { let internal_monkeypatch = py.import("baml_py.internal_monkeypatch").unwrap(); - let exception = internal_monkeypatch.getattr("BamlClientFinishReasonError").unwrap(); + let exception = internal_monkeypatch + .getattr("BamlClientFinishReasonError") + .unwrap(); let args = (prompt, message, raw_output, finish_reason); let inst = exception.call1(args).unwrap(); PyErr::from_value(inst) @@ -40,23 +47,18 @@ fn raise_baml_client_finish_reason_error(prompt: String, raw_output: String, mes /// IIRC the name of this function is the name of the module that pyo3 generates (errors.py) #[pymodule] pub fn errors(parent_module: &Bound<'_, PyModule>) -> PyResult<()> { - parent_module.add( - "BamlError", - parent_module.py().get_type_bound::(), - )?; + parent_module.add("BamlError", parent_module.py().get_type::())?; parent_module.add( "BamlInvalidArgumentError", - parent_module - .py() - .get_type_bound::(), + parent_module.py().get_type::(), )?; parent_module.add( "BamlClientError", - parent_module.py().get_type_bound::(), + parent_module.py().get_type::(), )?; parent_module.add( "BamlClientHttpError", - parent_module.py().get_type_bound::(), + parent_module.py().get_type::(), )?; Ok(()) @@ -80,9 +82,12 @@ impl BamlError { raw_output, message, finish_reason, - } => { - raise_baml_client_finish_reason_error(prompt.clone(), raw_output.clone(), message.clone(), finish_reason.clone()) - } + } => raise_baml_client_finish_reason_error( + prompt.clone(), + raw_output.clone(), + message.clone(), + finish_reason.clone(), + ), } } else if let Some(er) = err.downcast_ref::() { PyErr::new::(format!("Invalid argument: {}", er)) diff --git a/engine/language_client_python/src/lib.rs b/engine/language_client_python/src/lib.rs index 1818fa7dc..73de3ceaf 100644 --- a/engine/language_client_python/src/lib.rs +++ b/engine/language_client_python/src/lib.rs @@ -4,13 +4,14 @@ mod runtime; mod types; use pyo3::prelude::{pyfunction, pymodule, PyAnyMethods, PyModule, PyResult}; +use pyo3::types::PyModuleMethods; use pyo3::{wrap_pyfunction, Bound, Python}; use tracing_subscriber::{self, EnvFilter}; #[pyfunction] fn invoke_runtime_cli(py: Python) -> PyResult<()> { baml_cli::run_cli( - py.import_bound("sys")? + py.import("sys")? .getattr("argv")? .extract::>()?, baml_runtime::RuntimeCliDefaults { diff --git a/engine/language_client_python/src/parse_py_type.rs b/engine/language_client_python/src/parse_py_type.rs index a520c0b36..5177499a0 100644 --- a/engine/language_client_python/src/parse_py_type.rs +++ b/engine/language_client_python/src/parse_py_type.rs @@ -6,7 +6,7 @@ use pyo3::{ exceptions::{PyRuntimeError, PyTypeError}, prelude::{PyAnyMethods, PyTypeMethods}, types::{PyBool, PyBoolMethods, PyDict, PyDictMethods, PyList}, - PyErr, PyObject, PyResult, Python, ToPyObject, + IntoPyObjectExt, PyErr, PyObject, PyResult, Python, }; use crate::types::{BamlAudioPy, BamlImagePy}; @@ -209,10 +209,8 @@ pub fn parse_py_type( serialize_unknown_types_as_str: bool, ) -> PyResult> { Python::with_gil(|py| { - let enum_type = py.import_bound("enum").and_then(|m| m.getattr("Enum"))?; - let base_model = py - .import_bound("pydantic") - .and_then(|m| m.getattr("BaseModel"))?; + let enum_type = py.import("enum").and_then(|m| m.getattr("Enum"))?; + let base_model = py.import("pydantic").and_then(|m| m.getattr("BaseModel"))?; let mut get_type = |py: Python<'_>, any: PyObject, @@ -227,10 +225,10 @@ pub fn parse_py_type( let name = t .name() .map(|n| { - if let Some(x) = n.rfind("baml_client.types.") { - n[x + "baml_client.types.".len()..].to_string() - } else { - n.to_string() + let n = n.to_string(); + match n.strip_prefix("baml_client.types.") { + Some(s) => s.to_string(), + None => n, } }) .unwrap_or("".to_string()); @@ -241,10 +239,10 @@ pub fn parse_py_type( let name = t .name() .map(|n| { - if let Some(x) = n.rfind("baml_client.types.") { - n[x + "baml_client.types.".len()..].to_string() - } else { - n.to_string() + let n = n.to_string(); + match n.strip_prefix("baml_client.types.") { + Some(s) => s.to_string(), + None => n, } }) .unwrap_or("".to_string()); @@ -256,7 +254,7 @@ pub fn parse_py_type( { for (key, _) in model_fields { if let Ok(value) = any.getattr(py, key.as_str()) { - fields.insert(key, value.to_object(py)); + fields.insert(key, value.into_py_any(py)?); } } } @@ -266,7 +264,7 @@ pub fn parse_py_type( if let Ok(extra_dict) = extra.downcast_bound::(py) { for (key, value) in extra_dict.iter() { if let (Ok(key), value) = (key.extract::(), value) { - fields.insert(key, value.to_object(py)); + fields.insert(key, value.into_py_any(py)?); } } } @@ -276,7 +274,7 @@ pub fn parse_py_type( // log::info!("Fields of {}:", name); // for (key, value) in &fields { // let repr = py - // .import_bound("builtins")? + // .import("builtins")? // .getattr("repr")? // .call1((value,))?; // let repr_str = repr.extract::()?; @@ -288,7 +286,7 @@ pub fn parse_py_type( let mut items = vec![]; let len = list.len()?; for idx in 0..len { - items.push(list.get_item(idx)?.to_object(py)); + items.push(list.get_item(idx)?.into_py_any(py)?); } Ok(MappedPyType::List(items)) } else if let Ok(kv) = any.extract::>(py) { diff --git a/engine/language_client_python/src/runtime.rs b/engine/language_client_python/src/runtime.rs index c5f725a51..fe08f44d5 100644 --- a/engine/language_client_python/src/runtime.rs +++ b/engine/language_client_python/src/runtime.rs @@ -10,9 +10,10 @@ use crate::types::ClientRegistry; use baml_runtime::runtime_interface::ExperimentalTracingInterface; use baml_runtime::BamlRuntime as CoreBamlRuntime; use pyo3::prelude::{pymethods, PyResult}; -use pyo3::{pyclass, PyObject, Python, ToPyObject}; +use pyo3::{pyclass, IntoPyObjectExt, PyObject, Python}; use std::collections::HashMap; use std::path::PathBuf; +use std::sync::Arc; crate::lang_wrapper!(BamlRuntime, CoreBamlRuntime, clone_safe); @@ -123,7 +124,7 @@ impl BamlRuntime { tb: Option<&TypeBuilder>, cb: Option<&ClientRegistry>, ) -> PyResult { - let Some(args) = parse_py_type(args.into_bound(py).to_object(py), false)? else { + let Some(args) = parse_py_type(args.into_bound(py).into_py_any(py)?, false)? else { return Err(BamlInvalidArgumentError::new_err( "Failed to parse args, perhaps you used a non-serializable type?", )); @@ -140,7 +141,7 @@ impl BamlRuntime { let tb = tb.map(|tb| tb.inner.clone()); let cb = cb.map(|cb| cb.inner.clone()); - pyo3_asyncio::tokio::future_into_py(py, async move { + pyo3_async_runtimes::tokio::future_into_py(py, async move { let ctx_mng = ctx_mng; let (result, _) = baml_runtime .call_function(function_name, &args_map, &ctx_mng, tb.as_ref(), cb.as_ref()) @@ -202,7 +203,7 @@ impl BamlRuntime { tb: Option<&TypeBuilder>, cb: Option<&ClientRegistry>, ) -> PyResult { - let Some(args) = parse_py_type(args.into_bound(py).to_object(py), false)? else { + let Some(args) = parse_py_type(args.into_bound(py).into_py_any(py)?, false)? else { return Err(BamlInvalidArgumentError::new_err( "Failed to parse args, perhaps you used a non-serializable type?", )); @@ -243,7 +244,7 @@ impl BamlRuntime { tb: Option<&TypeBuilder>, cb: Option<&ClientRegistry>, ) -> PyResult { - let Some(args) = parse_py_type(args.into_bound(py).to_object(py), false)? else { + let Some(args) = parse_py_type(args.into_bound(py).into_py_any(py)?, false)? else { return Err(BamlInvalidArgumentError::new_err( "Failed to parse args, perhaps you used a non-serializable type?", )); @@ -283,17 +284,17 @@ impl BamlRuntime { self.inner.drain_stats().into() } - #[pyo3()] - fn set_log_event_callback(&self, callback: Option) -> PyResult<()> { - let callback = callback.clone(); + #[pyo3(signature = (callback = None))] + fn set_log_event_callback(&self, callback: Option, py: Python<'_>) -> PyResult<()> { let baml_runtime = self.inner.clone(); if let Some(callback) = callback { + let arc_callback = Arc::new(callback.into_py_any(py)?); baml_runtime .as_ref() .set_log_event_callback(Some(Box::new(move |log_event| { Python::with_gil(|py| { - match callback.call1( + match arc_callback.call1( py, (BamlLogEvent { metadata: LogEventMetadata { diff --git a/engine/language_client_python/src/types/audio.rs b/engine/language_client_python/src/types/audio.rs index 2ce520f6c..c23e550d0 100644 --- a/engine/language_client_python/src/types/audio.rs +++ b/engine/language_client_python/src/types/audio.rs @@ -2,7 +2,7 @@ use baml_types::BamlMediaContent; use pyo3::prelude::{pymethods, PyResult}; use pyo3::types::{PyTuple, PyType}; use pyo3::{Bound, PyAny, PyObject, Python}; -use pythonize::{depythonize_bound, pythonize}; +use pythonize::{depythonize, pythonize}; use crate::errors::BamlError; @@ -54,14 +54,14 @@ impl BamlAudioPy { /// /// Used for `pickle.load`: https://docs.python.org/3/library/pickle.html#object.__getnewargs__ #[new] - pub fn py_new(data: PyObject, py: Python<'_>) -> PyResult { - Self::baml_deserialize(data, py) + pub fn py_new(data: Bound<'_, PyAny>) -> PyResult { + Self::baml_deserialize(data) } /// Used for `pickle.dump`: https://docs.python.org/3/library/pickle.html#object.__getnewargs__ pub fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult> { let o = self.baml_serialize(py)?; - Ok(PyTuple::new_bound(py, vec![o])) + PyTuple::new(py, vec![o]) } pub fn __repr__(&self) -> String { @@ -90,9 +90,9 @@ impl BamlAudioPy { } #[staticmethod] - fn baml_deserialize(data: PyObject, py: Python<'_>) -> PyResult { - let data: UserFacingBamlMedia = depythonize_bound(data.into_bound(py))?; - Ok(BamlAudioPy { + fn baml_deserialize(data: Bound<'_, PyAny>) -> PyResult { + let data: UserFacingBamlMedia = depythonize(&data)?; + Ok(Self { inner: data.into_baml_media(baml_types::BamlMediaType::Audio), }) } @@ -100,7 +100,7 @@ impl BamlAudioPy { pub fn baml_serialize(&self, py: Python<'_>) -> PyResult { let s: UserFacingBamlMedia = (&self.inner).try_into().map_err(BamlError::from_anyhow)?; let s = serde_json::to_value(&s).map_err(|e| BamlError::from_anyhow(e.into()))?; - Ok(pythonize(py, &s)?) + Ok(pythonize(py, &s)?.into()) } pub fn __eq__(&self, other: &Self) -> bool { diff --git a/engine/language_client_python/src/types/client_registry.rs b/engine/language_client_python/src/types/client_registry.rs index 7437c13bd..86334c970 100644 --- a/engine/language_client_python/src/types/client_registry.rs +++ b/engine/language_client_python/src/types/client_registry.rs @@ -2,7 +2,7 @@ use std::str::FromStr; use baml_runtime::client_registry; use pyo3::prelude::{pymethods, PyResult}; -use pyo3::{PyObject, Python, ToPyObject}; +use pyo3::{IntoPyObjectExt, PyObject, Python}; use crate::errors::BamlInvalidArgumentError; use crate::parse_py_type::parse_py_type; @@ -34,7 +34,7 @@ impl ClientRegistry { options: PyObject, retry_policy: Option, ) -> PyResult<()> { - let Some(args) = parse_py_type(options.into_bound(py).to_object(py), false)? else { + let Some(args) = parse_py_type(options.into_bound(py).into_py_any(py)?, false)? else { return Err(BamlInvalidArgumentError::new_err( "Failed to parse args, perhaps you used a non-serializable type?", )); diff --git a/engine/language_client_python/src/types/function_result_stream.rs b/engine/language_client_python/src/types/function_result_stream.rs index dfba380a7..bb8ff24e3 100644 --- a/engine/language_client_python/src/types/function_result_stream.rs +++ b/engine/language_client_python/src/types/function_result_stream.rs @@ -90,7 +90,7 @@ impl FunctionResultStream { let ctx_mng = ctx.inner.clone(); let tb = self.tb.clone(); let cb = self.cb.clone(); - pyo3_asyncio::tokio::future_into_py(py, async move { + pyo3_async_runtimes::tokio::future_into_py(py, async move { let ctx_mng = ctx_mng; let mut locked = inner.lock().await; let (res, _) = locked diff --git a/engine/language_client_python/src/types/function_results.rs b/engine/language_client_python/src/types/function_results.rs index a08e713d5..0a52cb831 100644 --- a/engine/language_client_python/src/types/function_results.rs +++ b/engine/language_client_python/src/types/function_results.rs @@ -1,7 +1,7 @@ use baml_types::{BamlValueWithMeta, ResponseCheck}; use pyo3::prelude::{pymethods, PyResult}; use pyo3::types::{PyAnyMethods, PyDict, PyModule, PyTuple, PyType}; -use pyo3::{Bound, IntoPy, PyAny, PyObject, Python}; +use pyo3::{Bound, IntoPyObject, IntoPyObjectExt, PyAny, PyObject, Python}; use crate::errors::BamlError; @@ -49,8 +49,9 @@ fn pythonize_checks<'a>( types_module: &Bound<'_, PyModule>, checks: &[ResponseCheck], ) -> PyResult> { - let dict = PyDict::new_bound(py); - let check_class: &PyType = types_module.getattr("Check")?.extract()?; + let dict = PyDict::new(py); + let check_class = types_module.getattr("Check")?; + let check_class = check_class.downcast::()?; checks.iter().try_for_each( |ResponseCheck { name, @@ -58,7 +59,7 @@ fn pythonize_checks<'a>( status, }| { // Construct the Check. - let check_properties_dict = pyo3::types::PyDict::new_bound(py); + let check_properties_dict = pyo3::types::PyDict::new(py); check_properties_dict.set_item("name", name)?; check_properties_dict.set_item("expression", expression)?; check_properties_dict.set_item("status", status)?; @@ -79,32 +80,32 @@ fn pythonize_strict( ) -> PyResult { let meta = parsed.meta().clone(); let py_value_without_constraints = match parsed { - BamlValueWithMeta::String(val, _) => PyResult::Ok(val.into_py(py)), - BamlValueWithMeta::Int(val, _) => Ok(val.into_py(py)), - BamlValueWithMeta::Float(val, _) => Ok(val.into_py(py)), - BamlValueWithMeta::Bool(val, _) => Ok(val.into_py(py)), + BamlValueWithMeta::String(val, _) => val.into_py_any(py), + BamlValueWithMeta::Int(val, _) => val.into_py_any(py), + BamlValueWithMeta::Float(val, _) => val.into_py_any(py), + BamlValueWithMeta::Bool(val, _) => val.into_py_any(py), BamlValueWithMeta::Map(index_map, _) => { - let dict = pyo3::types::PyDict::new_bound(py); + let dict = pyo3::types::PyDict::new(py); for (key, value) in index_map { - let key = key.into_py(py); + let key = key.into_pyobject(py)?; let value = pythonize_strict(py, value, enum_module, cls_module)?; dict.set_item(key, value)?; } Ok(dict.into()) } - BamlValueWithMeta::List(vec, _) => Ok(pyo3::types::PyList::new_bound( + BamlValueWithMeta::List(vec, _) => pyo3::types::PyList::new( py, vec.into_iter() .map(|v| pythonize_strict(py, v, enum_module, cls_module)) .collect::>>()?, - ) - .into()), + )? + .into_py_any(py), BamlValueWithMeta::Media(baml_media, _) => match baml_media.media_type { baml_types::BamlMediaType::Image => { - Ok(BamlImagePy::from(baml_media.clone()).into_py(py)) + BamlImagePy::from(baml_media.clone()).into_py_any(py) } baml_types::BamlMediaType::Audio => { - Ok(BamlAudioPy::from(baml_media.clone()).into_py(py)) + BamlAudioPy::from(baml_media.clone()).into_py_any(py) } }, BamlValueWithMeta::Enum(enum_name, ref value, _) => { @@ -115,7 +116,7 @@ fn pythonize_strict( tb = TypeBuilder() tb.add_enum("Foo") */ - Err(_) => return Ok(value.into_py(py)), + Err(_) => return value.into_py_any(py), }; // Call the constructor with the value @@ -128,7 +129,7 @@ fn pythonize_strict( @@dynamic } */ - return Ok(value.into_py(py)); + return value.into_py_any(py); } }; Ok(instance.into()) @@ -142,7 +143,7 @@ fn pythonize_strict( }) .collect::>>()?; - let properties_dict = pyo3::types::PyDict::new_bound(py); + let properties_dict = pyo3::types::PyDict::new(py); for (key, value) in properties { // For each field, try to call pydantic's `model_dump` on the // field. This is necessary in case the field is `Checked[_,_]`, @@ -196,26 +197,25 @@ fn pythonize_strict( let value_type = py_value_without_constraints.bind(py).get_type(); // Import the necessary modules and objects - let typing = py.import_bound("typing")?; + let typing = py.import("typing")?; let literal = typing.getattr("Literal")?; // Collect check names as &str and turn them into a Python tuple let check_names: Vec<&str> = meta.iter().map(|check| check.name.as_str()).collect(); - let literal_args = PyTuple::new_bound(py, check_names); + let literal_args = PyTuple::new(py, check_names)?; // Call Literal[...] dynamically let literal_check_names = literal.get_item(literal_args)?; // Prepare the properties dictionary - let properties_dict = pyo3::types::PyDict::new_bound(py); + let properties_dict = pyo3::types::PyDict::new(py); properties_dict.set_item("value", py_value_without_constraints)?; properties_dict.set_item("checks", python_checks)?; let class_checked_type_constructor = cls_module.getattr("Checked")?; // Prepare type parameters for Checked[...] - let type_parameters_tuple = - PyTuple::new_bound(py, [value_type.as_ref(), &literal_check_names]); + let type_parameters_tuple = PyTuple::new(py, [value_type.as_ref(), &literal_check_names])?; // Create the Checked type using __class_getitem__ let class_checked_type: Bound<'_, PyAny> = class_checked_type_constructor diff --git a/engine/language_client_python/src/types/image.rs b/engine/language_client_python/src/types/image.rs index 01dbae941..754691004 100644 --- a/engine/language_client_python/src/types/image.rs +++ b/engine/language_client_python/src/types/image.rs @@ -1,7 +1,7 @@ use pyo3::prelude::{pymethods, PyResult}; use pyo3::types::{PyTuple, PyType}; use pyo3::{Bound, PyAny, PyObject, Python}; -use pythonize::{depythonize_bound, pythonize}; +use pythonize::{depythonize, pythonize}; use crate::errors::{BamlError, BamlInvalidArgumentError}; @@ -53,14 +53,14 @@ impl BamlImagePy { /// /// Used for `pickle.load`: https://docs.python.org/3/library/pickle.html#object.__getnewargs__ #[new] - pub fn py_new(data: PyObject, py: Python<'_>) -> PyResult { - Self::baml_deserialize(data, py) + pub fn py_new(data: Bound<'_, PyAny>) -> PyResult { + Self::baml_deserialize(data) } /// Used for `pickle.dump`: https://docs.python.org/3/library/pickle.html#object.__getnewargs__ pub fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult> { let o = self.baml_serialize(py)?; - Ok(PyTuple::new_bound(py, vec![o])) + PyTuple::new(py, vec![o]) } pub fn __repr__(&self) -> String { @@ -89,9 +89,9 @@ impl BamlImagePy { } #[staticmethod] - fn baml_deserialize(data: PyObject, py: Python<'_>) -> PyResult { - let data: UserFacingBamlMedia = depythonize_bound(data.into_bound(py))?; - Ok(BamlImagePy { + fn baml_deserialize(data: Bound<'_, PyAny>) -> PyResult { + let data: UserFacingBamlMedia = depythonize(&data)?; + Ok(Self { inner: data.into_baml_media(baml_types::BamlMediaType::Image), }) } @@ -99,7 +99,7 @@ impl BamlImagePy { pub fn baml_serialize(&self, py: Python<'_>) -> PyResult { let s: UserFacingBamlMedia = (&self.inner).try_into().map_err(BamlError::from_anyhow)?; let s = serde_json::to_value(&s).map_err(|e| BamlError::from_anyhow(e.into()))?; - Ok(pythonize(py, &s)?) + Ok(pythonize(py, &s)?.into()) } pub fn __eq__(&self, other: &Self) -> bool { diff --git a/engine/language_client_python/src/types/media_repr.rs b/engine/language_client_python/src/types/media_repr.rs index 5e07688ff..f70d6aa71 100644 --- a/engine/language_client_python/src/types/media_repr.rs +++ b/engine/language_client_python/src/types/media_repr.rs @@ -1,8 +1,11 @@ +use std::ffi::CString; + use anyhow::Result; use baml_types::{BamlMedia, BamlMediaContent, BamlMediaType, MediaBase64, MediaUrl}; use pyo3::{ + ffi::c_str, types::{PyAnyMethods, PyModule, PyType}, - Bound, Py, PyAny, PyObject, PyResult, Python, ToPyObject, + Bound, IntoPyObjectExt, PyAny, PyObject, PyResult, Python, }; use serde::{Deserialize, Serialize}; @@ -82,7 +85,8 @@ pub fn __get_pydantic_core_schema__( _handler: Bound<'_, PyAny>, ) -> PyResult { Python::with_gil(|py| { - let code = r#" + let code = c_str!( + r#" from pydantic_core import core_schema, SchemaValidator def deserialize(data): @@ -130,11 +134,15 @@ def get_schema(): ) ret = get_schema() - "#; - // py.run(code, None, Some(ret_dict)); - let fun: Py = PyModule::from_code_bound(py, code, file!(), crate::MODULE_NAME)? - .getattr("ret")? - .into(); - Ok(fun.to_object(py)) + "# + ); + PyModule::from_code( + py, + code, + c_str!(file!()), + CString::new(crate::MODULE_NAME).unwrap().as_c_str(), + )? + .getattr("ret")? + .into_py_any(py) }) } diff --git a/engine/language_client_python/src/types/runtime_ctx_manager.rs b/engine/language_client_python/src/types/runtime_ctx_manager.rs index 271e52815..ffa967242 100644 --- a/engine/language_client_python/src/types/runtime_ctx_manager.rs +++ b/engine/language_client_python/src/types/runtime_ctx_manager.rs @@ -1,5 +1,5 @@ use pyo3::prelude::{pymethods, PyResult}; -use pyo3::{PyObject, Python, ToPyObject}; +use pyo3::{IntoPyObjectExt, PyObject, Python}; use crate::errors::BamlError; use crate::parse_py_type::parse_py_type; @@ -10,7 +10,7 @@ crate::lang_wrapper!(RuntimeContextManager, baml_runtime::RuntimeContextManager) impl RuntimeContextManager { #[pyo3()] fn upsert_tags(&self, py: Python<'_>, tags: PyObject) -> PyResult { - let Some(tags) = parse_py_type(tags.into_bound(py).to_object(py), true)? else { + let Some(tags) = parse_py_type(tags.into_bound(py).into_py_any(py)?, true)? else { // No tags to process return Ok(true); }; diff --git a/engine/language_client_python/src/types/span.rs b/engine/language_client_python/src/types/span.rs index 792189b4b..db40d2e25 100644 --- a/engine/language_client_python/src/types/span.rs +++ b/engine/language_client_python/src/types/span.rs @@ -1,7 +1,7 @@ use baml_runtime::runtime_interface::ExperimentalTracingInterface; use baml_types::BamlValue; use pyo3::prelude::{pymethods, PyResult}; -use pyo3::{PyObject, Python, ToPyObject}; +use pyo3::{IntoPyObjectExt, PyObject, Python}; use crate::errors::{BamlError, BamlInvalidArgumentError}; use crate::parse_py_type::parse_py_type; @@ -25,7 +25,7 @@ impl BamlSpan { args: PyObject, ctx: &RuntimeContextManager, ) -> PyResult { - let args = parse_py_type(args.into_bound(py).to_object(py), true)? + let args = parse_py_type(args.into_bound(py).into_py_any(py)?, true)? .unwrap_or(BamlValue::Map(Default::default())); let Some(args_map) = args.as_map() else { return Err(BamlInvalidArgumentError::new_err("Failed to parse args")); @@ -50,7 +50,7 @@ impl BamlSpan { ctx: &RuntimeContextManager, ) -> PyResult> { log::trace!("Finishing span: {:?}", self.inner); - let result = parse_py_type(result.into_bound(py).to_object(py), true)?; + let result = parse_py_type(result.into_bound(py).into_py_any(py)?, true)?; let span = self .inner diff --git a/engine/language_client_python/src/types/type_builder.rs b/engine/language_client_python/src/types/type_builder.rs index efed39d28..4e9d698a3 100644 --- a/engine/language_client_python/src/types/type_builder.rs +++ b/engine/language_client_python/src/types/type_builder.rs @@ -128,6 +128,7 @@ impl EnumBuilder { self.inner.lock().unwrap().value(name).into() } + #[pyo3(signature = (alias = None))] pub fn alias(&self, alias: Option<&str>) -> Self { self.inner.lock().unwrap().with_meta( "alias", @@ -145,6 +146,7 @@ impl EnumBuilder { #[pymethods] impl EnumValueBuilder { + #[pyo3(signature = (alias = None))] pub fn alias(&self, alias: Option<&str>) -> Self { self.inner.lock().unwrap().with_meta( "alias", @@ -164,6 +166,7 @@ impl EnumValueBuilder { self.inner.clone().into() } + #[pyo3(signature = (description = None))] pub fn description(&self, description: Option<&str>) -> Self { self.inner.lock().unwrap().with_meta( "description", @@ -196,6 +199,7 @@ impl ClassPropertyBuilder { self.inner.clone().into() } + #[pyo3(signature = (alias = None))] pub fn alias(&self, alias: Option<&str>) -> Self { self.inner.lock().unwrap().with_meta( "alias", @@ -206,6 +210,7 @@ impl ClassPropertyBuilder { self.inner.clone().into() } + #[pyo3(signature = (description = None))] pub fn description(&self, description: Option<&str>) -> Self { self.inner.lock().unwrap().with_meta( "description", diff --git a/engine/language_client_ruby/ext/ruby_ffi/src/types/type_builder.rs b/engine/language_client_ruby/ext/ruby_ffi/src/types/type_builder.rs index 17bb07e43..1bbd8a429 100644 --- a/engine/language_client_ruby/ext/ruby_ffi/src/types/type_builder.rs +++ b/engine/language_client_ruby/ext/ruby_ffi/src/types/type_builder.rs @@ -200,7 +200,6 @@ impl EnumValueBuilder { self.inner.clone().into() } - // #[pyo3(signature = (skip = true))] pub fn skip(&self, skip: Option) -> Self { self.inner .lock()