Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updating codegen for Python/Swift to match Kotlin #1113

Merged
merged 1 commit into from
Nov 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Updating codegen for Python/Swift to match Kotlin
Uptading the Python/Swift code to match the Kotlin changes in PRs #1096
and #1087
  • Loading branch information
bendk committed Nov 24, 2021
commit f7b8eb4bcf859db8bdd182828901a32b622546c4
52 changes: 32 additions & 20 deletions uniffi_bindgen/src/bindings/python/gen_python/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,62 +273,74 @@ pub mod filters {
PythonCodeOracle
}

pub fn type_py(type_: &Type) -> Result<String, askama::Error> {
pub fn type_name(codetype: &impl CodeType) -> Result<String, askama::Error> {
let oracle = oracle();
Ok(oracle.find(type_).type_label(&oracle))
Ok(codetype.type_label(&oracle))
}

pub fn canonical_name(type_: &Type) -> Result<String, askama::Error> {
pub fn canonical_name(codetype: &impl CodeType) -> Result<String, askama::Error> {
let oracle = oracle();
Ok(oracle.find(type_).canonical_name(&oracle))
Ok(codetype.canonical_name(&oracle))
}

pub fn lower_py(nm: &dyn fmt::Display, type_: &Type) -> Result<String, askama::Error> {
pub fn lower_var(
nm: &dyn fmt::Display,
codetype: &impl CodeType,
) -> Result<String, askama::Error> {
let oracle = oracle();
Ok(oracle.find(type_).lower(&oracle, nm))
Ok(codetype.lower(&oracle, nm))
}

pub fn write_py(
pub fn write_var(
nm: &dyn fmt::Display,
target: &dyn fmt::Display,
type_: &Type,
codetype: &impl CodeType,
) -> Result<String, askama::Error> {
let oracle = oracle();
Ok(oracle.find(type_).write(&oracle, nm, target))
Ok(codetype.write(&oracle, nm, target))
}

pub fn lift_py(nm: &dyn fmt::Display, type_: &Type) -> Result<String, askama::Error> {
pub fn lift_var(
nm: &dyn fmt::Display,
codetype: &impl CodeType,
) -> Result<String, askama::Error> {
let oracle = oracle();
Ok(oracle.find(type_).lift(&oracle, nm))
Ok(codetype.lift(&oracle, nm))
}

pub fn literal_py(literal: &Literal, type_: &Type) -> Result<String, askama::Error> {
pub fn literal_py(
literal: &Literal,
codetype: &impl CodeType,
) -> Result<String, askama::Error> {
let oracle = oracle();
Ok(oracle.find(type_).literal(&oracle, literal))
Ok(codetype.literal(&oracle, literal))
}

pub fn read_py(nm: &dyn fmt::Display, type_: &Type) -> Result<String, askama::Error> {
pub fn read_var(
nm: &dyn fmt::Display,
codetype: &impl CodeType,
) -> Result<String, askama::Error> {
let oracle = oracle();
Ok(oracle.find(type_).read(&oracle, nm))
Ok(codetype.read(&oracle, nm))
}

/// Get the Python syntax for representing a given low-level `FFIType`.
pub fn type_ffi(type_: &FFIType) -> Result<String, askama::Error> {
pub fn ffi_type_name(type_: &FFIType) -> Result<String, askama::Error> {
Ok(oracle().ffi_type_label(type_))
}

/// Get the idiomatic Python rendering of a class name (for enums, records, errors, etc).
pub fn class_name_py(nm: &dyn fmt::Display) -> Result<String, askama::Error> {
pub fn class_name(nm: &dyn fmt::Display) -> Result<String, askama::Error> {
Ok(oracle().class_name(nm))
}

/// Get the idiomatic Python rendering of a function name.
pub fn fn_name_py(nm: &dyn fmt::Display) -> Result<String, askama::Error> {
pub fn fn_name(nm: &dyn fmt::Display) -> Result<String, askama::Error> {
Ok(oracle().fn_name(nm))
}

/// Get the idiomatic Python rendering of a variable name.
pub fn var_name_py(nm: &dyn fmt::Display) -> Result<String, askama::Error> {
pub fn var_name(nm: &dyn fmt::Display) -> Result<String, askama::Error> {
Ok(oracle().var_name(nm))
}

Expand All @@ -342,7 +354,7 @@ pub mod filters {
/// This replaces "Error" at the end of the name with "Exception". Rust code typically uses
/// "Error" for any type of error but in the Java world, "Error" means a non-recoverable error
/// and is distinguished from an "Exception".
pub fn exception_name_py(nm: &dyn fmt::Display) -> Result<String, askama::Error> {
pub fn exception_name(nm: &dyn fmt::Display) -> Result<String, askama::Error> {
Ok(oracle().error_name(nm))
}

Expand Down
32 changes: 16 additions & 16 deletions uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
{%- let e = self.inner() %}
{% if e.is_flat() %}

class {{ e.name()|class_name_py }}(ViaFfiUsingByteBuffer, enum.Enum):
class {{ e|type_name }}(ViaFfiUsingByteBuffer, enum.Enum):
{% for variant in e.variants() -%}
{{ variant.name()|enum_variant_py }} = {{ loop.index }}
{% endfor %}
Expand All @@ -17,42 +17,42 @@ def _read(buf):
variant = buf.readI32()
{% for variant in e.variants() -%}
if variant == {{ loop.index }}:
return {{ e.name()|class_name_py }}.{{ variant.name()|enum_variant_py }}
return {{ e|type_name }}.{{ variant.name()|enum_variant_py }}
{% endfor %}
raise InternalError("Raw enum value doesn't match any cases")

def _write(self, buf):
{% for variant in e.variants() -%}
if self is {{ e.name()|class_name_py }}.{{ variant.name()|enum_variant_py }}:
if self is {{ e|type_name }}.{{ variant.name()|enum_variant_py }}:
i = {{loop.index}}
buf.writeI32({{ loop.index }})
{% endfor %}
{% else %}

class {{ e.name()|class_name_py }}(ViaFfiUsingByteBuffer, object):
class {{ e|type_name }}(ViaFfiUsingByteBuffer, object):
def __init__(self):
raise RuntimeError("{{ e.name()|class_name_py }} cannot be instantiated directly")
raise RuntimeError("{{ e|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 }}(object):
def __init__(self,{% for field in variant.fields() %}{{ field.name()|var_name_py }}{% if loop.last %}{% else %}, {% endif %}{% endfor %}):
def __init__(self,{% for field in variant.fields() %}{{ field.name()|var_name }}{% if loop.last %}{% else %}, {% endif %}{% endfor %}):
{% if variant.has_fields() %}
{%- for field in variant.fields() %}
self.{{ field.name()|var_name_py }} = {{ field.name()|var_name_py }}
self.{{ field.name()|var_name }} = {{ field.name()|var_name }}
{%- endfor %}
{% else %}
pass
{% endif %}

def __str__(self):
return "{{ e.name()|class_name_py }}.{{ variant.name()|enum_variant_py }}({% for field in variant.fields() %}{{ field.name() }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in variant.fields() %}self.{{ field.name() }}{% if loop.last %}{% else %}, {% endif %}{% endfor %})
return "{{ e|type_name }}.{{ variant.name()|enum_variant_py }}({% for field in variant.fields() %}{{ field.name() }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in variant.fields() %}self.{{ field.name() }}{% if loop.last %}{% else %}, {% endif %}{% endfor %})

def __eq__(self, other):
if not other.is_{{ variant.name()|var_name_py }}():
if not other.is_{{ variant.name()|var_name }}():
return False
{%- for field in variant.fields() %}
if self.{{ field.name()|var_name_py }} != other.{{ field.name()|var_name_py }}:
if self.{{ field.name()|var_name }} != other.{{ field.name()|var_name }}:
return False
{%- endfor %}
return True
Expand All @@ -61,8 +61,8 @@ def __eq__(self, other):
# 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_py }}(self):
return isinstance(self, {{ e.name()|class_name_py }}.{{ variant.name()|enum_variant_py }})
def is_{{ variant.name()|var_name }}(self):
return isinstance(self, {{ e|type_name }}.{{ variant.name()|enum_variant_py }})
{% endfor %}

@classmethod
Expand All @@ -73,26 +73,26 @@ def _read(cls, buf):
if variant == {{ loop.index }}:
return cls.{{variant.name()|enum_variant_py}}(
{%- for field in variant.fields() %}
{{ field.name()|var_name_py }}={{ "buf"|read_py(field.type_()) }},
{{ field.name()|var_name }}={{ "buf"|read_var(field.type_()) }},
{%- endfor %}
)
{% endfor %}
raise InternalError("Raw enum value doesn't match any cases")

def _write(self, buf):
{% for variant in e.variants() -%}
if self.is_{{ variant.name()|var_name_py }}():
if self.is_{{ variant.name()|var_name }}():
buf.writeI32({{ loop.index }})
{%- for field in variant.fields() %}
{{ "self.{}"|format(field.name())|write_py("buf", field.type_()) }}
{{ "self.{}"|format(field.name())|write_var("buf", field.type_()) }}
{%- endfor %}
{% 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() -%}
{{ e.name()|class_name_py }}.{{ variant.name()|enum_variant_py }} = type("{{ e.name()|class_name_py }}.{{ variant.name()|enum_variant_py }}", ({{ e.name()|class_name_py }}.{{variant.name()|enum_variant_py}}, {{ e.name()|class_name_py }},), {})
{{ e|type_name }}.{{ variant.name()|enum_variant_py }} = type("{{ e|type_name }}.{{ variant.name()|enum_variant_py }}", ({{ e|type_name }}.{{variant.name()|enum_variant_py}}, {{ e|type_name }},), {})
{% endfor %}

{% endif %}
24 changes: 12 additions & 12 deletions uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
{%- let e = self.inner() %}
class {{ e.name()|class_name_py }}(ViaFfiUsingByteBuffer):
class {{ e|type_name }}(ViaFfiUsingByteBuffer):

{%- if e.is_flat() %}

# Each variant is a nested class of the error itself.
# It just carries a string error message, so no special implementation is necessary.
{%- for variant in e.variants() %}
class {{ variant.name()|class_name_py }}(ViaFfiUsingByteBuffer, Exception):
class {{ variant.name()|class_name }}(ViaFfiUsingByteBuffer, Exception):
def _write(self, buf):
buf.writeI32({{ loop.index }})
message = str(self)
{{ "message"|write_py("buf", Type::String) }}
{{ "message"|write_var("buf", Type::String) }}
{%- endfor %}

@classmethod
def _read(cls, buf):
variant = buf.readI32()
{% for variant in e.variants() -%}
if variant == {{ loop.index }}:
return cls.{{ variant.name()|class_name_py }}({{ "buf"|read_py(Type::String) }})
return cls.{{ variant.name()|class_name }}({{ "buf"|read_var(Type::String) }})
{% endfor %}
raise InternalError("Raw enum value doesn't match any cases")

{%- else %}

# Each variant is a nested class of the error itself.
{%- for variant in e.variants() %}
class {{ variant.name()|class_name_py }}(ViaFfiUsingByteBuffer, Exception):
def __init__(self{% for field in variant.fields() %}, {{ field.name()|var_name_py }}{% endfor %}):
class {{ variant.name()|class_name }}(ViaFfiUsingByteBuffer, Exception):
def __init__(self{% for field in variant.fields() %}, {{ field.name()|var_name }}{% endfor %}):
{%- if variant.has_fields() %}
{%- for field in variant.fields() %}
self.{{ field.name()|var_name_py }} = {{ field.name()|var_name_py }}
self.{{ field.name()|var_name }} = {{ field.name()|var_name }}
{%- endfor %}
{%- else %}
pass
Expand All @@ -43,15 +43,15 @@ def __str__(self):
'{{ field.name() }}={!r}'.format(self.{{ field.name() }}),
{%- endfor %}
]
return "{{ e.name()|class_name_py }}.{{ variant.name()|class_name_py }}({})".format(', '.join(field_parts))
return "{{ e|type_name }}.{{ variant.name()|class_name }}({})".format(', '.join(field_parts))
{%- else %}
return "{{ e.name()|class_name_py }}.{{ variant.name()|class_name_py }}"
return "{{ e|type_name }}.{{ variant.name()|class_name }}"
{%- endif %}

def _write(self, buf):
buf.writeI32({{ loop.index }})
{%- for field in variant.fields() %}
{{ "self.{}"|format(field.name()) |write_py("buf", field.type_()) }}
{{ "self.{}"|format(field.name()) |write_var("buf", field.type_()) }}
{%- endfor %}
{%- endfor %}

Expand All @@ -60,9 +60,9 @@ def _read(cls, buf):
variant = buf.readI32()
{% for variant in e.variants() -%}
if variant == {{ loop.index }}:
return cls.{{ variant.name()|class_name_py }}(
return cls.{{ variant.name()|class_name }}(
{% for field in variant.fields() -%}
{{ field.name()|var_name_py }}={{ "buf"|read_py(field.type_()) }}{% if loop.last %}{% else %},{% endif %}
{{ field.name()|var_name }}={{ "buf"|read_var(field.type_()) }}{% if loop.last %}{% else %},{% endif %}
{% endfor -%}
)
{% endfor %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
class FfiConverter{{ name }}(FfiConverterUsingByteBuffer):
@staticmethod
def _write(value, buf):
from {{ crate_name|fn_name_py }} import {{ name }};
from {{ crate_name|fn_name }} import {{ name }};
{{ name }}._write(value, buf)

@staticmethod
def _read(buf):
from {{ crate_name|fn_name_py }} import {{ name }};
from {{ crate_name|fn_name }} import {{ name }};
return {{ name }}._read(buf)
8 changes: 4 additions & 4 deletions uniffi_bindgen/src/bindings/python/templates/MapTemplate.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ class FfiConverter{{ canonical_type_name }}(FfiConverterUsingByteBuffer):
@staticmethod
def _write(value, buf):
def inner_write(key, value, buf):
{{ "key"|write_py("buf", Type::String) }}
{{ "value"|write_py("buf", inner_type) }}
{{ "key"|write_var("buf", Type::String) }}
{{ "value"|write_var("buf", inner_type) }}

FfiConverterDictionary._write(value, buf, inner_write)

@staticmethod
def _read(buf):
def inner_read(buf):
key = {{ "buf"|read_py(TypeIdentifier::String) }}
value = {{ "buf"|read_py(inner_type) }}
key = {{ "buf"|read_var(TypeIdentifier::String) }}
value = {{ "buf"|read_var(inner_type) }}
return (key, value)

return FfiConverterDictionary._read(buf, inner_read)
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@ def loadIndirect():
_UniFFILib.{{ func.name() }}.argtypes = (
{%- call py::arg_list_ffi_decl(func) -%}
)
_UniFFILib.{{ func.name() }}.restype = {% match func.return_type() %}{% when Some with (type_) %}{{ type_|type_ffi }}{% when None %}None{% endmatch %}
_UniFFILib.{{ func.name() }}.restype = {% match func.return_type() %}{% when Some with (type_) %}{{ type_|ffi_type_name }}{% when None %}None{% endmatch %}
{%- endfor %}
14 changes: 7 additions & 7 deletions uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{% import "macros.py" as py %}
{%- let obj = self.inner() %}

class {{ obj.name()|class_name_py }}(object):
class {{ obj|type_name }}(object):
{%- match obj.primary_constructor() %}
{%- when Some with (cons) %}
def __init__(self, {% call py::arg_list_decl(cons) -%}):
Expand All @@ -27,7 +27,7 @@ def _make_instance_(cls, pointer):

{% for cons in obj.alternate_constructors() -%}
@classmethod
def {{ cons.name()|fn_name_py }}(cls, {% call py::arg_list_decl(cons) %}):
def {{ cons.name()|fn_name }}(cls, {% call py::arg_list_decl(cons) %}):
{%- call py::coerce_args_extra_indent(cons) %}
# Call the (fallible) function before creating any half-baked object instances.
pointer = {% call py::to_ffi_call(cons) %}
Expand All @@ -38,13 +38,13 @@ def {{ cons.name()|fn_name_py }}(cls, {% call py::arg_list_decl(cons) %}):
{%- match meth.return_type() -%}

{%- when Some with (return_type) -%}
def {{ meth.name()|fn_name_py }}(self, {% call py::arg_list_decl(meth) %}):
def {{ meth.name()|fn_name }}(self, {% call py::arg_list_decl(meth) %}):
{%- call py::coerce_args_extra_indent(meth) %}
_retval = {% call py::to_ffi_call_with_prefix("self._pointer", meth) %}
return {{ "_retval"|lift_py(return_type) }}
return {{ "_retval"|lift_var(return_type) }}

{%- when None -%}
def {{ meth.name()|fn_name_py }}(self, {% call py::arg_list_decl(meth) %}):
def {{ meth.name()|fn_name }}(self, {% call py::arg_list_decl(meth) %}):
{%- call py::coerce_args_extra_indent(meth) %}
{% call py::to_ffi_call_with_prefix("self._pointer", meth) %}
{% endmatch %}
Expand All @@ -59,8 +59,8 @@ def _read(cls, buf):

@classmethod
def _write(cls, value, buf):
if not isinstance(value, {{ obj.name()|class_name_py }}):
raise TypeError("Expected {{ obj.name()|class_name_py }} instance, {} found".format(value.__class__.__name__))
if not isinstance(value, {{ obj|type_name }}):
raise TypeError("Expected {{ obj|type_name }} instance, {} found".format(value.__class__.__name__))
buf.writeU64(value._lower())

@classmethod
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
class FfiConverter{{ canonical_type_name }}(FfiConverterUsingByteBuffer):
@staticmethod
def _write(value, buf):
FfiConverterOptional._write(value, buf, lambda v, buf: {{ "v"|write_py("buf", inner_type) }})
FfiConverterOptional._write(value, buf, lambda v, buf: {{ "v"|write_var("buf", inner_type) }})

@staticmethod
def _read(buf):
return FfiConverterOptional._read(buf, lambda buf: {{ "buf"|read_py(inner_type) }})
return FfiConverterOptional._read(buf, lambda buf: {{ "buf"|read_var(inner_type) }})
Loading