Skip to content
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## Unreleased

- Python package codegen now emits sibling submodule imports in dependency order, so Python 3.14/Pydantic can import generated packages where one sibling model annotation references another sibling namespace.
- Rust codegen snapshot typechecking now handles macOS proc-macro library names instead of assuming Linux-style `.so` files.

## 0.17.5

Python client generation improvements:
Expand Down
126 changes: 126 additions & 0 deletions reflectapi-demo/src/tests/namespace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,132 @@ fn test_python_split_modules_import_parent_for_top_level_refs() {
);
}

#[test]
fn test_python_split_modules_order_sibling_imports_by_references() {
#[derive(
Debug, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output,
)]
struct Issue164Rule {
id: String,
}

#[derive(
Debug, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output,
)]
struct Issue164Group {
rule: Issue164Rule,
}

async fn group_get<S>(_s: S, _: reflectapi::Empty, _: reflectapi::Empty) -> Issue164Group {
unimplemented!()
}

let (schema, _) = reflectapi::Builder::<()>::new()
.route(group_get, |b| b.name("commerce.group.get"))
.rename_types(
"reflectapi_demo::tests::namespace::Issue164Group",
"commerce::group::Group",
)
.rename_types(
"reflectapi_demo::tests::namespace::Issue164Rule",
"commerce::rule::Rule",
)
.build()
.unwrap();

let files = reflectapi::codegen::python::generate_files(
schema,
&reflectapi::codegen::python::Config::default(),
)
.unwrap();

let commerce_file = files.get("commerce/__init__.py").unwrap();
let group_import = commerce_file.find("from . import group").unwrap();
let rule_import = commerce_file.find("from . import rule").unwrap();
assert!(
rule_import < group_import,
"`rule` must import before `group` because `group` references `commerce.rule`:\n{commerce_file}"
);

let group_file = files.get("commerce/group/__init__.py").unwrap();
assert!(
group_file.contains("rule: commerce.rule.Rule"),
"{group_file}"
);
}

#[test]
fn test_python_split_modules_root_sibling_order_ignores_nested_suffixes() {
#[derive(
Debug, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output,
)]
struct Issue164NestedLeaf {
id: String,
}

#[derive(
Debug, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output,
)]
/// This prose mentions common.UsesX but must not affect import ordering.
struct Issue164XRoot {
leaf: Issue164NestedLeaf,
}

#[derive(
Debug, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output,
)]
struct Issue164CommonUsesX {
root: Issue164XRoot,
}

async fn common_get<S>(
_s: S,
_: reflectapi::Empty,
_: reflectapi::Empty,
) -> Issue164CommonUsesX {
unimplemented!()
}

let (schema, _) = reflectapi::Builder::<()>::new()
.route(common_get, |b| b.name("common.get"))
.rename_types(
"reflectapi_demo::tests::namespace::Issue164NestedLeaf",
"x::common::Leaf",
)
.rename_types(
"reflectapi_demo::tests::namespace::Issue164XRoot",
"x::Root",
)
.rename_types(
"reflectapi_demo::tests::namespace::Issue164CommonUsesX",
"common::UsesX",
)
.build()
.unwrap();

let files = reflectapi::codegen::python::generate_files(
schema,
&reflectapi::codegen::python::Config::default(),
)
.unwrap();

let init_py = files.get("__init__.py").unwrap();
let init_common_import = init_py.find("from . import common").unwrap();
let init_x_import = init_py.find("from . import x").unwrap();
assert!(
init_x_import < init_common_import,
"root `x` must import before root `common`; nested `x.common` references must not create a false root `common` dependency:\n{init_py}"
);

let client_py = files.get("_client.py").unwrap();
let client_common_import = client_py.find("from . import common").unwrap();
let client_x_import = client_py.find("from . import x").unwrap();
assert!(
client_x_import < client_common_import,
"client imports must also load root `x` before root `common`:\n{client_py}"
);
}

#[test]
fn test_python_split_modules_handles_sys_root_and_sanitized_class_names() {
#[derive(
Expand Down
46 changes: 46 additions & 0 deletions reflectapi-demo/tests/codegen_coverage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ async fn codegen_coverage(
Ok(coverage::CoverageResponse { ok: true })
}

async fn commerce_group(
_: Arc<State>,
_request: reflectapi::Empty,
_headers: reflectapi::Empty,
) -> Result<commerce::group::Group, reflectapi::Infallible> {
Ok(commerce::group::Group {
rule: commerce::rule::Rule { id: String::new() },
})
}

fn builder() -> reflectapi::Builder<Arc<State>> {
reflectapi::Builder::new()
.name("Codegen coverage")
Expand All @@ -60,6 +70,10 @@ fn builder() -> reflectapi::Builder<Arc<State>> {
b.name("coverage.edges")
.description("Coverage fixtures for codegen edge cases")
})
.route(commerce_group, |b| {
b.name("coverage.commerce_group")
.description("Coverage fixture for sibling package import ordering")
})
}

#[test]
Expand Down Expand Up @@ -136,6 +150,38 @@ mod order {
}
}

mod commerce {
pub mod rule {
#[derive(
Debug,
Clone,
serde::Serialize,
serde::Deserialize,
reflectapi::Input,
reflectapi::Output,
)]
pub struct Rule {
pub id: String,
}
}

pub mod group {
use super::rule::Rule;

#[derive(
Debug,
Clone,
serde::Serialize,
serde::Deserialize,
reflectapi::Input,
reflectapi::Output,
)]
pub struct Group {
pub rule: Rule,
}
}
}

mod coverage {
use std::collections::HashMap;

Expand Down
Loading
Loading