Compare commits

...

2 Commits

5 changed files with 45 additions and 12 deletions
+2 -2
View File
@@ -7,7 +7,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from .utils import NodeDict
from .utils import NodeDict, get_cxx_name
if TYPE_CHECKING:
from .emitter import Emitter
@@ -30,7 +30,7 @@ def generate_enum(
literals: list[str] = []
for e in enumerants:
literals.append(f'"{e["name"]}"')
literals.append(f'"{get_cxx_name(e) or e["name"]}"')
emitter.add_typing_import("Literal")
line = f"Literal[{', '.join(literals)}]"
+4 -3
View File
@@ -16,7 +16,7 @@ from .gen_const import generate_const
from .gen_enum import generate_enum
from .gen_generic import generate_type_vars
from .type_resolver import resolve_type
from .utils import NodeDict, type_id_to_int
from .utils import NodeDict, get_cxx_name, type_id_to_int
if TYPE_CHECKING:
from .emitter import Emitter
@@ -44,7 +44,7 @@ def generate_struct(
# ── collect fields ─────────────────────────────────────────────────
for field in struct_body.get("fields", []):
field_name: str = field["name"]
field_name: str = get_cxx_name(field) or field["name"]
disc_value: int = field.get("discriminantValue", _NO_DISCRIMINANT)
if "slot" in field:
@@ -112,7 +112,8 @@ def generate_struct(
generate_const(emitter, nt, registry)
for field_name, type_str in non_union_fields:
emitter.add_field(field_name, type_str)
default = "None" if type_str == "None" else None
emitter.add_field(field_name, type_str, default=default)
if discriminant_count > 0 and union_fields:
_generate_union_methods(emitter, union_fields)
+3 -3
View File
@@ -7,7 +7,7 @@ building a ``TypeRegistry`` that maps type IDs to ``TypeInfo``.
from __future__ import annotations
from .models import TypeInfo, TypeRegistry
from .utils import NodeDict, get_display_name, get_node_kind, type_id_to_int
from .utils import NodeDict, get_cxx_name, get_display_name, get_node_kind, type_id_to_int
_GENERATABLE_KINDS = frozenset({"struct", "enum", "interface", "const"})
@@ -35,7 +35,7 @@ def build_type_registry(
continue
type_id = type_id_to_int(node["id"])
name = get_display_name(node)
name = get_cxx_name(node) or get_display_name(node)
scope_id = node.get("scopeId", "0")
parent_id = type_id_to_int(scope_id) if scope_id != "0" else None
@@ -117,7 +117,7 @@ def _build_scoped_name(
break
if get_node_kind(node) != "file":
parts.append(get_display_name(node))
parts.append(get_cxx_name(node) or get_display_name(node))
scope_id_str = node.get("scopeId", "0")
scope_id = int(scope_id_str) if scope_id_str != "0" else 0
+19
View File
@@ -38,3 +38,22 @@ def get_node_kind(node: NodeDict) -> str:
def type_id_to_int(type_id: str) -> int:
"""Convert a type ID string (from dict) to an integer."""
return int(type_id)
# Annotation IDs from c++.capnp
_CXX_NAME_ANNOTATION_ID = 17466269397259751886 # 0xf2466592b7084e81
def get_cxx_name(node: NodeDict) -> str | None:
"""Return the ``$Cxx.name`` annotation value if present on a node."""
try:
for annot in node.get("annotations", []):
if annot.get("id") == _CXX_NAME_ANNOTATION_ID:
val = annot.get("value")
if isinstance(val, dict):
text = val.get("text", "")
if isinstance(text, str):
return text
except Exception:
pass
return None
+17 -4
View File
@@ -15,7 +15,7 @@ _HERE = Path(__file__).parent
if str(_HERE) not in sys.path:
sys.path.insert(0, str(_HERE))
import capnp # pyright: ignore[reportMissingTypeStubs, reportUnusedImport] # activates import hook
import capnp # pyright: ignore[reportMissingTypeStubs] # activates import hook
import test_generics_capnp
import test_interface_capnp
import test_nested_capnp
@@ -95,9 +95,22 @@ class TestAddressBookStub:
assert ab.people is not None
# dummy.capnp runtime tests skipped — its annotation imports (c.capnp,
# c++.capnp) cause pycapnp's C++ SchemaParser to SIGABRT. The generated
# .pyi passes static type-checking and `capnp compile -opy` succeeds.
class TestDummyStub:
"""dummy.capnp — comprehensive test schema."""
def test_all_types_loads(self) -> None:
from pathlib import Path
parser = capnp.SchemaParser() # pyright: ignore[reportAttributeAccessIssue]
m = parser.load(str(Path(__file__).parent / "dummy.capnp"))
msg = m.TestAllTypes.new_message(textField="hello", int32Field=42)
assert msg.textField == "hello"
def test_unions(self) -> None:
from pathlib import Path
parser = capnp.SchemaParser() # pyright: ignore[reportAttributeAccessIssue]
m = parser.load(str(Path(__file__).parent / "dummy.capnp"))
u = m.TestUnion.new_message()
assert u is not None
# Note: multi/consumer.capnp cross-file import test skipped —