summaryrefslogtreecommitdiffstats
path: root/third_party/rust/uniffi_bindgen/src/bindings/python/templates
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/uniffi_bindgen/src/bindings/python/templates
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/uniffi_bindgen/src/bindings/python/templates')
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Async.py40
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/BooleanHelper.py16
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/BytesHelper.py16
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceRuntime.py77
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceTemplate.py105
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/CustomType.py58
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/DurationHelper.py21
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py97
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py70
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py9
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float32Helper.py8
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float64Helper.py8
-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/Helpers.py75
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int16Helper.py12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int32Helper.py12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int64Helper.py12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int8Helper.py12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/MapTemplate.py27
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py83
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py87
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/OptionalTemplate.py21
-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/RecordTemplate.py49
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferHelper.py59
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferTemplate.py211
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/SequenceTemplate.py19
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/StringHelper.py33
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/TimestampHelper.py32
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py38
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/Types.py102
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt16Helper.py12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt32Helper.py12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt64Helper.py12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt8Helper.py12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/macros.py147
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/templates/wrapper.py74
37 files changed, 1809 insertions, 0 deletions
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
new file mode 100644
index 0000000000..82aa534b46
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Async.py
@@ -0,0 +1,40 @@
+# RustFuturePoll values
+_UNIFFI_RUST_FUTURE_POLL_READY = 0
+_UNIFFI_RUST_FUTURE_POLL_MAYBE_READY = 1
+
+# Stores futures for _uniffi_continuation_callback
+_UniffiContinuationPointerManager = _UniffiPointerManager()
+
+# 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
+def _uniffi_continuation_callback(future_ptr, poll_code):
+ (eventloop, future) = _UniffiContinuationPointerManager.release_pointer(future_ptr)
+ eventloop.call_soon_threadsafe(_uniffi_set_future_result, future, poll_code)
+
+def _uniffi_set_future_result(future, poll_code):
+ if not future.cancelled():
+ future.set_result(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()
+
+ # 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)),
+ )
+ poll_code = await future
+ if poll_code == _UNIFFI_RUST_FUTURE_POLL_READY:
+ break
+
+ return lift_func(
+ _rust_call_with_error(error_ffi_converter, ffi_complete, rust_future)
+ )
+ finally:
+ ffi_free(rust_future)
+
+_UniffiLib.{{ ci.ffi_rust_future_continuation_callback_set().name() }}(_uniffi_continuation_callback)
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
new file mode 100644
index 0000000000..6775e9e132
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/BooleanHelper.py
@@ -0,0 +1,16 @@
+class _UniffiConverterBool(_UniffiConverterPrimitive):
+ @classmethod
+ def check(cls, value):
+ return not not value
+
+ @classmethod
+ def read(cls, buf):
+ return cls.lift(buf.read_u8())
+
+ @classmethod
+ def write_unchecked(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
new file mode 100644
index 0000000000..196b5b29fa
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/BytesHelper.py
@@ -0,0 +1,16 @@
+class _UniffiConverterBytes(_UniffiConverterRustBuffer):
+ @staticmethod
+ def read(buf):
+ size = buf.read_i32()
+ if size < 0:
+ raise InternalError("Unexpected negative byte string length")
+ return buf.read(size)
+
+ @staticmethod
+ def write(value, buf):
+ try:
+ memoryview(value)
+ except TypeError:
+ raise TypeError("a bytes-like object is required, not {!r}".format(type(value).__name__))
+ buf.write_i32(len(value))
+ buf.write(value)
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
new file mode 100644
index 0000000000..0fe2ab8dc0
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceRuntime.py
@@ -0,0 +1,77 @@
+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
+# Return codes for callback calls
+_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)
+
+ @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
+
+ @classmethod
+ def read(cls, buf):
+ handle = buf.read_u64()
+ cls.lift(handle)
+
+ @classmethod
+ def lower(cls, cb):
+ handle = cls._handle_map.insert(cb)
+ return handle
+
+ @classmethod
+ def write(cls, cb, buf):
+ buf.write_u64(cls.lower(cb))
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
new file mode 100644
index 0000000000..e0e926757a
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceTemplate.py
@@ -0,0 +1,105 @@
+{%- let cbi = ci|get_callback_interface_definition(id) %}
+{%- let foreign_callback = format!("foreignCallback{}", canonical_type_name) %}
+
+{% 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))
+
+# The _UniffiConverter which transforms the Callbacks in to Handles to pass to Rust.
+{{ ffi_converter_name }} = _UniffiConverterCallbackInterface({{ foreign_callback }})
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
new file mode 100644
index 0000000000..5be6155b84
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CustomType.py
@@ -0,0 +1,58 @@
+{%- match python_config.custom_types.get(name.as_str()) %}
+{% when None %}
+{#- No custom type config, just forward all methods to our builtin type #}
+# Type alias
+{{ name }} = {{ builtin|type_name }}
+
+class _UniffiConverterType{{ name }}:
+ @staticmethod
+ def write(value, buf):
+ {{ builtin|ffi_converter_name }}.write(value, buf)
+
+ @staticmethod
+ def read(buf):
+ return {{ builtin|ffi_converter_name }}.read(buf)
+
+ @staticmethod
+ def lift(value):
+ return {{ builtin|ffi_converter_name }}.lift(value)
+
+ @staticmethod
+ def lower(value):
+ return {{ builtin|ffi_converter_name }}.lower(value)
+
+{%- when Some(config) %}
+
+{%- match config.imports %}
+{%- when Some(imports) %}
+{%- for import_name in imports %}
+{{ self.add_import(import_name) }}
+{%- endfor %}
+{%- else %}
+{%- endmatch %}
+
+# Type alias
+{{ name }} = {{ builtin|type_name }}
+
+{#- Custom type config supplied, use it to convert the builtin type #}
+class _UniffiConverterType{{ name }}:
+ @staticmethod
+ def write(value, buf):
+ builtin_value = {{ config.from_custom.render("value") }}
+ {{ builtin|write_fn }}(builtin_value, buf)
+
+ @staticmethod
+ def read(buf):
+ builtin_value = {{ builtin|read_fn }}(buf)
+ return {{ config.into_custom.render("builtin_value") }}
+
+ @staticmethod
+ def lift(value):
+ builtin_value = {{ builtin|lift_fn }}(value)
+ return {{ config.into_custom.render("builtin_value") }}
+
+ @staticmethod
+ def lower(value):
+ builtin_value = {{ config.from_custom.render("value") }}
+ return {{ builtin|lower_fn }}(builtin_value)
+{%- endmatch %}
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
new file mode 100644
index 0000000000..10974e009d
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/DurationHelper.py
@@ -0,0 +1,21 @@
+# The Duration type.
+Duration = datetime.timedelta
+
+# There is a loss of precision when converting from Rust durations,
+# which are accurate to the nanosecond,
+# to Python durations, which are only accurate to the microsecond.
+class _UniffiConverterDuration(_UniffiConverterRustBuffer):
+ @staticmethod
+ def read(buf):
+ seconds = buf.read_u64()
+ microseconds = buf.read_u32() / 1.0e3
+ return datetime.timedelta(seconds=seconds, microseconds=microseconds)
+
+ @staticmethod
+ def write(value, buf):
+ seconds = value.seconds + value.days * 24 * 3600
+ nanoseconds = value.microseconds * 1000
+ if seconds < 0:
+ raise ValueError("Invalid duration, must be non-negative")
+ 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
new file mode 100644
index 0000000000..84d089baf9
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py
@@ -0,0 +1,97 @@
+{#
+# Python has a built-in `enum` module which is nice to use, but doesn't support
+# variants with associated data. So, we switch here, and generate a stdlib `enum`
+# when none of the variants have associated data, or a generic nested-class
+# construct when they do.
+#}
+{% if e.is_flat() %}
+
+class {{ type_name }}(enum.Enum):
+ {% for variant in e.variants() -%}
+ {{ variant.name()|enum_variant_py }} = {{ loop.index }}
+ {% endfor %}
+{% else %}
+
+class {{ type_name }}:
+ 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 }}";
+ {%- 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() %}
+ {%- for field in variant.fields() %}
+ self.{{ field.name()|var_name }} = {{ field.name()|var_name }}
+ {%- endfor %}
+ {% else %}
+ pass
+ {% 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 %})
+
+ def __eq__(self, other):
+ if not other.is_{{ variant.name()|var_name }}():
+ return False
+ {%- for field in variant.fields() %}
+ if self.{{ field.name()|var_name }} != other.{{ field.name()|var_name }}:
+ return False
+ {%- endfor %}
+ return True
+ {% endfor %}
+
+ # For each variant, we have an `is_NAME` method for easily checking
+ # whether an instance is that variant.
+ {% for variant in e.variants() -%}
+ def is_{{ variant.name()|var_name }}(self) -> bool:
+ return isinstance(self, {{ type_name }}.{{ variant.name()|enum_variant_py }})
+ {% endfor %}
+
+# Now, a little trick - we make each nested variant class be a subclass of the main
+# enum class, so that method calls and instance checks etc will work intuitively.
+# We might be able to do this a little more neatly with a metaclass, but this'll do.
+{% for variant in e.variants() -%}
+{{ type_name }}.{{ variant.name()|enum_variant_py }} = type("{{ type_name }}.{{ variant.name()|enum_variant_py }}", ({{ type_name }}.{{variant.name()|enum_variant_py}}, {{ type_name }},), {}) # type: ignore
+{% endfor %}
+
+{% endif %}
+
+class {{ ffi_converter_name }}(_UniffiConverterRustBuffer):
+ @staticmethod
+ def read(buf):
+ variant = buf.read_i32()
+
+ {%- for variant in e.variants() %}
+ if variant == {{ loop.index }}:
+ {%- if e.is_flat() %}
+ return {{ type_name }}.{{variant.name()|enum_variant_py}}
+ {%- else %}
+ return {{ type_name }}.{{variant.name()|enum_variant_py}}(
+ {%- for field in variant.fields() %}
+ {{ field|read_fn }}(buf),
+ {%- endfor %}
+ )
+ {%- endif %}
+ {%- endfor %}
+ raise InternalError("Raw enum value doesn't match any cases")
+
+ def write(value, buf):
+ {%- for variant in e.variants() %}
+ {%- if e.is_flat() %}
+ if value == {{ type_name }}.{{ variant.name()|enum_variant_py }}:
+ buf.write_i32({{ loop.index }})
+ {%- else %}
+ if value.is_{{ variant.name()|var_name }}():
+ buf.write_i32({{ loop.index }})
+ {%- for field in variant.fields() %}
+ {{ field|write_fn }}(value.{{ field.name()|var_name }}, buf)
+ {%- 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
new file mode 100644
index 0000000000..26a1e6452a
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py
@@ -0,0 +1,70 @@
+# {{ type_name }}
+# We want to define each variant as a nested class that's also a subclass,
+# which is tricky in Python. To accomplish this we're going to create each
+# class separately, then manually add the child classes to the base class's
+# __dict__. All of this happens in dummy class to avoid polluting the module
+# namespace.
+class {{ type_name }}(Exception):
+ pass
+
+_UniffiTemp{{ type_name }} = {{ type_name }}
+
+class {{ type_name }}: # type: ignore
+ {%- for variant in e.variants() -%}
+ {%- let variant_type_name = variant.name()|class_name -%}
+ {%- if e.is_flat() %}
+ class {{ variant_type_name }}(_UniffiTemp{{ type_name }}):
+ def __repr__(self):
+ return "{{ type_name }}.{{ variant_type_name }}({})".format(repr(str(self)))
+ {%- else %}
+ class {{ variant_type_name }}(_UniffiTemp{{ type_name }}):
+ def __init__(self{% for field in variant.fields() %}, {{ field.name()|var_name }}{% endfor %}):
+ {%- if variant.has_fields() %}
+ super().__init__(", ".join([
+ {%- for field in variant.fields() %}
+ "{{ field.name()|var_name }}={!r}".format({{ field.name()|var_name }}),
+ {%- endfor %}
+ ]))
+ {%- for field in variant.fields() %}
+ self.{{ field.name()|var_name }} = {{ field.name()|var_name }}
+ {%- endfor %}
+ {%- else %}
+ pass
+ {%- endif %}
+ def __repr__(self):
+ return "{{ type_name }}.{{ variant_type_name }}({})".format(str(self))
+ {%- endif %}
+ _UniffiTemp{{ type_name }}.{{ variant_type_name }} = {{ variant_type_name }} # type: ignore
+ {%- endfor %}
+
+{{ type_name }} = _UniffiTemp{{ type_name }} # type: ignore
+del _UniffiTemp{{ type_name }}
+
+
+class {{ ffi_converter_name }}(_UniffiConverterRustBuffer):
+ @staticmethod
+ def read(buf):
+ variant = buf.read_i32()
+ {%- for variant in e.variants() %}
+ if variant == {{ loop.index }}:
+ return {{ type_name }}.{{ variant.name()|class_name }}(
+ {%- if e.is_flat() %}
+ {{ Type::String.borrow()|read_fn }}(buf),
+ {%- else %}
+ {%- for field in variant.fields() %}
+ {{ field.name()|var_name }}={{ field|read_fn }}(buf),
+ {%- endfor %}
+ {%- endif %}
+ )
+ {%- endfor %}
+ raise InternalError("Raw enum value doesn't match any cases")
+
+ @staticmethod
+ def write(value, buf):
+ {%- for variant in e.variants() %}
+ if isinstance(value, {{ type_name }}.{{ variant.name()|class_name }}):
+ buf.write_i32({{ loop.index }})
+ {%- for field in variant.fields() %}
+ {{ field|write_fn }}(value.{{ field.name()|var_name }}, buf)
+ {%- endfor %}
+ {%- endfor %}
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
new file mode 100644
index 0000000000..71e05e8b06
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py
@@ -0,0 +1,9 @@
+{%- let ns = namespace|fn_name %}
+
+# 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 -#}
+
+{%- let rustbuffer_local_name = "_UniffiRustBuffer{}"|format(name) %}
+{{ self.add_import_of_as(ns, "_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
new file mode 100644
index 0000000000..a52107a638
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float32Helper.py
@@ -0,0 +1,8 @@
+class _UniffiConverterFloat(_UniffiConverterPrimitiveFloat):
+ @staticmethod
+ def read(buf):
+ return buf.read_float()
+
+ @staticmethod
+ def write_unchecked(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
new file mode 100644
index 0000000000..772f5080e9
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Float64Helper.py
@@ -0,0 +1,8 @@
+class _UniffiConverterDouble(_UniffiConverterPrimitiveFloat):
+ @staticmethod
+ def read(buf):
+ return buf.read_double()
+
+ @staticmethod
+ def write_unchecked(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
new file mode 100644
index 0000000000..6a6932fed0
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ForeignExecutorTemplate.py
@@ -0,0 +1,63 @@
+# 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/Helpers.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Helpers.py
new file mode 100644
index 0000000000..dca962f176
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Helpers.py
@@ -0,0 +1,75 @@
+# A handful of classes and functions to support the generated data structures.
+# This would be a good candidate for isolating in its own ffi-support lib.
+
+class InternalError(Exception):
+ pass
+
+class _UniffiRustCallStatus(ctypes.Structure):
+ """
+ Error runtime.
+ """
+ _fields_ = [
+ ("code", ctypes.c_int8),
+ ("error_buf", _UniffiRustBuffer),
+ ]
+
+ # These match the values from the uniffi::rustcalls module
+ CALL_SUCCESS = 0
+ CALL_ERROR = 1
+ CALL_PANIC = 2
+
+ 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)"
+ else:
+ return "_UniffiRustCallStatus(<invalid code>)"
+
+def _rust_call(fn, *args):
+ # Call a rust function
+ return _rust_call_with_error(None, fn, *args)
+
+def _rust_call_with_error(error_ffi_converter, fn, *args):
+ # Call a rust function and handle any errors
+ #
+ # 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))
+
+ args_with_error = args + (ctypes.byref(call_status),)
+ result = fn(*args_with_error)
+ _uniffi_check_call_status(error_ffi_converter, call_status)
+ return result
+
+def _uniffi_check_call_status(error_ffi_converter, call_status):
+ if call_status.code == _UniffiRustCallStatus.CALL_SUCCESS:
+ pass
+ elif call_status.code == _UniffiRustCallStatus.CALL_ERROR:
+ if error_ffi_converter is None:
+ call_status.error_buf.free()
+ 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:
+ # 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.
+ if call_status.error_buf.len > 0:
+ msg = _UniffiConverterString.lift(call_status.error_buf)
+ else:
+ msg = "Unknown rust panic"
+ raise InternalError(msg)
+ else:
+ 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)
+
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
new file mode 100644
index 0000000000..99f19dc1c0
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int16Helper.py
@@ -0,0 +1,12 @@
+class _UniffiConverterInt16(_UniffiConverterPrimitiveInt):
+ CLASS_NAME = "i16"
+ VALUE_MIN = -2**15
+ VALUE_MAX = 2**15
+
+ @staticmethod
+ def read(buf):
+ return buf.read_i16()
+
+ @staticmethod
+ def write_unchecked(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
new file mode 100644
index 0000000000..3b142c8749
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int32Helper.py
@@ -0,0 +1,12 @@
+class _UniffiConverterInt32(_UniffiConverterPrimitiveInt):
+ CLASS_NAME = "i32"
+ VALUE_MIN = -2**31
+ VALUE_MAX = 2**31
+
+ @staticmethod
+ def read(buf):
+ return buf.read_i32()
+
+ @staticmethod
+ def write_unchecked(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
new file mode 100644
index 0000000000..6e94379cbf
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int64Helper.py
@@ -0,0 +1,12 @@
+class _UniffiConverterInt64(_UniffiConverterPrimitiveInt):
+ CLASS_NAME = "i64"
+ VALUE_MIN = -2**63
+ VALUE_MAX = 2**63
+
+ @staticmethod
+ def read(buf):
+ return buf.read_i64()
+
+ @staticmethod
+ def write_unchecked(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
new file mode 100644
index 0000000000..732530e3cb
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Int8Helper.py
@@ -0,0 +1,12 @@
+class _UniffiConverterInt8(_UniffiConverterPrimitiveInt):
+ CLASS_NAME = "i8"
+ VALUE_MIN = -2**7
+ VALUE_MAX = 2**7
+
+ @staticmethod
+ def read(buf):
+ return buf.read_i8()
+
+ @staticmethod
+ def write_unchecked(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
new file mode 100644
index 0000000000..387227ed09
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/MapTemplate.py
@@ -0,0 +1,27 @@
+{%- let key_ffi_converter = key_type|ffi_converter_name %}
+{%- let value_ffi_converter = value_type|ffi_converter_name %}
+
+class {{ ffi_converter_name }}(_UniffiConverterRustBuffer):
+ @classmethod
+ def write(cls, items, buf):
+ buf.write_i32(len(items))
+ for (key, value) in items.items():
+ {{ key_ffi_converter }}.write(key, buf)
+ {{ value_ffi_converter }}.write(value, buf)
+
+ @classmethod
+ def read(cls, buf):
+ count = buf.read_i32()
+ if count < 0:
+ raise InternalError("Unexpected negative map size")
+
+ # It would be nice to use a dict comprehension,
+ # but in Python 3.7 and before the evaluation order is not according to spec,
+ # so we we're reading the value before the key.
+ # This loop makes the order explicit: first reading the key, then the value.
+ d = {}
+ for i in range(count):
+ key = {{ key_ffi_converter }}.read(buf)
+ val = {{ value_ffi_converter }}.read(buf)
+ d[key] = val
+ return d
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
new file mode 100644
index 0000000000..fac6cd5564
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py
@@ -0,0 +1,83 @@
+# 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)
+
+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)
+
+def _uniffi_load_indirect():
+ """
+ This is how we find and load the dynamic library provided by the component.
+ For now we just look it up by name.
+ """
+ if sys.platform == "darwin":
+ libname = "lib{}.dylib"
+ elif sys.platform.startswith("win"):
+ # As of python3.8, ctypes does not seem to search $PATH when loading DLLs.
+ # We could use `os.add_dll_directory` to configure the search path, but
+ # it doesn't feel right to mess with application-wide settings. Let's
+ # assume that the `.dll` is next to the `.py` file and load by full path.
+ libname = os.path.join(
+ os.path.dirname(__file__),
+ "{}.dll",
+ )
+ else:
+ # Anything else must be an ELF platform - Linux, *BSD, Solaris/illumos
+ libname = "lib{}.so"
+
+ libname = libname.format("{{ config.cdylib_name() }}")
+ path = os.path.join(os.path.dirname(__file__), libname)
+ lib = ctypes.cdll.LoadLibrary(path)
+ return lib
+
+def _uniffi_check_contract_api_version(lib):
+ # Get the bindings contract version from our ComponentInterface
+ bindings_contract_version = {{ ci.uniffi_contract_version() }}
+ # Get the scaffolding contract version by calling the into the dylib
+ scaffolding_contract_version = lib.{{ ci.ffi_uniffi_contract_version().name() }}()
+ if bindings_contract_version != scaffolding_contract_version:
+ raise InternalError("UniFFI contract version mismatch: try cleaning and rebuilding your project")
+
+def _uniffi_check_api_checksums(lib):
+ {%- for (name, expected_checksum) in ci.iter_checksums() %}
+ if lib.{{ name }}() != {{ expected_checksum }}:
+ raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ {%- else %}
+ pass
+ {%- endfor %}
+
+# A ctypes library to expose the extern-C FFI definitions.
+# 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() %}
+_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 %}
+{%- 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
new file mode 100644
index 0000000000..7e98f7c46f
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py
@@ -0,0 +1,87 @@
+{%- let obj = ci|get_object_definition(name) %}
+
+class {{ type_name }}:
+ _pointer: ctypes.c_void_p
+
+{%- match obj.primary_constructor() %}
+{%- when Some with (cons) %}
+ def __init__(self, {% call py::arg_list_decl(cons) -%}):
+ {%- call py::setup_args_extra_indent(cons) %}
+ self._pointer = {% call py::to_ffi_call(cons) %}
+{%- when None %}
+{%- endmatch %}
+
+ def __del__(self):
+ # In case of partial initialization of instances.
+ pointer = getattr(self, "_pointer", None)
+ if pointer is not None:
+ _rust_call(_UniffiLib.{{ obj.ffi_object_free().name() }}, pointer)
+
+ # Used by alternative constructors or any methods which return this type.
+ @classmethod
+ def _make_instance_(cls, pointer):
+ # Lightly yucky way to bypass the usual __init__ logic
+ # and just create a new instance with the required pointer.
+ inst = cls.__new__(cls)
+ inst._pointer = pointer
+ return inst
+
+{%- for cons in obj.alternate_constructors() %}
+
+ @classmethod
+ def {{ cons.name()|fn_name }}(cls, {% call py::arg_list_decl(cons) %}):
+ {%- 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)
+{% endfor %}
+
+{%- for meth in obj.methods() -%}
+ {%- call py::method_decl(meth.name()|fn_name, meth) %}
+{% endfor %}
+
+{%- for tm in obj.uniffi_traits() -%}
+{%- match tm %}
+{%- when UniffiTrait::Debug { fmt } %}
+ {%- call py::method_decl("__repr__", fmt) %}
+{%- when UniffiTrait::Display { fmt } %}
+ {%- call py::method_decl("__str__", fmt) %}
+{%- when UniffiTrait::Eq { eq, ne } %}
+ def __eq__(self, other: object) -> {{ eq.return_type().unwrap()|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) %})
+
+ 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) %})
+{%- when UniffiTrait::Hash { hash } %}
+ {%- call py::method_decl("__hash__", hash) %}
+{% endmatch %}
+{% endfor %}
+
+
+class {{ ffi_converter_name }}:
+ @classmethod
+ def read(cls, buf):
+ ptr = buf.read_u64()
+ if ptr == 0:
+ raise InternalError("Raw pointer value was null")
+ return cls.lift(ptr)
+
+ @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))
+
+ @staticmethod
+ def lift(value):
+ return {{ type_name }}._make_instance_(value)
+
+ @staticmethod
+ def lower(value):
+ return value._pointer
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
new file mode 100644
index 0000000000..e406c51d49
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/OptionalTemplate.py
@@ -0,0 +1,21 @@
+{%- let inner_ffi_converter = inner_type|ffi_converter_name %}
+
+class {{ ffi_converter_name }}(_UniffiConverterRustBuffer):
+ @classmethod
+ def write(cls, value, buf):
+ if value is None:
+ buf.write_u8(0)
+ return
+
+ buf.write_u8(1)
+ {{ inner_ffi_converter }}.write(value, buf)
+
+ @classmethod
+ def read(cls, buf):
+ flag = buf.read_u8()
+ if flag == 0:
+ return None
+ elif flag == 1:
+ return {{ inner_ffi_converter }}.read(buf)
+ else:
+ raise InternalError("Unexpected flag byte for optional type")
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
new file mode 100644
index 0000000000..23aa28eab4
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/PointerManager.py
@@ -0,0 +1,68 @@
+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/RecordTemplate.py b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py
new file mode 100644
index 0000000000..99a30e120f
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py
@@ -0,0 +1,49 @@
+{%- let rec = ci|get_record_definition(name) %}
+class {{ type_name }}:
+ {% for field in rec.fields() %}
+ {{- field.name()|var_name }}: "{{- field|type_name }}";
+ {%- endfor %}
+
+ @typing.no_type_check
+ 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 %}
+ {%- endfor %}):
+ {%- for field in rec.fields() %}
+ {%- let field_name = field.name()|var_name %}
+ {%- match field.default_value() %}
+ {%- when None %}
+ self.{{ field_name }} = {{ field_name }}
+ {%- when Some with(literal) %}
+ if {{ field_name }} is _DEFAULT:
+ self.{{ field_name }} = {{ literal|literal_py(field) }}
+ else:
+ self.{{ field_name }} = {{ field_name }}
+ {%- endmatch %}
+ {%- endfor %}
+
+ 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 %})
+
+ def __eq__(self, other):
+ {%- for field in rec.fields() %}
+ if self.{{ field.name()|var_name }} != other.{{ field.name()|var_name }}:
+ return False
+ {%- endfor %}
+ return True
+
+class {{ ffi_converter_name }}(_UniffiConverterRustBuffer):
+ @staticmethod
+ def read(buf):
+ return {{ type_name }}(
+ {%- for field in rec.fields() %}
+ {{ field.name()|var_name }}={{ field|read_fn }}(buf),
+ {%- endfor %}
+ )
+
+ @staticmethod
+ def write(value, buf):
+ {%- for field in rec.fields() %}
+ {{ field|write_fn }}(value.{{ field.name()|var_name }}, buf)
+ {%- endfor %}
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
new file mode 100644
index 0000000000..daabd5b4b9
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferHelper.py
@@ -0,0 +1,59 @@
+# 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):
+ try:
+ value = value.__index__()
+ except Exception:
+ raise TypeError("'{}' object cannot be interpreted as an integer".format(type(value).__name__))
+ if not isinstance(value, int):
+ 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):
+ 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.
+class _UniffiConverterRustBuffer:
+ @classmethod
+ def lift(cls, rbuf):
+ with rbuf.consume_with_stream() as stream:
+ return cls.read(stream)
+
+ @classmethod
+ def lower(cls, value):
+ with _UniffiRustBuffer.alloc_with_builder() as builder:
+ cls.write(value, builder)
+ return builder.finalize()
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
new file mode 100644
index 0000000000..c317a632fc
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/RustBufferTemplate.py
@@ -0,0 +1,211 @@
+
+class _UniffiRustBuffer(ctypes.Structure):
+ _fields_ = [
+ ("capacity", ctypes.c_int32),
+ ("len", ctypes.c_int32),
+ ("data", ctypes.POINTER(ctypes.c_char)),
+ ]
+
+ @staticmethod
+ def alloc(size):
+ return _rust_call(_UniffiLib.{{ ci.ffi_rustbuffer_alloc().name() }}, size)
+
+ @staticmethod
+ def reserve(rbuf, additional):
+ return _rust_call(_UniffiLib.{{ ci.ffi_rustbuffer_reserve().name() }}, rbuf, additional)
+
+ def free(self):
+ return _rust_call(_UniffiLib.{{ ci.ffi_rustbuffer_free().name() }}, self)
+
+ def __str__(self):
+ return "_UniffiRustBuffer(capacity={}, len={}, data={})".format(
+ self.capacity,
+ self.len,
+ self.data[0:self.len]
+ )
+
+ @contextlib.contextmanager
+ def alloc_with_builder(*args):
+ """Context-manger to allocate a buffer using a _UniffiRustBufferBuilder.
+
+ The allocated buffer will be automatically freed if an error occurs, ensuring that
+ we don't accidentally leak it.
+ """
+ builder = _UniffiRustBufferBuilder()
+ try:
+ yield builder
+ except:
+ builder.discard()
+ raise
+
+ @contextlib.contextmanager
+ def consume_with_stream(self):
+ """Context-manager to consume a buffer using a _UniffiRustBufferStream.
+
+ The _UniffiRustBuffer will be freed once the context-manager exits, ensuring that we don't
+ leak it even if an error occurs.
+ """
+ try:
+ s = _UniffiRustBufferStream.from_rust_buffer(self)
+ yield s
+ if s.remaining() != 0:
+ raise RuntimeError("junk data left in buffer at end of consume_with_stream")
+ finally:
+ self.free()
+
+ @contextlib.contextmanager
+ def read_with_stream(self):
+ """Context-manager to read a buffer using a _UniffiRustBufferStream.
+
+ This is like consume_with_stream, but doesn't free the buffer afterwards.
+ It should only be used with borrowed `_UniffiRustBuffer` data.
+ """
+ s = _UniffiRustBufferStream.from_rust_buffer(self)
+ yield s
+ if s.remaining() != 0:
+ raise RuntimeError("junk data left in buffer at end of read_with_stream")
+
+class _UniffiForeignBytes(ctypes.Structure):
+ _fields_ = [
+ ("len", ctypes.c_int32),
+ ("data", ctypes.POINTER(ctypes.c_char)),
+ ]
+
+ def __str__(self):
+ return "_UniffiForeignBytes(len={}, data={})".format(self.len, self.data[0:self.len])
+
+
+class _UniffiRustBufferStream:
+ """
+ Helper for structured reading of bytes from a _UniffiRustBuffer
+ """
+
+ def __init__(self, data, len):
+ self.data = data
+ self.len = len
+ self.offset = 0
+
+ @classmethod
+ def from_rust_buffer(cls, buf):
+ return cls(buf.data, buf.len)
+
+ def remaining(self):
+ return self.len - self.offset
+
+ def _unpack_from(self, size, format):
+ if self.offset + size > self.len:
+ raise InternalError("read past end of rust buffer")
+ value = struct.unpack(format, self.data[self.offset:self.offset+size])[0]
+ self.offset += size
+ return value
+
+ def read(self, size):
+ if self.offset + size > self.len:
+ raise InternalError("read past end of rust buffer")
+ data = self.data[self.offset:self.offset+size]
+ self.offset += size
+ return data
+
+ def read_i8(self):
+ return self._unpack_from(1, ">b")
+
+ def read_u8(self):
+ return self._unpack_from(1, ">B")
+
+ def read_i16(self):
+ return self._unpack_from(2, ">h")
+
+ def read_u16(self):
+ return self._unpack_from(2, ">H")
+
+ def read_i32(self):
+ return self._unpack_from(4, ">i")
+
+ def read_u32(self):
+ return self._unpack_from(4, ">I")
+
+ def read_i64(self):
+ return self._unpack_from(8, ">q")
+
+ def read_u64(self):
+ return self._unpack_from(8, ">Q")
+
+ def read_float(self):
+ v = self._unpack_from(4, ">f")
+ return v
+
+ 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.
+ """
+
+ def __init__(self):
+ self.rbuf = _UniffiRustBuffer.alloc(16)
+ self.rbuf.len = 0
+
+ def finalize(self):
+ rbuf = self.rbuf
+ self.rbuf = None
+ return rbuf
+
+ def discard(self):
+ if self.rbuf is not None:
+ rbuf = self.finalize()
+ rbuf.free()
+
+ @contextlib.contextmanager
+ def _reserve(self, num_bytes):
+ if self.rbuf.len + num_bytes > self.rbuf.capacity:
+ self.rbuf = _UniffiRustBuffer.reserve(self.rbuf, num_bytes)
+ yield None
+ self.rbuf.len += num_bytes
+
+ def _pack_into(self, size, format, value):
+ with self._reserve(size):
+ # XXX TODO: I feel like I should be able to use `struct.pack_into` here but can't figure it out.
+ for i, byte in enumerate(struct.pack(format, value)):
+ self.rbuf.data[self.rbuf.len + i] = byte
+
+ def write(self, value):
+ with self._reserve(len(value)):
+ for i, byte in enumerate(value):
+ self.rbuf.data[self.rbuf.len + i] = byte
+
+ def write_i8(self, v):
+ self._pack_into(1, ">b", v)
+
+ def write_u8(self, v):
+ self._pack_into(1, ">B", v)
+
+ def write_i16(self, v):
+ self._pack_into(2, ">h", v)
+
+ def write_u16(self, v):
+ self._pack_into(2, ">H", v)
+
+ def write_i32(self, v):
+ self._pack_into(4, ">i", v)
+
+ def write_u32(self, v):
+ self._pack_into(4, ">I", v)
+
+ def write_i64(self, v):
+ self._pack_into(8, ">q", v)
+
+ def write_u64(self, v):
+ self._pack_into(8, ">Q", v)
+
+ def write_float(self, v):
+ self._pack_into(4, ">f", v)
+
+ def write_double(self, v):
+ self._pack_into(8, ">d", v)
+
+ def write_c_size_t(self, v):
+ self._pack_into(ctypes.sizeof(ctypes.c_size_t) , "@N", v)
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
new file mode 100644
index 0000000000..3c9f5a4596
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/SequenceTemplate.py
@@ -0,0 +1,19 @@
+{%- let inner_ffi_converter = inner_type|ffi_converter_name %}
+
+class {{ ffi_converter_name}}(_UniffiConverterRustBuffer):
+ @classmethod
+ def write(cls, value, buf):
+ items = len(value)
+ buf.write_i32(items)
+ for item in value:
+ {{ inner_ffi_converter }}.write(item, buf)
+
+ @classmethod
+ def read(cls, buf):
+ count = buf.read_i32()
+ if count < 0:
+ raise InternalError("Unexpected negative sequence length")
+
+ return [
+ {{ inner_ffi_converter }}.read(buf) for i in range(count)
+ ]
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
new file mode 100644
index 0000000000..40890b6abc
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/StringHelper.py
@@ -0,0 +1,33 @@
+class _UniffiConverterString:
+ @staticmethod
+ def check(value):
+ if not isinstance(value, str):
+ raise TypeError("argument must be str, not {}".format(type(value).__name__))
+ return value
+
+ @staticmethod
+ def read(buf):
+ size = buf.read_i32()
+ if size < 0:
+ raise InternalError("Unexpected negative string length")
+ utf8_bytes = buf.read(size)
+ return utf8_bytes.decode("utf-8")
+
+ @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)
+
+ @staticmethod
+ def lift(buf):
+ with buf.consume_with_stream() as stream:
+ return stream.read(stream.remaining()).decode("utf-8")
+
+ @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
new file mode 100644
index 0000000000..8402f6095d
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/TimestampHelper.py
@@ -0,0 +1,32 @@
+# The Timestamp type.
+Timestamp = datetime.datetime
+
+# There is a loss of precision when converting from Rust timestamps,
+# which are accurate to the nanosecond,
+# to Python datetimes, which have a variable precision due to the use of float as representation.
+class _UniffiConverterTimestamp(_UniffiConverterRustBuffer):
+ @staticmethod
+ def read(buf):
+ seconds = buf.read_i64()
+ microseconds = buf.read_u32() / 1000
+ # Use fromtimestamp(0) then add the seconds using a timedelta. This
+ # ensures that we get OverflowError rather than ValueError when
+ # seconds is too large.
+ if seconds >= 0:
+ return datetime.datetime.fromtimestamp(0, tz=datetime.timezone.utc) + datetime.timedelta(seconds=seconds, microseconds=microseconds)
+ else:
+ return datetime.datetime.fromtimestamp(0, tz=datetime.timezone.utc) - datetime.timedelta(seconds=-seconds, microseconds=microseconds)
+
+ @staticmethod
+ def write(value, buf):
+ if value >= datetime.datetime.fromtimestamp(0, datetime.timezone.utc):
+ sign = 1
+ delta = value - datetime.datetime.fromtimestamp(0, datetime.timezone.utc)
+ else:
+ sign = -1
+ delta = datetime.datetime.fromtimestamp(0, datetime.timezone.utc) - value
+
+ seconds = delta.seconds + delta.days * 24 * 3600
+ nanoseconds = delta.microseconds * 1000
+ buf.write_i64(sign * seconds)
+ buf.write_u32(nanoseconds)
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
new file mode 100644
index 0000000000..f258b60a1c
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py
@@ -0,0 +1,38 @@
+{%- if func.is_async() %}
+
+def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}):
+ return _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) }},
+ _UniffiLib.{{func.ffi_rust_future_free(ci) }},
+ # lift function
+ {%- match func.return_type() %}
+ {%- when Some(return_type) %}
+ {{ return_type|lift_fn }},
+ {%- when None %}
+ lambda val: None,
+ {% endmatch %}
+ # Error FFI converter
+ {%- match func.throws_type() %}
+ {%- when Some(e) %}
+ {{ e|ffi_converter_name }},
+ {%- when None %}
+ None,
+ {%- endmatch %}
+ )
+
+{%- else %}
+{%- match func.return_type() -%}
+{%- when Some with (return_type) %}
+
+def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}) -> "{{ return_type|type_name }}":
+ {%- 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) -%}):
+ {%- call py::setup_args(func) %}
+ {% call py::to_ffi_call(func) %}
+{% endmatch %}
+{%- endif %}
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
new file mode 100644
index 0000000000..5e05314c37
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/Types.py
@@ -0,0 +1,102 @@
+{%- import "macros.py" as py %}
+
+{%- for type_ in ci.iter_types() %}
+{%- let type_name = type_|type_name %}
+{%- let ffi_converter_name = type_|ffi_converter_name %}
+{%- let canonical_type_name = type_|canonical_name %}
+
+{#
+ # Map `Type` instances to an include statement for that type.
+ #
+ # There is a companion match in `PythonCodeOracle::create_code_type()` which performs a similar function for the
+ # Rust code.
+ #
+ # - When adding additional types here, make sure to also add a match arm to that function.
+ # - To keep things manageable, let's try to limit ourselves to these 2 mega-matches
+ #}
+{%- match type_ %}
+
+{%- when Type::Boolean %}
+{%- include "BooleanHelper.py" %}
+
+{%- when Type::Int8 %}
+{%- include "Int8Helper.py" %}
+
+{%- when Type::Int16 %}
+{%- include "Int16Helper.py" %}
+
+{%- when Type::Int32 %}
+{%- include "Int32Helper.py" %}
+
+{%- when Type::Int64 %}
+{%- include "Int64Helper.py" %}
+
+{%- when Type::UInt8 %}
+{%- include "UInt8Helper.py" %}
+
+{%- when Type::UInt16 %}
+{%- include "UInt16Helper.py" %}
+
+{%- when Type::UInt32 %}
+{%- include "UInt32Helper.py" %}
+
+{%- when Type::UInt64 %}
+{%- include "UInt64Helper.py" %}
+
+{%- when Type::Float32 %}
+{%- include "Float32Helper.py" %}
+
+{%- when Type::Float64 %}
+{%- include "Float64Helper.py" %}
+
+{%- when Type::String %}
+{%- include "StringHelper.py" %}
+
+{%- when Type::Bytes %}
+{%- include "BytesHelper.py" %}
+
+{%- when Type::Enum { name, module_path } %}
+{%- let e = ci.get_enum_definition(name).unwrap() %}
+{# For enums, there are either an error *or* an enum, they can't be both. #}
+{%- if ci.is_name_used_as_error(name) %}
+{%- include "ErrorTemplate.py" %}
+{%- else %}
+{%- include "EnumTemplate.py" %}
+{% endif %}
+
+{%- when Type::Record { name, module_path } %}
+{%- include "RecordTemplate.py" %}
+
+{%- when Type::Object { name, module_path, imp } %}
+{%- include "ObjectTemplate.py" %}
+
+{%- when Type::Timestamp %}
+{%- include "TimestampHelper.py" %}
+
+{%- when Type::Duration %}
+{%- include "DurationHelper.py" %}
+
+{%- when Type::Optional { inner_type } %}
+{%- include "OptionalTemplate.py" %}
+
+{%- when Type::Sequence { inner_type } %}
+{%- include "SequenceTemplate.py" %}
+
+{%- when Type::Map { key_type, value_type } %}
+{%- include "MapTemplate.py" %}
+
+{%- when Type::CallbackInterface { name: id, module_path } %}
+{%- include "CallbackInterfaceTemplate.py" %}
+
+{%- when Type::Custom { name, module_path, builtin } %}
+{%- include "CustomType.py" %}
+
+{%- 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
new file mode 100644
index 0000000000..081c6731ce
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt16Helper.py
@@ -0,0 +1,12 @@
+class _UniffiConverterUInt16(_UniffiConverterPrimitiveInt):
+ CLASS_NAME = "u16"
+ VALUE_MIN = 0
+ VALUE_MAX = 2**16
+
+ @staticmethod
+ def read(buf):
+ return buf.read_u16()
+
+ @staticmethod
+ def write_unchecked(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
new file mode 100644
index 0000000000..b80e75177d
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt32Helper.py
@@ -0,0 +1,12 @@
+class _UniffiConverterUInt32(_UniffiConverterPrimitiveInt):
+ CLASS_NAME = "u32"
+ VALUE_MIN = 0
+ VALUE_MAX = 2**32
+
+ @staticmethod
+ def read(buf):
+ return buf.read_u32()
+
+ @staticmethod
+ def write_unchecked(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
new file mode 100644
index 0000000000..4b87e58547
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt64Helper.py
@@ -0,0 +1,12 @@
+class _UniffiConverterUInt64(_UniffiConverterPrimitiveInt):
+ CLASS_NAME = "u64"
+ VALUE_MIN = 0
+ VALUE_MAX = 2**64
+
+ @staticmethod
+ def read(buf):
+ return buf.read_u64()
+
+ @staticmethod
+ def write_unchecked(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
new file mode 100644
index 0000000000..33026706f2
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/UInt8Helper.py
@@ -0,0 +1,12 @@
+class _UniffiConverterUInt8(_UniffiConverterPrimitiveInt):
+ CLASS_NAME = "u8"
+ VALUE_MIN = 0
+ VALUE_MAX = 2**8
+
+ @staticmethod
+ def read(buf):
+ return buf.read_u8()
+
+ @staticmethod
+ def write_unchecked(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
new file mode 100644
index 0000000000..ef3b1bb94d
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/macros.py
@@ -0,0 +1,147 @@
+{#
+// Template to call into rust. Used in several places.
+// Variable names in `arg_list_decl` should match up with arg lists
+// passed to rust via `arg_list_lowered`
+#}
+
+{%- 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) -%}
+)
+{%- 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 -%}
+_rust_call(
+ {%- endmatch -%}
+ _UniffiLib.{{ func.ffi_func().name() }},
+ {{- prefix }},
+ {%- call arg_list_lowered(func) -%}
+)
+{%- endmacro -%}
+
+{%- macro arg_list_lowered(func) %}
+ {%- for arg in func.arguments() %}
+ {{ arg|lower_fn }}({{ arg.name()|var_name }})
+ {%- if !loop.last %},{% endif %}
+ {%- endfor %}
+{%- endmacro -%}
+
+{#-
+// Arglist as used in Python declarations of methods, functions and constructors.
+// Note the var_name and type_name filters.
+-#}
+
+{% macro arg_list_decl(func) %}
+ {%- for arg in func.arguments() -%}
+ {{ arg.name()|var_name }}
+ {%- match arg.default_value() %}
+ {%- when Some with(literal) %}: "typing.Union[object, {{ arg|type_name -}}]" = _DEFAULT
+ {%- else %}: "{{ arg|type_name -}}"
+ {%- endmatch %}
+ {%- if !loop.last %},{% endif -%}
+ {%- endfor %}
+{%- endmacro %}
+
+{#-
+// Arglist as used in the _UniffiLib function declarations.
+// Note unfiltered name but ffi_type_name filters.
+-#}
+{%- macro arg_list_ffi_decl(func) %}
+ {%- for arg in func.arguments() %}
+ {{ arg.type_().borrow()|ffi_type_name }},
+ {%- endfor %}
+ {%- if func.has_rust_call_status_arg() %}
+ ctypes.POINTER(_UniffiRustCallStatus),{% endif %}
+{% endmacro -%}
+
+{#
+ # Setup function arguments by initializing default values.
+ #}
+{%- macro setup_args(func) %}
+ {%- for arg in func.arguments() %}
+ {%- match arg.default_value() %}
+ {%- when None %}
+ {%- when Some with(literal) %}
+ if {{ arg.name()|var_name }} is _DEFAULT:
+ {{ arg.name()|var_name }} = {{ literal|literal_py(arg.as_type().borrow()) }}
+ {%- endmatch %}
+ {% endfor -%}
+{%- endmacro -%}
+
+{#
+ # Exactly the same thing as `setup_args()` but with an extra 4 spaces of
+ # indent so that it works with object methods.
+ #}
+{%- macro setup_args_extra_indent(func) %}
+ {%- for arg in func.arguments() %}
+ {%- match arg.default_value() %}
+ {%- when None %}
+ {%- when Some with(literal) %}
+ if {{ arg.name()|var_name }} is _DEFAULT:
+ {{ arg.name()|var_name }} = {{ literal|literal_py(arg.as_type().borrow()) }}
+ {%- endmatch %}
+ {% endfor -%}
+{%- endmacro -%}
+
+{#
+ # Macro to call methods
+ #}
+{%- macro method_decl(py_method_name, meth) %}
+{% if meth.is_async() %}
+
+ def {{ py_method_name }}(self, {% call arg_list_decl(meth) %}):
+ {%- call setup_args_extra_indent(meth) %}
+ return _uniffi_rust_call_async(
+ _UniffiLib.{{ meth.ffi_func().name() }}(
+ self._pointer, {% call arg_list_lowered(meth) %}
+ ),
+ _UniffiLib.{{ meth.ffi_rust_future_poll(ci) }},
+ _UniffiLib.{{ meth.ffi_rust_future_complete(ci) }},
+ _UniffiLib.{{ meth.ffi_rust_future_free(ci) }},
+ # lift function
+ {%- match meth.return_type() %}
+ {%- when Some(return_type) %}
+ {{ return_type|lift_fn }},
+ {%- when None %}
+ lambda val: None,
+ {% endmatch %}
+ # Error FFI converter
+ {%- match meth.throws_type() %}
+ {%- when Some(e) %}
+ {{ e|ffi_converter_name }},
+ {%- when None %}
+ None,
+ {%- endmatch %}
+ )
+
+{%- else -%}
+{%- match meth.return_type() %}
+
+{%- when Some with (return_type) %}
+
+ def {{ py_method_name }}(self, {% call arg_list_decl(meth) %}) -> "{{ return_type|type_name }}":
+ {%- call setup_args_extra_indent(meth) %}
+ return {{ return_type|lift_fn }}(
+ {% call to_ffi_call_with_prefix("self._pointer", meth) %}
+ )
+
+{%- when None %}
+
+ def {{ py_method_name }}(self, {% call arg_list_decl(meth) %}):
+ {%- call setup_args_extra_indent(meth) %}
+ {% call to_ffi_call_with_prefix("self._pointer", meth) %}
+{% endmatch %}
+{% endif %}
+
+{% 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
new file mode 100644
index 0000000000..24c3290ff7
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/templates/wrapper.py
@@ -0,0 +1,74 @@
+# This file was autogenerated by some hot garbage in the `uniffi` crate.
+# Trust me, you don't want to mess with it!
+
+# Common helper code.
+#
+# Ideally this would live in a separate .py file where it can be unittested etc
+# in isolation, and perhaps even published as a re-useable package.
+#
+# However, it's important that the details of how this helper code works (e.g. the
+# way that different builtin types are passed across the FFI) exactly match what's
+# expected by the rust code on the other side of the interface. In practice right
+# now that means coming from the exact some version of `uniffi` that was used to
+# compile the rust component. The easiest way to ensure this is to bundle the Python
+# helpers directly inline like we're doing here.
+
+import os
+import sys
+import ctypes
+import enum
+import struct
+import contextlib
+import datetime
+import typing
+{%- if ci.has_async_fns() %}
+import asyncio
+{%- endif %}
+import platform
+{%- for req in self.imports() %}
+{{ req.render() }}
+{%- endfor %}
+
+# Used for default argument values
+_DEFAULT = object()
+
+{% include "RustBufferTemplate.py" %}
+{% include "Helpers.py" %}
+{% include "PointerManager.py" %}
+{% include "RustBufferHelper.py" %}
+
+# Contains loading, initialization code, and the FFI Function declarations.
+{% include "NamespaceLibraryTemplate.py" %}
+
+# 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 %}
+
+__all__ = [
+ "InternalError",
+ {%- for e in ci.enum_definitions() %}
+ "{{ e|type_name }}",
+ {%- endfor %}
+ {%- for record in ci.record_definitions() %}
+ "{{ record|type_name }}",
+ {%- endfor %}
+ {%- for func in ci.function_definitions() %}
+ "{{ func.name()|fn_name }}",
+ {%- endfor %}
+ {%- for obj in ci.object_definitions() %}
+ "{{ obj|type_name }}",
+ {%- endfor %}
+ {%- for c in ci.callback_interface_definitions() %}
+ "{{ c.name()|class_name }}",
+ {%- endfor %}
+]
+
+{% import "macros.py" as py %}