summaryrefslogtreecommitdiffstats
path: root/third_party/rust/uniffi_bindgen/src/bindings/python
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/uniffi_bindgen/src/bindings/python')
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/compounds.rs16
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/executor.rs18
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/external.rs2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/mod.rs177
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Async.py86
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/BooleanHelper.py18
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/BytesHelper.py5
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceImpl.py98
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceRuntime.py59
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceTemplate.py112
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/CustomType.py9
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/DurationHelper.py8
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py71
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py19
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py8
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float32Helper.py2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float64Helper.py2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/ForeignExecutorTemplate.py63
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/HandleMap.py33
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Helpers.py36
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int16Helper.py2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int32Helper.py2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int64Helper.py2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int8Helper.py2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/MapTemplate.py6
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py45
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py124
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/OptionalTemplate.py5
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/PointerManager.py68
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Protocol.py9
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py24
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferHelper.py18
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferTemplate.py11
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/SequenceTemplate.py5
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/StringHelper.py4
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/TimestampHelper.py4
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py24
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Types.py5
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt16Helper.py2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt32Helper.py2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt64Helper.py2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt8Helper.py2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/macros.py95
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/wrapper.py17
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/test.rs14
45 files changed, 850 insertions, 486 deletions
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/compounds.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/compounds.rs
index b91bcbe18f..16adeca9a5 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/compounds.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/compounds.rs
@@ -3,7 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use super::CodeType;
-use crate::backend::{Literal, Type};
+use crate::{
+ backend::{Literal, Type},
+ bindings::python::gen_python::AsCodeType,
+};
#[derive(Debug)]
pub struct OptionalCodeType {
@@ -33,8 +36,9 @@ impl CodeType for OptionalCodeType {
fn literal(&self, literal: &Literal) -> String {
match literal {
- Literal::Null => "None".into(),
- _ => super::PythonCodeOracle.find(&self.inner).literal(literal),
+ Literal::None => "None".into(),
+ Literal::Some { inner } => super::PythonCodeOracle.find(&self.inner).literal(inner),
+ _ => panic!("Invalid literal for Optional type: {literal:?}"),
}
}
}
@@ -88,7 +92,11 @@ impl MapCodeType {
impl CodeType for MapCodeType {
fn type_label(&self) -> String {
- "dict".to_string()
+ format!(
+ "dict[{}, {}]",
+ self.key.as_codetype().type_label(),
+ self.value.as_codetype().type_label()
+ )
}
fn canonical_name(&self) -> String {
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/executor.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/executor.rs
deleted file mode 100644
index be3ba1d791..0000000000
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/executor.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-use super::CodeType;
-
-#[derive(Debug)]
-pub struct ForeignExecutorCodeType;
-
-impl CodeType for ForeignExecutorCodeType {
- fn type_label(&self) -> String {
- "asyncio.BaseEventLoop".into()
- }
-
- fn canonical_name(&self) -> String {
- "ForeignExecutor".into()
- }
-}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/external.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/external.rs
index 0d19c4bb3c..0a46251d6d 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/external.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/external.rs
@@ -17,7 +17,7 @@ impl ExternalCodeType {
impl CodeType for ExternalCodeType {
fn type_label(&self) -> String {
- self.name.clone()
+ super::PythonCodeOracle.class_name(&self.name)
}
fn canonical_name(&self) -> String {
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/mod.rs
index 8178fcc102..6a10a38e7f 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/mod.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/mod.rs
@@ -2,8 +2,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use anyhow::{Context, Result};
+use anyhow::{bail, Context, Result};
use askama::Template;
+use camino::Utf8Path;
use heck::{ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase};
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
@@ -13,20 +14,43 @@ use std::collections::{BTreeSet, HashMap, HashSet};
use std::fmt::Debug;
use crate::backend::TemplateExpression;
+use crate::bindings::python;
use crate::interface::*;
-use crate::BindingsConfig;
+use crate::{BindingGenerator, BindingsConfig};
mod callback_interface;
mod compounds;
mod custom;
mod enum_;
-mod executor;
mod external;
mod miscellany;
mod object;
mod primitives;
mod record;
+pub struct PythonBindingGenerator;
+
+impl BindingGenerator for PythonBindingGenerator {
+ type Config = Config;
+
+ fn write_bindings(
+ &self,
+ ci: &ComponentInterface,
+ config: &Config,
+ out_dir: &Utf8Path,
+ try_format_code: bool,
+ ) -> Result<()> {
+ python::write_bindings(config, ci, out_dir, try_format_code)
+ }
+
+ fn check_library_path(&self, library_path: &Utf8Path, cdylib_name: Option<&str>) -> Result<()> {
+ if cdylib_name.is_none() {
+ bail!("Generate bindings for Python requires a cdylib, but {library_path} was given");
+ }
+ Ok(())
+ }
+}
+
/// A trait tor the implementation.
trait CodeType: Debug {
/// The language specific label used to reference this type. This will be used in
@@ -114,6 +138,8 @@ pub struct Config {
cdylib_name: Option<String>,
#[serde(default)]
custom_types: HashMap<String, CustomTypeConfig>,
+ #[serde(default)]
+ external_packages: HashMap<String, String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
@@ -133,6 +159,16 @@ impl Config {
"uniffi".into()
}
}
+
+ /// Get the package name for a given external namespace.
+ pub fn module_for_namespace(&self, ns: &str) -> String {
+ let ns = ns.to_string().to_snake_case();
+ match self.external_packages.get(&ns) {
+ None => format!(".{ns}"),
+ Some(value) if value.is_empty() => ns,
+ Some(value) => format!("{value}.{ns}"),
+ }
+ }
}
impl BindingsConfig for Config {
@@ -326,7 +362,19 @@ impl PythonCodeOracle {
fixup_keyword(nm.to_string().to_shouty_snake_case())
}
- fn ffi_type_label(ffi_type: &FfiType) -> String {
+ /// Get the idiomatic Python rendering of an FFI callback function name
+ fn ffi_callback_name(&self, nm: &str) -> String {
+ format!("UNIFFI_{}", nm.to_shouty_snake_case())
+ }
+
+ /// Get the idiomatic Python rendering of an FFI struct name
+ fn ffi_struct_name(&self, nm: &str) -> String {
+ // The ctypes docs use both SHOUTY_SNAKE_CASE AND UpperCamelCase for structs. Let's use
+ // UpperCamelCase and reserve shouting for global variables
+ format!("Uniffi{}", nm.to_upper_camel_case())
+ }
+
+ fn ffi_type_label(&self, ffi_type: &FfiType) -> String {
match ffi_type {
FfiType::Int8 => "ctypes.c_int8".to_string(),
FfiType::UInt8 => "ctypes.c_uint8".to_string(),
@@ -338,19 +386,64 @@ impl PythonCodeOracle {
FfiType::UInt64 => "ctypes.c_uint64".to_string(),
FfiType::Float32 => "ctypes.c_float".to_string(),
FfiType::Float64 => "ctypes.c_double".to_string(),
+ FfiType::Handle => "ctypes.c_uint64".to_string(),
FfiType::RustArcPtr(_) => "ctypes.c_void_p".to_string(),
FfiType::RustBuffer(maybe_suffix) => match maybe_suffix {
Some(suffix) => format!("_UniffiRustBuffer{suffix}"),
None => "_UniffiRustBuffer".to_string(),
},
+ FfiType::RustCallStatus => "_UniffiRustCallStatus".to_string(),
FfiType::ForeignBytes => "_UniffiForeignBytes".to_string(),
- FfiType::ForeignCallback => "_UNIFFI_FOREIGN_CALLBACK_T".to_string(),
+ FfiType::Callback(name) => self.ffi_callback_name(name),
+ FfiType::Struct(name) => self.ffi_struct_name(name),
// Pointer to an `asyncio.EventLoop` instance
- FfiType::ForeignExecutorHandle => "ctypes.c_size_t".to_string(),
- FfiType::ForeignExecutorCallback => "_UNIFFI_FOREIGN_EXECUTOR_CALLBACK_T".to_string(),
- FfiType::RustFutureHandle => "ctypes.c_void_p".to_string(),
- FfiType::RustFutureContinuationCallback => "_UNIFFI_FUTURE_CONTINUATION_T".to_string(),
- FfiType::RustFutureContinuationData => "ctypes.c_size_t".to_string(),
+ FfiType::Reference(inner) => format!("ctypes.POINTER({})", self.ffi_type_label(inner)),
+ FfiType::VoidPointer => "ctypes.c_void_p".to_string(),
+ }
+ }
+
+ /// Default values for FFI types
+ ///
+ /// Used to set a default return value when returning an error
+ fn ffi_default_value(&self, return_type: Option<&FfiType>) -> String {
+ match return_type {
+ Some(t) => match t {
+ FfiType::UInt8
+ | FfiType::Int8
+ | FfiType::UInt16
+ | FfiType::Int16
+ | FfiType::UInt32
+ | FfiType::Int32
+ | FfiType::UInt64
+ | FfiType::Int64 => "0".to_owned(),
+ FfiType::Float32 | FfiType::Float64 => "0.0".to_owned(),
+ FfiType::RustArcPtr(_) => "ctypes.c_void_p()".to_owned(),
+ FfiType::RustBuffer(maybe_suffix) => match maybe_suffix {
+ Some(suffix) => format!("_UniffiRustBuffer{suffix}.default()"),
+ None => "_UniffiRustBuffer.default()".to_owned(),
+ },
+ _ => unimplemented!("FFI return type: {t:?}"),
+ },
+ // When we need to use a value for void returns, we use a `u8` placeholder
+ None => "0".to_owned(),
+ }
+ }
+
+ /// Get the name of the protocol and class name for an object.
+ ///
+ /// If we support callback interfaces, the protocol name is the object name, and the class name is derived from that.
+ /// Otherwise, the class name is the object name and the protocol name is derived from that.
+ ///
+ /// This split determines what types `FfiConverter.lower()` inputs. If we support callback
+ /// interfaces, `lower` must lower anything that implements the protocol. If not, then lower
+ /// only lowers the concrete class.
+ fn object_names(&self, obj: &Object) -> (String, String) {
+ let class_name = self.class_name(obj.name());
+ if obj.has_callback_interface() {
+ let impl_name = format!("{class_name}Impl");
+ (class_name, impl_name)
+ } else {
+ (format!("{class_name}Protocol"), class_name)
}
}
}
@@ -392,7 +485,6 @@ impl<T: AsType> AsCodeType for T {
Type::CallbackInterface { name, .. } => {
Box::new(callback_interface::CallbackInterfaceCodeType::new(name))
}
- Type::ForeignExecutor => Box::new(executor::ForeignExecutorCodeType),
Type::Optional { inner_type } => {
Box::new(compounds::OptionalCodeType::new(*inner_type))
}
@@ -429,6 +521,10 @@ pub mod filters {
Ok(format!("{}.lift", ffi_converter_name(as_ct)?))
}
+ pub(super) fn check_lower_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(format!("{}.check_lower", ffi_converter_name(as_ct)?))
+ }
+
pub(super) fn lower_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
Ok(format!("{}.lower", ffi_converter_name(as_ct)?))
}
@@ -448,8 +544,18 @@ pub mod filters {
Ok(as_ct.as_codetype().literal(literal))
}
+ // Get the idiomatic Python rendering of an individual enum variant's discriminant
+ pub fn variant_discr_literal(e: &Enum, index: &usize) -> Result<String, askama::Error> {
+ let literal = e.variant_discr(*index).expect("invalid index");
+ Ok(Type::UInt64.as_codetype().literal(&literal))
+ }
+
pub fn ffi_type_name(type_: &FfiType) -> Result<String, askama::Error> {
- Ok(PythonCodeOracle::ffi_type_label(type_))
+ Ok(PythonCodeOracle.ffi_type_label(type_))
+ }
+
+ pub fn ffi_default_value(return_type: Option<FfiType>) -> Result<String, askama::Error> {
+ Ok(PythonCodeOracle.ffi_default_value(return_type.as_ref()))
}
/// Get the idiomatic Python rendering of a class name (for enums, records, errors, etc).
@@ -471,4 +577,51 @@ pub mod filters {
pub fn enum_variant_py(nm: &str) -> Result<String, askama::Error> {
Ok(PythonCodeOracle.enum_variant_name(nm))
}
+
+ /// Get the idiomatic Python rendering of an FFI callback function name
+ pub fn ffi_callback_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(PythonCodeOracle.ffi_callback_name(nm))
+ }
+
+ /// Get the idiomatic Python rendering of an FFI struct name
+ pub fn ffi_struct_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(PythonCodeOracle.ffi_struct_name(nm))
+ }
+
+ /// Get the idiomatic Python rendering of an individual enum variant.
+ pub fn object_names(obj: &Object) -> Result<(String, String), askama::Error> {
+ Ok(PythonCodeOracle.object_names(obj))
+ }
+
+ /// Get the idiomatic Python rendering of docstring
+ pub fn docstring(docstring: &str, spaces: &i32) -> Result<String, askama::Error> {
+ let docstring = textwrap::dedent(docstring);
+ // Escape triple quotes to avoid syntax error
+ let escaped = docstring.replace(r#"""""#, r#"\"\"\""#);
+
+ let wrapped = format!("\"\"\"\n{escaped}\n\"\"\"");
+
+ let spaces = usize::try_from(*spaces).unwrap_or_default();
+ Ok(textwrap::indent(&wrapped, &" ".repeat(spaces)))
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn test_docstring_escape() {
+ let docstring = r#""""This is a docstring beginning with triple quotes.
+Contains "quotes" in it.
+It also has a triple quote: """
+And a even longer quote: """"""#;
+
+ let expected = r#""""
+\"\"\"This is a docstring beginning with triple quotes.
+Contains "quotes" in it.
+It also has a triple quote: \"\"\"
+And a even longer quote: \"\"\"""
+""""#;
+
+ assert_eq!(super::filters::docstring(docstring, &0).unwrap(), expected);
+ }
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Async.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Async.py
index 82aa534b46..26daa9ba5c 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Async.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Async.py
@@ -3,13 +3,37 @@ _UNIFFI_RUST_FUTURE_POLL_READY = 0
_UNIFFI_RUST_FUTURE_POLL_MAYBE_READY = 1
# Stores futures for _uniffi_continuation_callback
-_UniffiContinuationPointerManager = _UniffiPointerManager()
+_UniffiContinuationHandleMap = _UniffiHandleMap()
+
+UNIFFI_GLOBAL_EVENT_LOOP = None
+
+"""
+Set the event loop to use for async functions
+
+This is needed if some async functions run outside of the eventloop, for example:
+ - A non-eventloop thread is spawned, maybe from `EventLoop.run_in_executor` or maybe from the
+ Rust code spawning its own thread.
+ - The Rust code calls an async callback method from a sync callback function, using something
+ like `pollster` to block on the async call.
+
+In this case, we need an event loop to run the Python async function, but there's no eventloop set
+for the thread. Use `uniffi_set_event_loop` to force an eventloop to be used in this case.
+"""
+def uniffi_set_event_loop(eventloop: asyncio.BaseEventLoop):
+ global UNIFFI_GLOBAL_EVENT_LOOP
+ UNIFFI_GLOBAL_EVENT_LOOP = eventloop
+
+def _uniffi_get_event_loop():
+ if UNIFFI_GLOBAL_EVENT_LOOP is not None:
+ return UNIFFI_GLOBAL_EVENT_LOOP
+ else:
+ return asyncio.get_running_loop()
# Continuation callback for async functions
# lift the return value or error and resolve the future, causing the async function to resume.
-@_UNIFFI_FUTURE_CONTINUATION_T
+@UNIFFI_RUST_FUTURE_CONTINUATION_CALLBACK
def _uniffi_continuation_callback(future_ptr, poll_code):
- (eventloop, future) = _UniffiContinuationPointerManager.release_pointer(future_ptr)
+ (eventloop, future) = _UniffiContinuationHandleMap.remove(future_ptr)
eventloop.call_soon_threadsafe(_uniffi_set_future_result, future, poll_code)
def _uniffi_set_future_result(future, poll_code):
@@ -18,14 +42,15 @@ def _uniffi_set_future_result(future, poll_code):
async def _uniffi_rust_call_async(rust_future, ffi_poll, ffi_complete, ffi_free, lift_func, error_ffi_converter):
try:
- eventloop = asyncio.get_running_loop()
+ eventloop = _uniffi_get_event_loop()
# Loop and poll until we see a _UNIFFI_RUST_FUTURE_POLL_READY value
while True:
future = eventloop.create_future()
ffi_poll(
rust_future,
- _UniffiContinuationPointerManager.new_pointer((eventloop, future)),
+ _uniffi_continuation_callback,
+ _UniffiContinuationHandleMap.insert((eventloop, future)),
)
poll_code = await future
if poll_code == _UNIFFI_RUST_FUTURE_POLL_READY:
@@ -37,4 +62,53 @@ async def _uniffi_rust_call_async(rust_future, ffi_poll, ffi_complete, ffi_free,
finally:
ffi_free(rust_future)
-_UniffiLib.{{ ci.ffi_rust_future_continuation_callback_set().name() }}(_uniffi_continuation_callback)
+{%- if ci.has_async_callback_interface_definition() %}
+def uniffi_trait_interface_call_async(make_call, handle_success, handle_error):
+ async def make_call_and_call_callback():
+ try:
+ handle_success(await make_call())
+ except Exception as e:
+ print("UniFFI: Unhandled exception in trait interface call", file=sys.stderr)
+ traceback.print_exc(file=sys.stderr)
+ handle_error(
+ _UniffiRustCallStatus.CALL_UNEXPECTED_ERROR,
+ {{ Type::String.borrow()|lower_fn }}(repr(e)),
+ )
+ eventloop = _uniffi_get_event_loop()
+ task = asyncio.run_coroutine_threadsafe(make_call_and_call_callback(), eventloop)
+ handle = UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.insert((eventloop, task))
+ return UniffiForeignFuture(handle, uniffi_foreign_future_free)
+
+def uniffi_trait_interface_call_async_with_error(make_call, handle_success, handle_error, error_type, lower_error):
+ async def make_call_and_call_callback():
+ try:
+ try:
+ handle_success(await make_call())
+ except error_type as e:
+ handle_error(
+ _UniffiRustCallStatus.CALL_ERROR,
+ lower_error(e),
+ )
+ except Exception as e:
+ print("UniFFI: Unhandled exception in trait interface call", file=sys.stderr)
+ traceback.print_exc(file=sys.stderr)
+ handle_error(
+ _UniffiRustCallStatus.CALL_UNEXPECTED_ERROR,
+ {{ Type::String.borrow()|lower_fn }}(repr(e)),
+ )
+ eventloop = _uniffi_get_event_loop()
+ task = asyncio.run_coroutine_threadsafe(make_call_and_call_callback(), eventloop)
+ handle = UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.insert((eventloop, task))
+ return UniffiForeignFuture(handle, uniffi_foreign_future_free)
+
+UNIFFI_FOREIGN_FUTURE_HANDLE_MAP = _UniffiHandleMap()
+
+@UNIFFI_FOREIGN_FUTURE_FREE
+def uniffi_foreign_future_free(handle):
+ (eventloop, task) = UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.remove(handle)
+ eventloop.call_soon(uniffi_foreign_future_do_free, task)
+
+def uniffi_foreign_future_do_free(task):
+ if not task.done():
+ task.cancel()
+{%- endif %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/BooleanHelper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/BooleanHelper.py
index 6775e9e132..3f8c5d1d4d 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/BooleanHelper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/BooleanHelper.py
@@ -1,16 +1,20 @@
-class _UniffiConverterBool(_UniffiConverterPrimitive):
+class _UniffiConverterBool:
@classmethod
- def check(cls, value):
+ def check_lower(cls, value):
return not not value
@classmethod
+ def lower(cls, value):
+ return 1 if value else 0
+
+ @staticmethod
+ def lift(value):
+ return value != 0
+
+ @classmethod
def read(cls, buf):
return cls.lift(buf.read_u8())
@classmethod
- def write_unchecked(cls, value, buf):
+ def write(cls, value, buf):
buf.write_u8(value)
-
- @staticmethod
- def lift(value):
- return value != 0
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/BytesHelper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/BytesHelper.py
index 196b5b29fa..4d09531322 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/BytesHelper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/BytesHelper.py
@@ -7,10 +7,13 @@ class _UniffiConverterBytes(_UniffiConverterRustBuffer):
return buf.read(size)
@staticmethod
- def write(value, buf):
+ def check_lower(value):
try:
memoryview(value)
except TypeError:
raise TypeError("a bytes-like object is required, not {!r}".format(type(value).__name__))
+
+ @staticmethod
+ def write(value, buf):
buf.write_i32(len(value))
buf.write(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceImpl.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceImpl.py
new file mode 100644
index 0000000000..676f01177a
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceImpl.py
@@ -0,0 +1,98 @@
+{% if self.include_once_check("CallbackInterfaceRuntime.py") %}{% include "CallbackInterfaceRuntime.py" %}{% endif %}
+{%- let trait_impl=format!("UniffiTraitImpl{}", name) %}
+
+# Put all the bits inside a class to keep the top-level namespace clean
+class {{ trait_impl }}:
+ # For each method, generate a callback function to pass to Rust
+ {%- for (ffi_callback, meth) in vtable_methods.iter() %}
+
+ @{{ ffi_callback.name()|ffi_callback_name }}
+ def {{ meth.name()|fn_name }}(
+ {%- for arg in ffi_callback.arguments() %}
+ {{ arg.name()|var_name }},
+ {%- endfor -%}
+ {%- if ffi_callback.has_rust_call_status_arg() %}
+ uniffi_call_status_ptr,
+ {%- endif %}
+ ):
+ uniffi_obj = {{ ffi_converter_name }}._handle_map.get(uniffi_handle)
+ def make_call():
+ args = ({% for arg in meth.arguments() %}{{ arg|lift_fn }}({{ arg.name()|var_name }}), {% endfor %})
+ method = uniffi_obj.{{ meth.name()|fn_name }}
+ return method(*args)
+
+ {% if !meth.is_async() %}
+ {%- match meth.return_type() %}
+ {%- when Some(return_type) %}
+ def write_return_value(v):
+ uniffi_out_return[0] = {{ return_type|lower_fn }}(v)
+ {%- when None %}
+ write_return_value = lambda v: None
+ {%- endmatch %}
+
+ {%- match meth.throws_type() %}
+ {%- when None %}
+ _uniffi_trait_interface_call(
+ uniffi_call_status_ptr.contents,
+ make_call,
+ write_return_value,
+ )
+ {%- when Some(error) %}
+ _uniffi_trait_interface_call_with_error(
+ uniffi_call_status_ptr.contents,
+ make_call,
+ write_return_value,
+ {{ error|type_name }},
+ {{ error|lower_fn }},
+ )
+ {%- endmatch %}
+ {%- else %}
+ def handle_success(return_value):
+ uniffi_future_callback(
+ uniffi_callback_data,
+ {{ meth.foreign_future_ffi_result_struct().name()|ffi_struct_name }}(
+ {%- match meth.return_type() %}
+ {%- when Some(return_type) %}
+ {{ return_type|lower_fn }}(return_value),
+ {%- when None %}
+ {%- endmatch %}
+ _UniffiRustCallStatus.default()
+ )
+ )
+
+ def handle_error(status_code, rust_buffer):
+ uniffi_future_callback(
+ uniffi_callback_data,
+ {{ meth.foreign_future_ffi_result_struct().name()|ffi_struct_name }}(
+ {%- match meth.return_type() %}
+ {%- when Some(return_type) %}
+ {{ meth.return_type().map(FfiType::from)|ffi_default_value }},
+ {%- when None %}
+ {%- endmatch %}
+ _UniffiRustCallStatus(status_code, rust_buffer),
+ )
+ )
+
+ {%- match meth.throws_type() %}
+ {%- when None %}
+ uniffi_out_return[0] = uniffi_trait_interface_call_async(make_call, handle_success, handle_error)
+ {%- when Some(error) %}
+ uniffi_out_return[0] = uniffi_trait_interface_call_async_with_error(make_call, handle_success, handle_error, {{ error|type_name }}, {{ error|lower_fn }})
+ {%- endmatch %}
+ {%- endif %}
+ {%- endfor %}
+
+ @{{ "CallbackInterfaceFree"|ffi_callback_name }}
+ def uniffi_free(uniffi_handle):
+ {{ ffi_converter_name }}._handle_map.remove(uniffi_handle)
+
+ # Generate the FFI VTable. This has a field for each callback interface method.
+ uniffi_vtable = {{ vtable|ffi_type_name }}(
+ {%- for (_, meth) in vtable_methods.iter() %}
+ {{ meth.name()|fn_name }},
+ {%- endfor %}
+ uniffi_free
+ )
+ # Send Rust a pointer to the VTable. Note: this means we need to keep the struct alive forever,
+ # or else bad things will happen when Rust tries to access it.
+ _UniffiLib.{{ ffi_init_callback.name() }}(ctypes.byref(uniffi_vtable))
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceRuntime.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceRuntime.py
index 0fe2ab8dc0..d802c90fde 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceRuntime.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceRuntime.py
@@ -1,42 +1,3 @@
-import threading
-
-class ConcurrentHandleMap:
- """
- A map where inserting, getting and removing data is synchronized with a lock.
- """
-
- def __init__(self):
- # type Handle = int
- self._left_map = {} # type: Dict[Handle, Any]
- self._right_map = {} # type: Dict[Any, Handle]
-
- self._lock = threading.Lock()
- self._current_handle = 0
- self._stride = 1
-
-
- def insert(self, obj):
- with self._lock:
- if obj in self._right_map:
- return self._right_map[obj]
- else:
- handle = self._current_handle
- self._current_handle += self._stride
- self._left_map[handle] = obj
- self._right_map[obj] = handle
- return handle
-
- def get(self, handle):
- with self._lock:
- return self._left_map.get(handle)
-
- def remove(self, handle):
- with self._lock:
- if handle in self._left_map:
- obj = self._left_map.pop(handle)
- del self._right_map[obj]
- return obj
-
# Magic number for the Rust proxy to call using the same mechanism as every other method,
# to free the callback once it's dropped by Rust.
IDX_CALLBACK_FREE = 0
@@ -45,22 +6,12 @@ _UNIFFI_CALLBACK_SUCCESS = 0
_UNIFFI_CALLBACK_ERROR = 1
_UNIFFI_CALLBACK_UNEXPECTED_ERROR = 2
-class _UniffiConverterCallbackInterface:
- _handle_map = ConcurrentHandleMap()
-
- def __init__(self, cb):
- self._foreign_callback = cb
-
- def drop(self, handle):
- self.__class__._handle_map.remove(handle)
+class UniffiCallbackInterfaceFfiConverter:
+ _handle_map = _UniffiHandleMap()
@classmethod
def lift(cls, handle):
- obj = cls._handle_map.get(handle)
- if not obj:
- raise InternalError("The object in the handle map has been dropped already")
-
- return obj
+ return cls._handle_map.get(handle)
@classmethod
def read(cls, buf):
@@ -68,6 +19,10 @@ class _UniffiConverterCallbackInterface:
cls.lift(handle)
@classmethod
+ def check_lower(cls, cb):
+ pass
+
+ @classmethod
def lower(cls, cb):
handle = cls._handle_map.insert(cb)
return handle
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceTemplate.py
index e0e926757a..a41e58e635 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceTemplate.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceTemplate.py
@@ -1,105 +1,13 @@
-{%- let cbi = ci|get_callback_interface_definition(id) %}
-{%- let foreign_callback = format!("foreignCallback{}", canonical_type_name) %}
+{%- let cbi = ci|get_callback_interface_definition(name) %}
+{%- let ffi_init_callback = cbi.ffi_init_callback() %}
+{%- let protocol_name = type_name.clone() %}
+{%- let protocol_docstring = cbi.docstring() %}
+{%- let vtable = cbi.vtable() %}
+{%- let methods = cbi.methods() %}
+{%- let vtable_methods = cbi.vtable_methods() %}
-{% if self.include_once_check("CallbackInterfaceRuntime.py") %}{% include "CallbackInterfaceRuntime.py" %}{% endif %}
-
-# Declaration and _UniffiConverters for {{ type_name }} Callback Interface
-
-class {{ type_name }}:
- {% for meth in cbi.methods() -%}
- def {{ meth.name()|fn_name }}(self, {% call py::arg_list_decl(meth) %}):
- raise NotImplementedError
-
- {% endfor %}
-
-def py_{{ foreign_callback }}(handle, method, args_data, args_len, buf_ptr):
- {% for meth in cbi.methods() -%}
- {% let method_name = format!("invoke_{}", meth.name())|fn_name %}
- def {{ method_name }}(python_callback, args_stream, buf_ptr):
- {#- Unpacking args from the _UniffiRustBuffer #}
- def makeCall():
- {#- Calling the concrete callback object #}
- {%- if meth.arguments().len() != 0 -%}
- return python_callback.{{ meth.name()|fn_name }}(
- {% for arg in meth.arguments() -%}
- {{ arg|read_fn }}(args_stream)
- {%- if !loop.last %}, {% endif %}
- {% endfor -%}
- )
- {%- else %}
- return python_callback.{{ meth.name()|fn_name }}()
- {%- endif %}
-
- def makeCallAndHandleReturn():
- {%- match meth.return_type() %}
- {%- when Some(return_type) %}
- rval = makeCall()
- with _UniffiRustBuffer.alloc_with_builder() as builder:
- {{ return_type|write_fn }}(rval, builder)
- buf_ptr[0] = builder.finalize()
- {%- when None %}
- makeCall()
- {%- endmatch %}
- return _UNIFFI_CALLBACK_SUCCESS
-
- {%- match meth.throws_type() %}
- {%- when None %}
- return makeCallAndHandleReturn()
- {%- when Some(err) %}
- try:
- return makeCallAndHandleReturn()
- except {{ err|type_name }} as e:
- # Catch errors declared in the UDL file
- with _UniffiRustBuffer.alloc_with_builder() as builder:
- {{ err|write_fn }}(e, builder)
- buf_ptr[0] = builder.finalize()
- return _UNIFFI_CALLBACK_ERROR
- {%- endmatch %}
-
- {% endfor %}
-
- cb = {{ ffi_converter_name }}.lift(handle)
- if not cb:
- raise InternalError("No callback in handlemap; this is a uniffi bug")
-
- if method == IDX_CALLBACK_FREE:
- {{ ffi_converter_name }}.drop(handle)
- # Successfull return
- # See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs`
- return _UNIFFI_CALLBACK_SUCCESS
-
- {% for meth in cbi.methods() -%}
- {% let method_name = format!("invoke_{}", meth.name())|fn_name -%}
- if method == {{ loop.index }}:
- # Call the method and handle any errors
- # See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs` for details
- try:
- return {{ method_name }}(cb, _UniffiRustBufferStream(args_data, args_len), buf_ptr)
- except BaseException as e:
- # Catch unexpected errors
- try:
- # Try to serialize the exception into a String
- buf_ptr[0] = {{ Type::String.borrow()|lower_fn }}(repr(e))
- except:
- # If that fails, just give up
- pass
- return _UNIFFI_CALLBACK_UNEXPECTED_ERROR
- {% endfor %}
-
- # This should never happen, because an out of bounds method index won't
- # ever be used. Once we can catch errors, we should return an InternalException.
- # https://github.com/mozilla/uniffi-rs/issues/351
-
- # An unexpected error happened.
- # See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs`
- return _UNIFFI_CALLBACK_UNEXPECTED_ERROR
-
-# We need to keep this function reference alive:
-# if they get GC'd while in use then UniFFI internals could attempt to call a function
-# that is in freed memory.
-# That would be...uh...bad. Yeah, that's the word. Bad.
-{{ foreign_callback }} = _UNIFFI_FOREIGN_CALLBACK_T(py_{{ foreign_callback }})
-_rust_call(lambda err: _UniffiLib.{{ cbi.ffi_init_callback().name() }}({{ foreign_callback }}, err))
+{% include "Protocol.py" %}
+{% include "CallbackInterfaceImpl.py" %}
# The _UniffiConverter which transforms the Callbacks in to Handles to pass to Rust.
-{{ ffi_converter_name }} = _UniffiConverterCallbackInterface({{ foreign_callback }})
+{{ ffi_converter_name }} = UniffiCallbackInterfaceFfiConverter()
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CustomType.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CustomType.py
index 5be6155b84..f75a85dc27 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CustomType.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CustomType.py
@@ -18,6 +18,10 @@ class _UniffiConverterType{{ name }}:
return {{ builtin|ffi_converter_name }}.lift(value)
@staticmethod
+ def check_lower(value):
+ return {{ builtin|ffi_converter_name }}.check_lower(value)
+
+ @staticmethod
def lower(value):
return {{ builtin|ffi_converter_name }}.lower(value)
@@ -52,6 +56,11 @@ class _UniffiConverterType{{ name }}:
return {{ config.into_custom.render("builtin_value") }}
@staticmethod
+ def check_lower(value):
+ builtin_value = {{ config.from_custom.render("value") }}
+ return {{ builtin|check_lower_fn }}(builtin_value)
+
+ @staticmethod
def lower(value):
builtin_value = {{ config.from_custom.render("value") }}
return {{ builtin|lower_fn }}(builtin_value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/DurationHelper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/DurationHelper.py
index 10974e009d..ecb035b7f4 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/DurationHelper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/DurationHelper.py
@@ -12,10 +12,14 @@ class _UniffiConverterDuration(_UniffiConverterRustBuffer):
return datetime.timedelta(seconds=seconds, microseconds=microseconds)
@staticmethod
- def write(value, buf):
+ def check_lower(value):
seconds = value.seconds + value.days * 24 * 3600
- nanoseconds = value.microseconds * 1000
if seconds < 0:
raise ValueError("Invalid duration, must be non-negative")
+
+ @staticmethod
+ def write(value, buf):
+ seconds = value.seconds + value.days * 24 * 3600
+ nanoseconds = value.microseconds * 1000
buf.write_i64(seconds)
buf.write_u32(nanoseconds)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py
index 84d089baf9..d07dd1c44a 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py
@@ -7,31 +7,59 @@
{% if e.is_flat() %}
class {{ type_name }}(enum.Enum):
- {% for variant in e.variants() -%}
- {{ variant.name()|enum_variant_py }} = {{ loop.index }}
+ {%- call py::docstring(e, 4) %}
+ {%- for variant in e.variants() %}
+ {{ variant.name()|enum_variant_py }} = {{ e|variant_discr_literal(loop.index0) }}
+ {%- call py::docstring(variant, 4) %}
{% endfor %}
{% else %}
class {{ type_name }}:
+ {%- call py::docstring(e, 4) %}
def __init__(self):
raise RuntimeError("{{ type_name }} cannot be instantiated directly")
# Each enum variant is a nested class of the enum itself.
{% for variant in e.variants() -%}
class {{ variant.name()|enum_variant_py }}:
- {% for field in variant.fields() %}
- {{- field.name()|var_name }}: "{{- field|type_name }}";
+ {%- call py::docstring(variant, 8) %}
+
+ {%- if variant.has_nameless_fields() %}
+ def __init__(self, *values):
+ if len(values) != {{ variant.fields().len() }}:
+ raise TypeError(f"Expected a tuple of len {{ variant.fields().len() }}, found len {len(values)}")
+ {%- for field in variant.fields() %}
+ if not isinstance(values[{{ loop.index0 }}], {{ field|type_name }}):
+ raise TypeError(f"unexpected type for tuple element {{ loop.index0 }} - expected '{{ field|type_name }}', got '{type(values[{{ loop.index0 }}])}'")
+ {%- endfor %}
+ self._values = values
+
+ def __getitem__(self, index):
+ return self._values[index]
+
+ def __str__(self):
+ return f"{{ type_name }}.{{ variant.name()|enum_variant_py }}{self._values!r}"
+
+ def __eq__(self, other):
+ if not other.is_{{ variant.name()|var_name }}():
+ return False
+ return self._values == other._values
+
+ {%- else -%}
+ {%- for field in variant.fields() %}
+ {{ field.name()|var_name }}: "{{ field|type_name }}"
+ {%- call py::docstring(field, 8) %}
{%- endfor %}
@typing.no_type_check
def __init__(self,{% for field in variant.fields() %}{{ field.name()|var_name }}: "{{- field|type_name }}"{% if loop.last %}{% else %}, {% endif %}{% endfor %}):
- {% if variant.has_fields() %}
+ {%- if variant.has_fields() %}
{%- for field in variant.fields() %}
self.{{ field.name()|var_name }} = {{ field.name()|var_name }}
{%- endfor %}
- {% else %}
+ {%- else %}
pass
- {% endif %}
+ {%- endif %}
def __str__(self):
return "{{ type_name }}.{{ variant.name()|enum_variant_py }}({% for field in variant.fields() %}{{ field.name()|var_name }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in variant.fields() %}self.{{ field.name()|var_name }}{% if loop.last %}{% else %}, {% endif %}{% endfor %})
@@ -44,6 +72,7 @@ class {{ type_name }}:
return False
{%- endfor %}
return True
+ {% endif %}
{% endfor %}
# For each variant, we have an `is_NAME` method for easily checking
@@ -81,6 +110,30 @@ class {{ ffi_converter_name }}(_UniffiConverterRustBuffer):
{%- endfor %}
raise InternalError("Raw enum value doesn't match any cases")
+ @staticmethod
+ def check_lower(value):
+ {%- if e.variants().is_empty() %}
+ pass
+ {%- else %}
+ {%- for variant in e.variants() %}
+ {%- if e.is_flat() %}
+ if value == {{ type_name }}.{{ variant.name()|enum_variant_py }}:
+ {%- else %}
+ if value.is_{{ variant.name()|var_name }}():
+ {%- endif %}
+ {%- for field in variant.fields() %}
+ {%- if variant.has_nameless_fields() %}
+ {{ field|check_lower_fn }}(value._values[{{ loop.index0 }}])
+ {%- else %}
+ {{ field|check_lower_fn }}(value.{{ field.name()|var_name }})
+ {%- endif %}
+ {%- endfor %}
+ return
+ {%- endfor %}
+ raise ValueError(value)
+ {%- endif %}
+
+ @staticmethod
def write(value, buf):
{%- for variant in e.variants() %}
{%- if e.is_flat() %}
@@ -90,7 +143,11 @@ class {{ ffi_converter_name }}(_UniffiConverterRustBuffer):
if value.is_{{ variant.name()|var_name }}():
buf.write_i32({{ loop.index }})
{%- for field in variant.fields() %}
+ {%- if variant.has_nameless_fields() %}
+ {{ field|write_fn }}(value._values[{{ loop.index0 }}], buf)
+ {%- else %}
{{ field|write_fn }}(value.{{ field.name()|var_name }}, buf)
+ {%- endif %}
{%- endfor %}
{%- endif %}
{%- endfor %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py
index 26a1e6452a..0911ff559a 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py
@@ -5,6 +5,7 @@
# __dict__. All of this happens in dummy class to avoid polluting the module
# namespace.
class {{ type_name }}(Exception):
+ {%- call py::docstring(e, 4) %}
pass
_UniffiTemp{{ type_name }} = {{ type_name }}
@@ -14,10 +15,14 @@ class {{ type_name }}: # type: ignore
{%- let variant_type_name = variant.name()|class_name -%}
{%- if e.is_flat() %}
class {{ variant_type_name }}(_UniffiTemp{{ type_name }}):
+ {%- call py::docstring(variant, 8) %}
+
def __repr__(self):
return "{{ type_name }}.{{ variant_type_name }}({})".format(repr(str(self)))
{%- else %}
class {{ variant_type_name }}(_UniffiTemp{{ type_name }}):
+ {%- call py::docstring(variant, 8) %}
+
def __init__(self{% for field in variant.fields() %}, {{ field.name()|var_name }}{% endfor %}):
{%- if variant.has_fields() %}
super().__init__(", ".join([
@@ -60,6 +65,20 @@ class {{ ffi_converter_name }}(_UniffiConverterRustBuffer):
raise InternalError("Raw enum value doesn't match any cases")
@staticmethod
+ def check_lower(value):
+ {%- if e.variants().is_empty() %}
+ pass
+ {%- else %}
+ {%- for variant in e.variants() %}
+ if isinstance(value, {{ type_name }}.{{ variant.name()|class_name }}):
+ {%- for field in variant.fields() %}
+ {{ field|check_lower_fn }}(value.{{ field.name()|var_name }})
+ {%- endfor %}
+ return
+ {%- endfor %}
+ {%- endif %}
+
+ @staticmethod
def write(value, buf):
{%- for variant in e.variants() %}
if isinstance(value, {{ type_name }}.{{ variant.name()|class_name }}):
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py
index 71e05e8b06..6c0cee85ef 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py
@@ -1,9 +1,9 @@
-{%- let ns = namespace|fn_name %}
+{%- let module = python_config.module_for_namespace(namespace) -%}
# External type {{name}} is in namespace "{{namespace}}", crate {{module_path}}
{%- let ffi_converter_name = "_UniffiConverterType{}"|format(name) %}
-{{ self.add_import_of(ns, ffi_converter_name) }}
-{{ self.add_import_of(ns, name) }} {#- import the type alias itself -#}
+{{ self.add_import_of(module, ffi_converter_name) }}
+{{ self.add_import_of(module, name|class_name) }} {#- import the type alias itself -#}
{%- let rustbuffer_local_name = "_UniffiRustBuffer{}"|format(name) %}
-{{ self.add_import_of_as(ns, "_UniffiRustBuffer", rustbuffer_local_name) }}
+{{ self.add_import_of_as(module, "_UniffiRustBuffer", rustbuffer_local_name) }}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float32Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float32Helper.py
index a52107a638..49a1a7286e 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float32Helper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float32Helper.py
@@ -4,5 +4,5 @@ class _UniffiConverterFloat(_UniffiConverterPrimitiveFloat):
return buf.read_float()
@staticmethod
- def write_unchecked(value, buf):
+ def write(value, buf):
buf.write_float(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float64Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float64Helper.py
index 772f5080e9..e2084c7b13 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float64Helper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float64Helper.py
@@ -4,5 +4,5 @@ class _UniffiConverterDouble(_UniffiConverterPrimitiveFloat):
return buf.read_double()
@staticmethod
- def write_unchecked(value, buf):
+ def write(value, buf):
buf.write_double(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ForeignExecutorTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ForeignExecutorTemplate.py
deleted file mode 100644
index 6a6932fed0..0000000000
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ForeignExecutorTemplate.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# FFI code for the ForeignExecutor type
-
-{{ self.add_import("asyncio") }}
-
-_UNIFFI_RUST_TASK_CALLBACK_SUCCESS = 0
-_UNIFFI_RUST_TASK_CALLBACK_CANCELLED = 1
-_UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS = 0
-_UNIFFI_FOREIGN_EXECUTOR_CALLBACK_CANCELED = 1
-_UNIFFI_FOREIGN_EXECUTOR_CALLBACK_ERROR = 2
-
-class {{ ffi_converter_name }}:
- _pointer_manager = _UniffiPointerManager()
-
- @classmethod
- def lower(cls, eventloop):
- if not isinstance(eventloop, asyncio.BaseEventLoop):
- raise TypeError("_uniffi_executor_callback: Expected EventLoop instance")
- return cls._pointer_manager.new_pointer(eventloop)
-
- @classmethod
- def write(cls, eventloop, buf):
- buf.write_c_size_t(cls.lower(eventloop))
-
- @classmethod
- def read(cls, buf):
- return cls.lift(buf.read_c_size_t())
-
- @classmethod
- def lift(cls, value):
- return cls._pointer_manager.lookup(value)
-
-@_UNIFFI_FOREIGN_EXECUTOR_CALLBACK_T
-def _uniffi_executor_callback(eventloop_address, delay, task_ptr, task_data):
- if task_ptr is None:
- {{ ffi_converter_name }}._pointer_manager.release_pointer(eventloop_address)
- return _UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS
- else:
- eventloop = {{ ffi_converter_name }}._pointer_manager.lookup(eventloop_address)
- if eventloop.is_closed():
- return _UNIFFI_FOREIGN_EXECUTOR_CALLBACK_CANCELED
-
- callback = _UNIFFI_RUST_TASK(task_ptr)
- # FIXME: there's no easy way to get a callback when an eventloop is closed. This means that
- # if eventloop is called before the `call_soon_threadsafe()` calls are invoked, the call
- # will never happen and we will probably leak a resource.
- if delay == 0:
- # This can be called from any thread, so make sure to use `call_soon_threadsafe'
- eventloop.call_soon_threadsafe(callback, task_data,
- _UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS)
- else:
- # For delayed tasks, we use `call_soon_threadsafe()` + `call_later()` to make the
- # operation threadsafe
- eventloop.call_soon_threadsafe(eventloop.call_later, delay / 1000.0, callback,
- task_data, _UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS)
- return _UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS
-
-# Register the callback with the scaffolding
-{%- match ci.ffi_foreign_executor_callback_set() %}
-{%- when Some with (fn) %}
-_UniffiLib.{{ fn.name() }}(_uniffi_executor_callback)
-{%- when None %}
-{#- No foreign executor, we don't set anything #}
-{% endmatch %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/HandleMap.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/HandleMap.py
new file mode 100644
index 0000000000..f7c13cf745
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/HandleMap.py
@@ -0,0 +1,33 @@
+class _UniffiHandleMap:
+ """
+ A map where inserting, getting and removing data is synchronized with a lock.
+ """
+
+ def __init__(self):
+ # type Handle = int
+ self._map = {} # type: Dict[Handle, Any]
+ self._lock = threading.Lock()
+ self._counter = itertools.count()
+
+ def insert(self, obj):
+ with self._lock:
+ handle = next(self._counter)
+ self._map[handle] = obj
+ return handle
+
+ def get(self, handle):
+ try:
+ with self._lock:
+ return self._map[handle]
+ except KeyError:
+ raise InternalError("UniffiHandleMap.get: Invalid handle")
+
+ def remove(self, handle):
+ try:
+ with self._lock:
+ return self._map.pop(handle)
+ except KeyError:
+ raise InternalError("UniffiHandleMap.remove: Invalid handle")
+
+ def __len__(self):
+ return len(self._map)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Helpers.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Helpers.py
index dca962f176..5d4bcbba89 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Helpers.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Helpers.py
@@ -16,15 +16,19 @@ class _UniffiRustCallStatus(ctypes.Structure):
# These match the values from the uniffi::rustcalls module
CALL_SUCCESS = 0
CALL_ERROR = 1
- CALL_PANIC = 2
+ CALL_UNEXPECTED_ERROR = 2
+
+ @staticmethod
+ def default():
+ return _UniffiRustCallStatus(code=_UniffiRustCallStatus.CALL_SUCCESS, error_buf=_UniffiRustBuffer.default())
def __str__(self):
if self.code == _UniffiRustCallStatus.CALL_SUCCESS:
return "_UniffiRustCallStatus(CALL_SUCCESS)"
elif self.code == _UniffiRustCallStatus.CALL_ERROR:
return "_UniffiRustCallStatus(CALL_ERROR)"
- elif self.code == _UniffiRustCallStatus.CALL_PANIC:
- return "_UniffiRustCallStatus(CALL_PANIC)"
+ elif self.code == _UniffiRustCallStatus.CALL_UNEXPECTED_ERROR:
+ return "_UniffiRustCallStatus(CALL_UNEXPECTED_ERROR)"
else:
return "_UniffiRustCallStatus(<invalid code>)"
@@ -37,7 +41,7 @@ def _rust_call_with_error(error_ffi_converter, fn, *args):
#
# This function is used for rust calls that return Result<> and therefore can set the CALL_ERROR status code.
# error_ffi_converter must be set to the _UniffiConverter for the error class that corresponds to the result.
- call_status = _UniffiRustCallStatus(code=_UniffiRustCallStatus.CALL_SUCCESS, error_buf=_UniffiRustBuffer(0, 0, None))
+ call_status = _UniffiRustCallStatus.default()
args_with_error = args + (ctypes.byref(call_status),)
result = fn(*args_with_error)
@@ -53,7 +57,7 @@ def _uniffi_check_call_status(error_ffi_converter, call_status):
raise InternalError("_rust_call_with_error: CALL_ERROR, but error_ffi_converter is None")
else:
raise error_ffi_converter.lift(call_status.error_buf)
- elif call_status.code == _UniffiRustCallStatus.CALL_PANIC:
+ elif call_status.code == _UniffiRustCallStatus.CALL_UNEXPECTED_ERROR:
# When the rust code sees a panic, it tries to construct a _UniffiRustBuffer
# with the message. But if that code panics, then it just sends back
# an empty buffer.
@@ -66,10 +70,20 @@ def _uniffi_check_call_status(error_ffi_converter, call_status):
raise InternalError("Invalid _UniffiRustCallStatus code: {}".format(
call_status.code))
-# A function pointer for a callback as defined by UniFFI.
-# Rust definition `fn(handle: u64, method: u32, args: _UniffiRustBuffer, buf_ptr: *mut _UniffiRustBuffer) -> int`
-_UNIFFI_FOREIGN_CALLBACK_T = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_ulonglong, ctypes.c_ulong, ctypes.POINTER(ctypes.c_char), ctypes.c_int, ctypes.POINTER(_UniffiRustBuffer))
-
-# UniFFI future continuation
-_UNIFFI_FUTURE_CONTINUATION_T = ctypes.CFUNCTYPE(None, ctypes.c_size_t, ctypes.c_int8)
+def _uniffi_trait_interface_call(call_status, make_call, write_return_value):
+ try:
+ return write_return_value(make_call())
+ except Exception as e:
+ call_status.code = _UniffiRustCallStatus.CALL_UNEXPECTED_ERROR
+ call_status.error_buf = {{ Type::String.borrow()|lower_fn }}(repr(e))
+def _uniffi_trait_interface_call_with_error(call_status, make_call, write_return_value, error_type, lower_error):
+ try:
+ try:
+ return write_return_value(make_call())
+ except error_type as e:
+ call_status.code = _UniffiRustCallStatus.CALL_ERROR
+ call_status.error_buf = lower_error(e)
+ except Exception as e:
+ call_status.code = _UniffiRustCallStatus.CALL_UNEXPECTED_ERROR
+ call_status.error_buf = {{ Type::String.borrow()|lower_fn }}(repr(e))
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int16Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int16Helper.py
index 99f19dc1c0..befa563384 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int16Helper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int16Helper.py
@@ -8,5 +8,5 @@ class _UniffiConverterInt16(_UniffiConverterPrimitiveInt):
return buf.read_i16()
@staticmethod
- def write_unchecked(value, buf):
+ def write(value, buf):
buf.write_i16(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int32Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int32Helper.py
index 3b142c8749..70644f6717 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int32Helper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int32Helper.py
@@ -8,5 +8,5 @@ class _UniffiConverterInt32(_UniffiConverterPrimitiveInt):
return buf.read_i32()
@staticmethod
- def write_unchecked(value, buf):
+ def write(value, buf):
buf.write_i32(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int64Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int64Helper.py
index 6e94379cbf..232f127bd6 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int64Helper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int64Helper.py
@@ -8,5 +8,5 @@ class _UniffiConverterInt64(_UniffiConverterPrimitiveInt):
return buf.read_i64()
@staticmethod
- def write_unchecked(value, buf):
+ def write(value, buf):
buf.write_i64(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int8Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int8Helper.py
index 732530e3cb..c1de1625e7 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int8Helper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int8Helper.py
@@ -8,5 +8,5 @@ class _UniffiConverterInt8(_UniffiConverterPrimitiveInt):
return buf.read_i8()
@staticmethod
- def write_unchecked(value, buf):
+ def write(value, buf):
buf.write_i8(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/MapTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/MapTemplate.py
index 387227ed09..a09ca28a30 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/MapTemplate.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/MapTemplate.py
@@ -3,6 +3,12 @@
class {{ ffi_converter_name }}(_UniffiConverterRustBuffer):
@classmethod
+ def check_lower(cls, items):
+ for (key, value) in items.items():
+ {{ key_ffi_converter }}.check_lower(key)
+ {{ value_ffi_converter }}.check_lower(value)
+
+ @classmethod
def write(cls, items, buf):
buf.write_i32(len(items))
for (key, value) in items.items():
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py
index fac6cd5564..1929f9aad6 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py
@@ -1,22 +1,6 @@
# Define some ctypes FFI types that we use in the library
"""
-ctypes type for the foreign executor callback. This is a built-in interface for scheduling
-tasks
-
-Args:
- executor: opaque c_size_t value representing the eventloop
- delay: delay in ms
- task: function pointer to the task callback
- task_data: void pointer to the task callback data
-
-Normally we should call task(task_data) after the detail.
-However, when task is NULL this indicates that Rust has dropped the ForeignExecutor and we should
-decrease the EventLoop refcount.
-"""
-_UNIFFI_FOREIGN_EXECUTOR_CALLBACK_T = ctypes.CFUNCTYPE(ctypes.c_int8, ctypes.c_size_t, ctypes.c_uint32, ctypes.c_void_p, ctypes.c_void_p)
-
-"""
Function pointer for a Rust task, which a callback function that takes a opaque pointer
"""
_UNIFFI_RUST_TASK = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_int8)
@@ -25,7 +9,7 @@ def _uniffi_future_callback_t(return_type):
"""
Factory function to create callback function types for async functions
"""
- return ctypes.CFUNCTYPE(None, ctypes.c_size_t, return_type, _UniffiRustCallStatus)
+ return ctypes.CFUNCTYPE(None, ctypes.c_uint64, return_type, _UniffiRustCallStatus)
def _uniffi_load_indirect():
"""
@@ -72,12 +56,37 @@ def _uniffi_check_api_checksums(lib):
# This is an implementation detail which will be called internally by the public API.
_UniffiLib = _uniffi_load_indirect()
-{%- for func in ci.iter_ffi_function_definitions() %}
+
+{%- for def in ci.ffi_definitions() %}
+{%- match def %}
+{%- when FfiDefinition::CallbackFunction(callback) %}
+{{ callback.name()|ffi_callback_name }} = ctypes.CFUNCTYPE(
+ {%- match callback.return_type() %}
+ {%- when Some(return_type) %}{{ return_type|ffi_type_name }},
+ {%- when None %}None,
+ {%- endmatch %}
+ {%- for arg in callback.arguments() -%}
+ {{ arg.type_().borrow()|ffi_type_name }},
+ {%- endfor -%}
+ {%- if callback.has_rust_call_status_arg() %}
+ ctypes.POINTER(_UniffiRustCallStatus),
+ {%- endif %}
+)
+{%- when FfiDefinition::Struct(ffi_struct) %}
+class {{ ffi_struct.name()|ffi_struct_name }}(ctypes.Structure):
+ _fields_ = [
+ {%- for field in ffi_struct.fields() %}
+ ("{{ field.name()|var_name }}", {{ field.type_().borrow()|ffi_type_name }}),
+ {%- endfor %}
+ ]
+{%- when FfiDefinition::Function(func) %}
_UniffiLib.{{ func.name() }}.argtypes = (
{%- call py::arg_list_ffi_decl(func) -%}
)
_UniffiLib.{{ func.name() }}.restype = {% match func.return_type() %}{% when Some with (type_) %}{{ type_|ffi_type_name }}{% when None %}None{% endmatch %}
+{%- endmatch %}
{%- endfor %}
+
{# Ensure to call the contract verification only after we defined all functions. -#}
_uniffi_check_contract_api_version(_UniffiLib)
_uniffi_check_api_checksums(_UniffiLib)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py
index 7e98f7c46f..18dca4743a 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py
@@ -1,14 +1,33 @@
{%- let obj = ci|get_object_definition(name) %}
-
-class {{ type_name }}:
+{%- let (protocol_name, impl_name) = obj|object_names %}
+{%- let methods = obj.methods() %}
+{%- let protocol_docstring = obj.docstring() %}
+
+{% include "Protocol.py" %}
+
+{% if ci.is_name_used_as_error(name) %}
+class {{ impl_name }}(Exception):
+{%- else %}
+class {{ impl_name }}:
+{%- endif %}
+ {%- call py::docstring(obj, 4) %}
_pointer: ctypes.c_void_p
{%- match obj.primary_constructor() %}
{%- when Some with (cons) %}
+{%- if cons.is_async() %}
+ def __init__(self, *args, **kw):
+ raise ValueError("async constructors not supported.")
+{%- else %}
def __init__(self, {% call py::arg_list_decl(cons) -%}):
+ {%- call py::docstring(cons, 8) %}
{%- call py::setup_args_extra_indent(cons) %}
self._pointer = {% call py::to_ffi_call(cons) %}
+{%- endif %}
{%- when None %}
+ {# no __init__ means simple construction without a pointer works, which can confuse #}
+ def __init__(self, *args, **kwargs):
+ raise ValueError("This class has no default constructor")
{%- endmatch %}
def __del__(self):
@@ -17,6 +36,9 @@ class {{ type_name }}:
if pointer is not None:
_rust_call(_UniffiLib.{{ obj.ffi_object_free().name() }}, pointer)
+ def _uniffi_clone_pointer(self):
+ return _rust_call(_UniffiLib.{{ obj.ffi_object_clone().name() }}, self._pointer)
+
# Used by alternative constructors or any methods which return this type.
@classmethod
def _make_instance_(cls, pointer):
@@ -29,17 +51,32 @@ class {{ type_name }}:
{%- for cons in obj.alternate_constructors() %}
@classmethod
+{%- if cons.is_async() %}
+ async def {{ cons.name()|fn_name }}(cls, {% call py::arg_list_decl(cons) %}):
+ {%- call py::docstring(cons, 8) %}
+ {%- call py::setup_args_extra_indent(cons) %}
+
+ return await _uniffi_rust_call_async(
+ _UniffiLib.{{ cons.ffi_func().name() }}({% call py::arg_list_lowered(cons) %}),
+ _UniffiLib.{{ cons.ffi_rust_future_poll(ci) }},
+ _UniffiLib.{{ cons.ffi_rust_future_complete(ci) }},
+ _UniffiLib.{{ cons.ffi_rust_future_free(ci) }},
+ {{ ffi_converter_name }}.lift,
+ {% call py::error_ffi_converter(cons) %}
+ )
+{%- else %}
def {{ cons.name()|fn_name }}(cls, {% call py::arg_list_decl(cons) %}):
+ {%- call py::docstring(cons, 8) %}
{%- call py::setup_args_extra_indent(cons) %}
# Call the (fallible) function before creating any half-baked object instances.
pointer = {% call py::to_ffi_call(cons) %}
return cls._make_instance_(pointer)
+{%- endif %}
{% endfor %}
{%- for meth in obj.methods() -%}
{%- call py::method_decl(meth.name()|fn_name, meth) %}
-{% endfor %}
-
+{%- endfor %}
{%- for tm in obj.uniffi_traits() -%}
{%- match tm %}
{%- when UniffiTrait::Debug { fmt } %}
@@ -51,37 +88,84 @@ class {{ type_name }}:
if not isinstance(other, {{ type_name }}):
return NotImplemented
- return {{ eq.return_type().unwrap()|lift_fn }}({% call py::to_ffi_call_with_prefix("self._pointer", eq) %})
+ return {{ eq.return_type().unwrap()|lift_fn }}({% call py::to_ffi_call_with_prefix("self._uniffi_clone_pointer()", eq) %})
def __ne__(self, other: object) -> {{ ne.return_type().unwrap()|type_name }}:
if not isinstance(other, {{ type_name }}):
return NotImplemented
- return {{ ne.return_type().unwrap()|lift_fn }}({% call py::to_ffi_call_with_prefix("self._pointer", ne) %})
+ return {{ ne.return_type().unwrap()|lift_fn }}({% call py::to_ffi_call_with_prefix("self._uniffi_clone_pointer()", ne) %})
{%- when UniffiTrait::Hash { hash } %}
{%- call py::method_decl("__hash__", hash) %}
-{% endmatch %}
-{% endfor %}
-
-
-class {{ ffi_converter_name }}:
+{%- endmatch %}
+{%- endfor %}
+
+{%- if obj.has_callback_interface() %}
+{%- let ffi_init_callback = obj.ffi_init_callback() %}
+{%- let vtable = obj.vtable().expect("trait interface should have a vtable") %}
+{%- let vtable_methods = obj.vtable_methods() %}
+{% include "CallbackInterfaceImpl.py" %}
+{%- endif %}
+
+{# Objects as error #}
+{%- if ci.is_name_used_as_error(name) %}
+{# Due to some mismatches in the ffi converter mechanisms, errors are forced to be a RustBuffer #}
+class {{ ffi_converter_name }}__as_error(_UniffiConverterRustBuffer):
@classmethod
def read(cls, buf):
- ptr = buf.read_u64()
- if ptr == 0:
- raise InternalError("Raw pointer value was null")
- return cls.lift(ptr)
+ raise NotImplementedError()
@classmethod
def write(cls, value, buf):
- if not isinstance(value, {{ type_name }}):
- raise TypeError("Expected {{ type_name }} instance, {} found".format(type(value).__name__))
- buf.write_u64(cls.lower(value))
+ raise NotImplementedError()
@staticmethod
def lift(value):
- return {{ type_name }}._make_instance_(value)
+ # Errors are always a rust buffer holding a pointer - which is a "read"
+ with value.consume_with_stream() as stream:
+ return {{ ffi_converter_name }}.read(stream)
@staticmethod
def lower(value):
- return value._pointer
+ raise NotImplementedError()
+
+{%- endif %}
+
+class {{ ffi_converter_name }}:
+ {%- if obj.has_callback_interface() %}
+ _handle_map = _UniffiHandleMap()
+ {%- endif %}
+
+ @staticmethod
+ def lift(value: int):
+ return {{ impl_name }}._make_instance_(value)
+
+ @staticmethod
+ def check_lower(value: {{ type_name }}):
+ {%- if obj.has_callback_interface() %}
+ pass
+ {%- else %}
+ if not isinstance(value, {{ impl_name }}):
+ raise TypeError("Expected {{ impl_name }} instance, {} found".format(type(value).__name__))
+ {%- endif %}
+
+ @staticmethod
+ def lower(value: {{ protocol_name }}):
+ {%- if obj.has_callback_interface() %}
+ return {{ ffi_converter_name }}._handle_map.insert(value)
+ {%- else %}
+ if not isinstance(value, {{ impl_name }}):
+ raise TypeError("Expected {{ impl_name }} instance, {} found".format(type(value).__name__))
+ return value._uniffi_clone_pointer()
+ {%- endif %}
+
+ @classmethod
+ def read(cls, buf: _UniffiRustBuffer):
+ ptr = buf.read_u64()
+ if ptr == 0:
+ raise InternalError("Raw pointer value was null")
+ return cls.lift(ptr)
+
+ @classmethod
+ def write(cls, value: {{ protocol_name }}, buf: _UniffiRustBuffer):
+ buf.write_u64(cls.lower(value))
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/OptionalTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/OptionalTemplate.py
index e406c51d49..4c07ae3e34 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/OptionalTemplate.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/OptionalTemplate.py
@@ -2,6 +2,11 @@
class {{ ffi_converter_name }}(_UniffiConverterRustBuffer):
@classmethod
+ def check_lower(cls, value):
+ if value is not None:
+ {{ inner_ffi_converter }}.check_lower(value)
+
+ @classmethod
def write(cls, value, buf):
if value is None:
buf.write_u8(0)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/PointerManager.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/PointerManager.py
deleted file mode 100644
index 23aa28eab4..0000000000
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/PointerManager.py
+++ /dev/null
@@ -1,68 +0,0 @@
-class _UniffiPointerManagerCPython:
- """
- Manage giving out pointers to Python objects on CPython
-
- This class is used to generate opaque pointers that reference Python objects to pass to Rust.
- It assumes a CPython platform. See _UniffiPointerManagerGeneral for the alternative.
- """
-
- def new_pointer(self, obj):
- """
- Get a pointer for an object as a ctypes.c_size_t instance
-
- Each call to new_pointer() must be balanced with exactly one call to release_pointer()
-
- This returns a ctypes.c_size_t. This is always the same size as a pointer and can be
- interchanged with pointers for FFI function arguments and return values.
- """
- # IncRef the object since we're going to pass a pointer to Rust
- ctypes.pythonapi.Py_IncRef(ctypes.py_object(obj))
- # id() is the object address on CPython
- # (https://docs.python.org/3/library/functions.html#id)
- return id(obj)
-
- def release_pointer(self, address):
- py_obj = ctypes.cast(address, ctypes.py_object)
- obj = py_obj.value
- ctypes.pythonapi.Py_DecRef(py_obj)
- return obj
-
- def lookup(self, address):
- return ctypes.cast(address, ctypes.py_object).value
-
-class _UniffiPointerManagerGeneral:
- """
- Manage giving out pointers to Python objects on non-CPython platforms
-
- This has the same API as _UniffiPointerManagerCPython, but doesn't assume we're running on
- CPython and is slightly slower.
-
- Instead of using real pointers, it maps integer values to objects and returns the keys as
- c_size_t values.
- """
-
- def __init__(self):
- self._map = {}
- self._lock = threading.Lock()
- self._current_handle = 0
-
- def new_pointer(self, obj):
- with self._lock:
- handle = self._current_handle
- self._current_handle += 1
- self._map[handle] = obj
- return handle
-
- def release_pointer(self, handle):
- with self._lock:
- return self._map.pop(handle)
-
- def lookup(self, handle):
- with self._lock:
- return self._map[handle]
-
-# Pick an pointer manager implementation based on the platform
-if platform.python_implementation() == 'CPython':
- _UniffiPointerManager = _UniffiPointerManagerCPython # type: ignore
-else:
- _UniffiPointerManager = _UniffiPointerManagerGeneral # type: ignore
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Protocol.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Protocol.py
new file mode 100644
index 0000000000..3b7e93596a
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Protocol.py
@@ -0,0 +1,9 @@
+class {{ protocol_name }}(typing.Protocol):
+ {%- call py::docstring_value(protocol_docstring, 4) %}
+ {%- for meth in methods.iter() %}
+ def {{ meth.name()|fn_name }}(self, {% call py::arg_list_decl(meth) %}):
+ {%- call py::docstring(meth, 8) %}
+ raise NotImplementedError
+ {%- else %}
+ pass
+ {%- endfor %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py
index 99a30e120f..0b5634eb52 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py
@@ -1,11 +1,14 @@
{%- let rec = ci|get_record_definition(name) %}
class {{ type_name }}:
- {% for field in rec.fields() %}
- {{- field.name()|var_name }}: "{{- field|type_name }}";
+ {%- call py::docstring(rec, 4) %}
+ {%- for field in rec.fields() %}
+ {{ field.name()|var_name }}: "{{ field|type_name }}"
+ {%- call py::docstring(field, 4) %}
{%- endfor %}
+ {%- if rec.has_fields() %}
@typing.no_type_check
- def __init__(self, {% for field in rec.fields() %}
+ def __init__(self, *, {% for field in rec.fields() %}
{{- field.name()|var_name }}: "{{- field|type_name }}"
{%- if field.default_value().is_some() %} = _DEFAULT{% endif %}
{%- if !loop.last %}, {% endif %}
@@ -22,6 +25,7 @@ class {{ type_name }}:
self.{{ field_name }} = {{ field_name }}
{%- endmatch %}
{%- endfor %}
+ {%- endif %}
def __str__(self):
return "{{ type_name }}({% for field in rec.fields() %}{{ field.name()|var_name }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in rec.fields() %}self.{{ field.name()|var_name }}{% if loop.last %}{% else %}, {% endif %}{% endfor %})
@@ -43,7 +47,21 @@ class {{ ffi_converter_name }}(_UniffiConverterRustBuffer):
)
@staticmethod
+ def check_lower(value):
+ {%- if rec.fields().is_empty() %}
+ pass
+ {%- else %}
+ {%- for field in rec.fields() %}
+ {{ field|check_lower_fn }}(value.{{ field.name()|var_name }})
+ {%- endfor %}
+ {%- endif %}
+
+ @staticmethod
def write(value, buf):
+ {%- if rec.has_fields() %}
{%- for field in rec.fields() %}
{{ field|write_fn }}(value.{{ field.name()|var_name }}, buf)
{%- endfor %}
+ {%- else %}
+ pass
+ {%- endif %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferHelper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferHelper.py
index daabd5b4b9..4db74fb157 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferHelper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferHelper.py
@@ -1,28 +1,16 @@
# Types conforming to `_UniffiConverterPrimitive` pass themselves directly over the FFI.
class _UniffiConverterPrimitive:
@classmethod
- def check(cls, value):
- return value
-
- @classmethod
def lift(cls, value):
return value
@classmethod
def lower(cls, value):
- return cls.lowerUnchecked(cls.check(value))
-
- @classmethod
- def lowerUnchecked(cls, value):
return value
- @classmethod
- def write(cls, value, buf):
- cls.write_unchecked(cls.check(value), buf)
-
class _UniffiConverterPrimitiveInt(_UniffiConverterPrimitive):
@classmethod
- def check(cls, value):
+ def check_lower(cls, value):
try:
value = value.__index__()
except Exception:
@@ -31,18 +19,16 @@ class _UniffiConverterPrimitiveInt(_UniffiConverterPrimitive):
raise TypeError("__index__ returned non-int (type {})".format(type(value).__name__))
if not cls.VALUE_MIN <= value < cls.VALUE_MAX:
raise ValueError("{} requires {} <= value < {}".format(cls.CLASS_NAME, cls.VALUE_MIN, cls.VALUE_MAX))
- return super().check(value)
class _UniffiConverterPrimitiveFloat(_UniffiConverterPrimitive):
@classmethod
- def check(cls, value):
+ def check_lower(cls, value):
try:
value = value.__float__()
except Exception:
raise TypeError("must be real number, not {}".format(type(value).__name__))
if not isinstance(value, float):
raise TypeError("__float__ returned non-float (type {})".format(type(value).__name__))
- return super().check(value)
# Helper class for wrapper types that will always go through a _UniffiRustBuffer.
# Classes should inherit from this and implement the `read` and `write` static methods.
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferTemplate.py
index c317a632fc..44e0ba1001 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferTemplate.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferTemplate.py
@@ -1,12 +1,16 @@
class _UniffiRustBuffer(ctypes.Structure):
_fields_ = [
- ("capacity", ctypes.c_int32),
- ("len", ctypes.c_int32),
+ ("capacity", ctypes.c_uint64),
+ ("len", ctypes.c_uint64),
("data", ctypes.POINTER(ctypes.c_char)),
]
@staticmethod
+ def default():
+ return _UniffiRustBuffer(0, 0, None)
+
+ @staticmethod
def alloc(size):
return _rust_call(_UniffiLib.{{ ci.ffi_rustbuffer_alloc().name() }}, size)
@@ -137,9 +141,6 @@ class _UniffiRustBufferStream:
def read_double(self):
return self._unpack_from(8, ">d")
- def read_c_size_t(self):
- return self._unpack_from(ctypes.sizeof(ctypes.c_size_t) , "@N")
-
class _UniffiRustBufferBuilder:
"""
Helper for structured writing of bytes into a _UniffiRustBuffer.
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/SequenceTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/SequenceTemplate.py
index 3c9f5a4596..3c30d9f9f5 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/SequenceTemplate.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/SequenceTemplate.py
@@ -2,6 +2,11 @@
class {{ ffi_converter_name}}(_UniffiConverterRustBuffer):
@classmethod
+ def check_lower(cls, value):
+ for item in value:
+ {{ inner_ffi_converter }}.check_lower(item)
+
+ @classmethod
def write(cls, value, buf):
items = len(value)
buf.write_i32(items)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/StringHelper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/StringHelper.py
index 40890b6abc..d574edd09f 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/StringHelper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/StringHelper.py
@@ -1,6 +1,6 @@
class _UniffiConverterString:
@staticmethod
- def check(value):
+ def check_lower(value):
if not isinstance(value, str):
raise TypeError("argument must be str, not {}".format(type(value).__name__))
return value
@@ -15,7 +15,6 @@ class _UniffiConverterString:
@staticmethod
def write(value, buf):
- value = _UniffiConverterString.check(value)
utf8_bytes = value.encode("utf-8")
buf.write_i32(len(utf8_bytes))
buf.write(utf8_bytes)
@@ -27,7 +26,6 @@ class _UniffiConverterString:
@staticmethod
def lower(value):
- value = _UniffiConverterString.check(value)
with _UniffiRustBuffer.alloc_with_builder() as builder:
builder.write(value.encode("utf-8"))
return builder.finalize()
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/TimestampHelper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/TimestampHelper.py
index 8402f6095d..76d7f8bcdb 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/TimestampHelper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/TimestampHelper.py
@@ -18,6 +18,10 @@ class _UniffiConverterTimestamp(_UniffiConverterRustBuffer):
return datetime.datetime.fromtimestamp(0, tz=datetime.timezone.utc) - datetime.timedelta(seconds=-seconds, microseconds=microseconds)
@staticmethod
+ def check_lower(value):
+ pass
+
+ @staticmethod
def write(value, buf):
if value >= datetime.datetime.fromtimestamp(0, datetime.timezone.utc):
sign = 1
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py
index f258b60a1c..230b9e853f 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py
@@ -1,7 +1,15 @@
{%- if func.is_async() %}
-def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}):
- return _uniffi_rust_call_async(
+{%- match func.return_type() -%}
+{%- when Some with (return_type) %}
+async def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}) -> "{{ return_type|type_name }}":
+{% when None %}
+async def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}) -> None:
+{% endmatch %}
+
+ {%- call py::docstring(func, 4) %}
+ {%- call py::setup_args(func) %}
+ return await _uniffi_rust_call_async(
_UniffiLib.{{ func.ffi_func().name() }}({% call py::arg_list_lowered(func) %}),
_UniffiLib.{{func.ffi_rust_future_poll(ci) }},
_UniffiLib.{{func.ffi_rust_future_complete(ci) }},
@@ -13,13 +21,7 @@ def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}):
{%- when None %}
lambda val: None,
{% endmatch %}
- # Error FFI converter
- {%- match func.throws_type() %}
- {%- when Some(e) %}
- {{ e|ffi_converter_name }},
- {%- when None %}
- None,
- {%- endmatch %}
+ {% call py::error_ffi_converter(func) %}
)
{%- else %}
@@ -27,11 +29,13 @@ def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}):
{%- when Some with (return_type) %}
def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}) -> "{{ return_type|type_name }}":
+ {%- call py::docstring(func, 4) %}
{%- call py::setup_args(func) %}
return {{ return_type|lift_fn }}({% call py::to_ffi_call(func) %})
{% when None %}
-def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}):
+def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}) -> None:
+ {%- call py::docstring(func, 4) %}
{%- call py::setup_args(func) %}
{% call py::to_ffi_call(func) %}
{% endmatch %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Types.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Types.py
index 5e05314c37..4aaed253e0 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Types.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Types.py
@@ -85,7 +85,7 @@
{%- when Type::Map { key_type, value_type } %}
{%- include "MapTemplate.py" %}
-{%- when Type::CallbackInterface { name: id, module_path } %}
+{%- when Type::CallbackInterface { name, module_path } %}
{%- include "CallbackInterfaceTemplate.py" %}
{%- when Type::Custom { name, module_path, builtin } %}
@@ -94,9 +94,6 @@
{%- when Type::External { name, module_path, namespace, kind, tagged } %}
{%- include "ExternalTemplate.py" %}
-{%- when Type::ForeignExecutor %}
-{%- include "ForeignExecutorTemplate.py" %}
-
{%- else %}
{%- endmatch %}
{%- endfor %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt16Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt16Helper.py
index 081c6731ce..039bf76162 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt16Helper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt16Helper.py
@@ -8,5 +8,5 @@ class _UniffiConverterUInt16(_UniffiConverterPrimitiveInt):
return buf.read_u16()
@staticmethod
- def write_unchecked(value, buf):
+ def write(value, buf):
buf.write_u16(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt32Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt32Helper.py
index b80e75177d..1650bf9b60 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt32Helper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt32Helper.py
@@ -8,5 +8,5 @@ class _UniffiConverterUInt32(_UniffiConverterPrimitiveInt):
return buf.read_u32()
@staticmethod
- def write_unchecked(value, buf):
+ def write(value, buf):
buf.write_u32(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt64Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt64Helper.py
index 4b87e58547..f354545e26 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt64Helper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt64Helper.py
@@ -8,5 +8,5 @@ class _UniffiConverterUInt64(_UniffiConverterPrimitiveInt):
return buf.read_u64()
@staticmethod
- def write_unchecked(value, buf):
+ def write(value, buf):
buf.write_u64(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt8Helper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt8Helper.py
index 33026706f2..cee130b4d9 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt8Helper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt8Helper.py
@@ -8,5 +8,5 @@ class _UniffiConverterUInt8(_UniffiConverterPrimitiveInt):
return buf.read_u8()
@staticmethod
- def write_unchecked(value, buf):
+ def write(value, buf):
buf.write_u8(value)
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/macros.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/macros.py
index ef3b1bb94d..6818a8c107 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/macros.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/macros.py
@@ -5,27 +5,29 @@
#}
{%- macro to_ffi_call(func) -%}
- {%- match func.throws_type() -%}
- {%- when Some with (e) -%}
-_rust_call_with_error({{ e|ffi_converter_name }},
- {%- else -%}
-_rust_call(
- {%- endmatch -%}
- _UniffiLib.{{ func.ffi_func().name() }},
- {%- call arg_list_lowered(func) -%}
-)
+{%- call _to_ffi_call_with_prefix_arg("", func) %}
{%- endmacro -%}
{%- macro to_ffi_call_with_prefix(prefix, func) -%}
- {%- match func.throws_type() -%}
- {%- when Some with (e) -%}
-_rust_call_with_error(
- {{ e|ffi_converter_name }},
- {%- else -%}
+{%- call _to_ffi_call_with_prefix_arg(format!("{},", prefix), func) %}
+{%- endmacro -%}
+
+{%- macro _to_ffi_call_with_prefix_arg(prefix, func) -%}
+{%- match func.throws_type() -%}
+{%- when Some with (e) -%}
+{%- match e -%}
+{%- when Type::Enum { name, module_path } -%}
+_rust_call_with_error({{ e|ffi_converter_name }},
+{%- when Type::Object { name, module_path, imp } -%}
+_rust_call_with_error({{ e|ffi_converter_name }}__as_error,
+{%- else %}
+# unsupported error type!
+{%- endmatch %}
+{%- else -%}
_rust_call(
- {%- endmatch -%}
+{%- endmatch -%}
_UniffiLib.{{ func.ffi_func().name() }},
- {{- prefix }},
+ {{- prefix }}
{%- call arg_list_lowered(func) -%}
)
{%- endmacro -%}
@@ -37,6 +39,19 @@ _rust_call(
{%- endfor %}
{%- endmacro -%}
+{%- macro docstring_value(maybe_docstring, indent_spaces) %}
+{%- match maybe_docstring %}
+{%- when Some(docstring) %}
+{{ docstring|docstring(indent_spaces) }}
+{{ "" }}
+{%- else %}
+{%- endmatch %}
+{%- endmacro %}
+
+{%- macro docstring(defn, indent_spaces) %}
+{%- call docstring_value(defn.docstring(), indent_spaces) %}
+{%- endmacro %}
+
{#-
// Arglist as used in Python declarations of methods, functions and constructors.
// Note the var_name and type_name filters.
@@ -76,6 +91,7 @@ _rust_call(
if {{ arg.name()|var_name }} is _DEFAULT:
{{ arg.name()|var_name }} = {{ literal|literal_py(arg.as_type().borrow()) }}
{%- endmatch %}
+ {{ arg|check_lower_fn }}({{ arg.name()|var_name }})
{% endfor -%}
{%- endmacro -%}
@@ -91,6 +107,7 @@ _rust_call(
if {{ arg.name()|var_name }} is _DEFAULT:
{{ arg.name()|var_name }} = {{ literal|literal_py(arg.as_type().borrow()) }}
{%- endmatch %}
+ {{ arg|check_lower_fn }}({{ arg.name()|var_name }})
{% endfor -%}
{%- endmacro -%}
@@ -100,11 +117,18 @@ _rust_call(
{%- macro method_decl(py_method_name, meth) %}
{% if meth.is_async() %}
- def {{ py_method_name }}(self, {% call arg_list_decl(meth) %}):
+{%- match meth.return_type() %}
+{%- when Some with (return_type) %}
+ async def {{ py_method_name }}(self, {% call arg_list_decl(meth) %}) -> "{{ return_type|type_name }}":
+{%- when None %}
+ async def {{ py_method_name }}(self, {% call arg_list_decl(meth) %}) -> None:
+{% endmatch %}
+
+ {%- call docstring(meth, 8) %}
{%- call setup_args_extra_indent(meth) %}
- return _uniffi_rust_call_async(
+ return await _uniffi_rust_call_async(
_UniffiLib.{{ meth.ffi_func().name() }}(
- self._pointer, {% call arg_list_lowered(meth) %}
+ self._uniffi_clone_pointer(), {% call arg_list_lowered(meth) %}
),
_UniffiLib.{{ meth.ffi_rust_future_poll(ci) }},
_UniffiLib.{{ meth.ffi_rust_future_complete(ci) }},
@@ -116,13 +140,7 @@ _rust_call(
{%- when None %}
lambda val: None,
{% endmatch %}
- # Error FFI converter
- {%- match meth.throws_type() %}
- {%- when Some(e) %}
- {{ e|ffi_converter_name }},
- {%- when None %}
- None,
- {%- endmatch %}
+ {% call error_ffi_converter(meth) %}
)
{%- else -%}
@@ -131,17 +149,36 @@ _rust_call(
{%- when Some with (return_type) %}
def {{ py_method_name }}(self, {% call arg_list_decl(meth) %}) -> "{{ return_type|type_name }}":
+ {%- call docstring(meth, 8) %}
{%- call setup_args_extra_indent(meth) %}
return {{ return_type|lift_fn }}(
- {% call to_ffi_call_with_prefix("self._pointer", meth) %}
+ {% call to_ffi_call_with_prefix("self._uniffi_clone_pointer()", meth) %}
)
{%- when None %}
- def {{ py_method_name }}(self, {% call arg_list_decl(meth) %}):
+ def {{ py_method_name }}(self, {% call arg_list_decl(meth) %}) -> None:
+ {%- call docstring(meth, 8) %}
{%- call setup_args_extra_indent(meth) %}
- {% call to_ffi_call_with_prefix("self._pointer", meth) %}
+ {% call to_ffi_call_with_prefix("self._uniffi_clone_pointer()", meth) %}
{% endmatch %}
{% endif %}
{% endmacro %}
+
+{%- macro error_ffi_converter(func) %}
+ # Error FFI converter
+{% match func.throws_type() %}
+{%- when Some(e) %}
+{%- match e -%}
+{%- when Type::Enum { name, module_path } -%}
+ {{ e|ffi_converter_name }},
+{%- when Type::Object { name, module_path, imp } -%}
+ {{ e|ffi_converter_name }}__as_error,
+{%- else %}
+ # unsupported error type!
+{%- endmatch %}
+{%- when None %}
+ None,
+{%- endmatch %}
+{% endmacro %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/wrapper.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/wrapper.py
index 24c3290ff7..1ccd6821c0 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/templates/wrapper.py
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/wrapper.py
@@ -1,3 +1,5 @@
+{%- call py::docstring_value(ci.namespace_docstring(), 0) %}
+
# This file was autogenerated by some hot garbage in the `uniffi` crate.
# Trust me, you don't want to mess with it!
@@ -13,6 +15,7 @@
# compile the rust component. The easiest way to ensure this is to bundle the Python
# helpers directly inline like we're doing here.
+from __future__ import annotations
import os
import sys
import ctypes
@@ -20,6 +23,9 @@ import enum
import struct
import contextlib
import datetime
+import threading
+import itertools
+import traceback
import typing
{%- if ci.has_async_fns() %}
import asyncio
@@ -34,20 +40,20 @@ _DEFAULT = object()
{% include "RustBufferTemplate.py" %}
{% include "Helpers.py" %}
-{% include "PointerManager.py" %}
+{% include "HandleMap.py" %}
{% include "RustBufferHelper.py" %}
# Contains loading, initialization code, and the FFI Function declarations.
{% include "NamespaceLibraryTemplate.py" %}
+# Public interface members begin here.
+{{ type_helper_code }}
+
# Async support
{%- if ci.has_async_fns() %}
{%- include "Async.py" %}
{%- endif %}
-# Public interface members begin here.
-{{ type_helper_code }}
-
{%- for func in ci.function_definitions() %}
{%- include "TopLevelFunctionTemplate.py" %}
{%- endfor %}
@@ -69,6 +75,9 @@ __all__ = [
{%- for c in ci.callback_interface_definitions() %}
"{{ c.name()|class_name }}",
{%- endfor %}
+ {%- if ci.has_async_fns() %}
+ "uniffi_set_event_loop",
+ {%- endif %}
]
{% import "macros.py" as py %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/test.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/test.rs
index 0fcf09996f..0c23140b33 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/python/test.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/test.rs
@@ -2,10 +2,8 @@
License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use crate::{
- bindings::{RunScriptOptions, TargetLanguage},
- library_mode::generate_bindings,
-};
+use crate::bindings::TargetLanguage;
+use crate::{bindings::RunScriptOptions, library_mode::generate_bindings, BindingGeneratorDefault};
use anyhow::{Context, Result};
use camino::Utf8Path;
use std::env;
@@ -34,14 +32,18 @@ pub fn run_script(
args: Vec<String>,
_options: &RunScriptOptions,
) -> Result<()> {
- let script_path = Utf8Path::new(".").join(script_file).canonicalize_utf8()?;
+ let script_path = Utf8Path::new(script_file).canonicalize_utf8()?;
let test_helper = UniFFITestHelper::new(crate_name)?;
let out_dir = test_helper.create_out_dir(tmp_dir, &script_path)?;
let cdylib_path = test_helper.copy_cdylib_to_out_dir(&out_dir)?;
generate_bindings(
&cdylib_path,
None,
- &[TargetLanguage::Python],
+ &BindingGeneratorDefault {
+ target_languages: vec![TargetLanguage::Python],
+ try_format_code: false,
+ },
+ None,
&out_dir,
false,
)?;