Skip to content

Commit b082b3a

Browse files
committed
fix cd meta && uv run python -m pytest
1 parent 4b59a1b commit b082b3a

7 files changed

Lines changed: 965 additions & 959 deletions

File tree

meta/src/meta/codegen_go.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,15 @@ def _generate_NewMessage(
562562
# option_var is the original Option variable name for checking .Valid
563563
oneof_groups: dict[str, list[tuple[str, str, str]]] = {}
564564

565-
from .target import OptionType, Var
565+
from .target import Builtin, OptionType, Var
566+
567+
def _go_optional_string_unwrap_needs_ptr(field_expr: TargetExpr) -> bool:
568+
"""unwrap_option_or yields a plain string; optional proto fields need *string."""
569+
return (
570+
isinstance(field_expr, Call)
571+
and isinstance(field_expr.func, Builtin)
572+
and field_expr.func.name == "unwrap_option_or"
573+
)
566574

567575
def unwrap_if_option(field_expr, field_value: str) -> tuple[str, str]:
568576
"""Unwrap Option type values for struct field assignment.
@@ -654,6 +662,22 @@ def unwrap_if_option(field_expr, field_value: str) -> tuple[str, str]:
654662
assert field_value is not None
655663
pascal_field = to_pascal_case(field_name)
656664
_, field_value = unwrap_if_option(field_expr, field_value)
665+
666+
proto_msg = self.proto_messages.get((expr.module, expr.name))
667+
if proto_msg is not None:
668+
pf = next(
669+
(f for f in proto_msg.fields if f.name == field_name),
670+
None,
671+
)
672+
if (
673+
pf is not None
674+
and pf.is_optional
675+
and not pf.is_repeated
676+
and pf.type == "string"
677+
and _go_optional_string_unwrap_needs_ptr(field_expr)
678+
):
679+
field_value = f"ptr({field_value})"
680+
657681
regular_assignments.append(f"{pascal_field}: {field_value}")
658682

659683
# Generate struct literal with regular fields only
@@ -920,8 +944,9 @@ def _generate_Assign(self, expr, lines: list[str], indent: str) -> str:
920944
):
921945
var_type = self.gen_type(expr.var.type)
922946
if var_type in ("int64", "int32", "uint32", "uint64"):
923-
lines.append(f"{indent}{var_name} := {var_type}({expr.expr.value})")
924-
self.mark_declared(var_name)
947+
lines.append(
948+
f"{indent}{self.gen_assignment(var_name, f'{var_type}({expr.expr.value})')}"
949+
)
925950
return self.gen_none()
926951

927952
# Regular assignment

meta/src/meta/grammar.y

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,7 +1182,7 @@ iceberg_to_snapshot
11821182

11831183
iceberg_data
11841184
: "(" "iceberg_data" iceberg_locator iceberg_config gnf_columns iceberg_to_snapshot? ")"
1185-
construct: $$ = logic.IcebergData(locator=$3, config=$4, columns=$5, to_snapshot=iceberg_optional_string_field($6))
1185+
construct: $$ = logic.IcebergData(locator=$3, config=$4, columns=$5, to_snapshot=builtin.unwrap_option_or($6, ""))
11861186
deconstruct:
11871187
$3: logic.IcebergLocator = $$.locator
11881188
$4: logic.IcebergConfig = $$.config
@@ -1643,7 +1643,7 @@ def construct_iceberg_config(
16431643
) -> logic.IcebergConfig:
16441644
props: Dict[String, String] = builtin.string_map_from_pairs(property_pairs)
16451645
auth_props: Dict[String, String] = builtin.string_map_from_pairs(auth_property_pairs)
1646-
scope_pb: Optional[String] = iceberg_optional_string_field(scope_opt)
1646+
scope_pb: String = builtin.unwrap_option_or(scope_opt, "")
16471647
return logic.IcebergConfig(
16481648
catalog_uri=catalog_uri,
16491649
scope=scope_pb,
@@ -1652,12 +1652,6 @@ def construct_iceberg_config(
16521652
)
16531653

16541654

1655-
def iceberg_optional_string_field(s: Optional[String]) -> Optional[String]:
1656-
if s is None:
1657-
return builtin.none()
1658-
return builtin.some(s)
1659-
1660-
16611655
def deconstruct_iceberg_config_scope_optional(msg: logic.IcebergConfig) -> Optional[String]:
16621656
if builtin.has_proto_field(msg, "scope"):
16631657
return builtin.some(builtin.unwrap_option(msg.scope))

meta/src/meta/grammar_validator.py

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,19 @@
6262
from .validation_result import ValidationResult
6363

6464

65+
def _optional_string_allows_plain_string(
66+
actual: TargetType, expected: TargetType
67+
) -> bool:
68+
"""True if actual is String and expected is Option[String] (optional proto string)."""
69+
if isinstance(expected, OptionType) and isinstance(
70+
expected.element_type, BaseType
71+
):
72+
if expected.element_type.name != "String":
73+
return False
74+
return isinstance(actual, BaseType) and actual.name == "String"
75+
return False
76+
77+
6578
class GrammarValidator:
6679
"""Validates grammar against protobuf specification."""
6780

@@ -271,12 +284,17 @@ def _check_new_message_types(self, new_msg: NewMessage, context: str) -> None:
271284

272285
# Check actual type is a subtype of expected type (actual_type <: expected_type)
273286
if actual_type is not None and not is_subtype(actual_type, expected_type):
274-
self.result.add_error(
275-
"field_type",
276-
f"In {context}: NewMessage {new_msg.name} field '{field_name}' has type {actual_type}, expected {expected_type}",
277-
proto_type=new_msg.name,
278-
rule_name=context,
279-
)
287+
# Optional proto string fields are often represented as plain String at runtime
288+
# (e.g. ProtoBuf.jl uses "" for unset); allow String where Option[String] is expected.
289+
if _optional_string_allows_plain_string(actual_type, expected_type):
290+
pass
291+
else:
292+
self.result.add_error(
293+
"field_type",
294+
f"In {context}: NewMessage {new_msg.name} field '{field_name}' has type {actual_type}, expected {expected_type}",
295+
proto_type=new_msg.name,
296+
rule_name=context,
297+
)
280298

281299
# Check for missing fields
282300
for field in proto_message.fields:

meta/tests/meta/test_codegen_go.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,13 +232,13 @@ def test_go_assignment_generation():
232232
lines = []
233233
expr = Assign(Var("x", _int_type), Lit(42))
234234
gen.generate_lines(expr, lines, "")
235-
assert "x := 42" in lines[0]
235+
assert "x := int64(42)" in lines[0]
236236

237237
# Second assignment uses =
238238
lines = []
239239
expr = Assign(Var("x", _int_type), Lit(100))
240240
gen.generate_lines(expr, lines, "")
241-
assert "x = 100" in lines[0]
241+
assert "x = int64(100)" in lines[0]
242242

243243

244244
def test_go_return_generation():

0 commit comments

Comments
 (0)