Skip to content

Commit 4e24c89

Browse files
authored
Introspection: expose module members gated behind #[cfg(...)] (#5318)
1 parent fa0427c commit 4e24c89

File tree

3 files changed

+54
-25
lines changed

3 files changed

+54
-25
lines changed

newsfragments/5318.fixed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Introspection: properly include module members gated behind `#[cfg(...)]` attributes

pyo3-macros-backend/src/introspection.rs

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,9 @@ pub fn module_introspection_code<'a>(
4343
members
4444
.into_iter()
4545
.zip(members_cfg_attrs)
46-
.filter_map(|(member, attributes)| {
47-
if attributes.is_empty() {
48-
Some(IntrospectionNode::IntrospectionId(Some(ident_to_type(
49-
member,
50-
))))
51-
} else {
52-
None // TODO: properly interpret cfg attributes
53-
}
46+
.map(|(member, attributes)| AttributedIntrospectionNode {
47+
node: IntrospectionNode::IntrospectionId(Some(ident_to_type(member))),
48+
attributes,
5449
})
5550
.collect(),
5651
),
@@ -138,7 +133,7 @@ pub fn function_introspection_code(
138133
}
139134
let decorators = decorators
140135
.into_iter()
141-
.map(|d| IntrospectionNode::String(d.into()))
136+
.map(|d| IntrospectionNode::String(d.into()).into())
142137
.collect::<Vec<_>>();
143138
if !decorators.is_empty() {
144139
desc.insert("decorators", IntrospectionNode::List(decorators));
@@ -220,9 +215,12 @@ fn arguments_introspection_data<'a>(
220215
let mut kwarg = None;
221216

222217
if let Some(first_argument) = first_argument {
223-
posonlyargs.push(IntrospectionNode::Map(
224-
[("name", IntrospectionNode::String(first_argument.into()))].into(),
225-
));
218+
posonlyargs.push(
219+
IntrospectionNode::Map(
220+
[("name", IntrospectionNode::String(first_argument.into()))].into(),
221+
)
222+
.into(),
223+
);
226224
}
227225

228226
for (i, param) in signature
@@ -296,7 +294,7 @@ fn argument_introspection_data<'a>(
296294
name: &'a str,
297295
desc: &'a RegularArg<'_>,
298296
class_type: Option<&Type>,
299-
) -> IntrospectionNode<'a> {
297+
) -> AttributedIntrospectionNode<'a> {
300298
let mut params: HashMap<_, _> = [("name", IntrospectionNode::String(name.into()))].into();
301299
if desc.default_value.is_some() {
302300
params.insert(
@@ -338,7 +336,7 @@ fn argument_introspection_data<'a>(
338336
);
339337
}
340338
}
341-
IntrospectionNode::Map(params)
339+
IntrospectionNode::Map(params).into()
342340
}
343341

344342
enum IntrospectionNode<'a> {
@@ -348,7 +346,7 @@ enum IntrospectionNode<'a> {
348346
InputType { rust_type: Type, nullable: bool },
349347
OutputType { rust_type: Type, is_final: bool },
350348
Map(HashMap<&'static str, IntrospectionNode<'a>>),
351-
List(Vec<IntrospectionNode<'a>>),
349+
List(Vec<AttributedIntrospectionNode<'a>>),
352350
}
353351

354352
impl IntrospectionNode<'_> {
@@ -381,9 +379,9 @@ impl IntrospectionNode<'_> {
381379
Self::IntrospectionId(ident) => {
382380
content.push_str("\"");
383381
content.push_tokens(if let Some(ident) = ident {
384-
quote! { #ident::_PYO3_INTROSPECTION_ID }
382+
quote! { #ident::_PYO3_INTROSPECTION_ID.as_bytes() }
385383
} else {
386-
quote! { _PYO3_INTROSPECTION_ID }
384+
quote! { _PYO3_INTROSPECTION_ID.as_bytes() }
387385
});
388386
content.push_str("\"");
389387
}
@@ -392,7 +390,7 @@ impl IntrospectionNode<'_> {
392390
nullable,
393391
} => {
394392
content.push_str("\"");
395-
content.push_tokens(quote! { <#rust_type as #pyo3_crate_path::impl_::extract_argument::PyFunctionArgument<false>>::INPUT_TYPE });
393+
content.push_tokens(quote! { <#rust_type as #pyo3_crate_path::impl_::extract_argument::PyFunctionArgument<false>>::INPUT_TYPE.as_bytes() });
396394
if nullable {
397395
content.push_str(" | None");
398396
}
@@ -406,7 +404,7 @@ impl IntrospectionNode<'_> {
406404
if is_final {
407405
content.push_str("typing.Final[");
408406
}
409-
content.push_tokens(quote! { <#rust_type as #pyo3_crate_path::impl_::introspection::PyReturnType>::OUTPUT_TYPE });
407+
content.push_tokens(quote! { <#rust_type as #pyo3_crate_path::impl_::introspection::PyReturnType>::OUTPUT_TYPE.as_bytes() });
410408
if is_final {
411409
content.push_str("]");
412410
}
@@ -426,18 +424,45 @@ impl IntrospectionNode<'_> {
426424
}
427425
Self::List(list) => {
428426
content.push_str("[");
429-
for (i, value) in list.into_iter().enumerate() {
430-
if i > 0 {
431-
content.push_str(",");
427+
for (i, AttributedIntrospectionNode { node, attributes }) in
428+
list.into_iter().enumerate()
429+
{
430+
if attributes.is_empty() {
431+
if i > 0 {
432+
content.push_str(",");
433+
}
434+
node.add_to_serialization(content, pyo3_crate_path);
435+
} else {
436+
// We serialize the element to easily gate it behind the attributes
437+
let mut nested_builder = ConcatenationBuilder::default();
438+
if i > 0 {
439+
nested_builder.push_str(",");
440+
}
441+
node.add_to_serialization(&mut nested_builder, pyo3_crate_path);
442+
let nested_content = nested_builder.into_token_stream(pyo3_crate_path);
443+
content.push_tokens(quote! { #(#attributes)* #nested_content });
432444
}
433-
value.add_to_serialization(content, pyo3_crate_path);
434445
}
435446
content.push_str("]");
436447
}
437448
}
438449
}
439450
}
440451

452+
struct AttributedIntrospectionNode<'a> {
453+
node: IntrospectionNode<'a>,
454+
attributes: &'a [Attribute],
455+
}
456+
457+
impl<'a> From<IntrospectionNode<'a>> for AttributedIntrospectionNode<'a> {
458+
fn from(node: IntrospectionNode<'a>) -> Self {
459+
Self {
460+
node,
461+
attributes: &[],
462+
}
463+
}
464+
}
465+
441466
#[derive(Default)]
442467
struct ConcatenationBuilder {
443468
elements: Vec<ConcatenationBuilderElement>,
@@ -490,7 +515,7 @@ impl ConcatenationBuilder {
490515

491516
quote! {
492517
{
493-
const PIECES: &[&[u8]] = &[#(#elements.as_bytes() , )*];
518+
const PIECES: &[&[u8]] = &[#(#elements , )*];
494519
&#pyo3_crate_path::impl_::concat::combine_to_array::<{
495520
#pyo3_crate_path::impl_::concat::combined_len(PIECES)
496521
}>(PIECES)
@@ -507,7 +532,7 @@ enum ConcatenationBuilderElement {
507532
impl ToTokens for ConcatenationBuilderElement {
508533
fn to_tokens(&self, tokens: &mut TokenStream) {
509534
match self {
510-
Self::String(s) => s.to_tokens(tokens),
535+
Self::String(s) => quote! { #s.as_bytes() }.to_tokens(tokens),
511536
Self::TokenStream(ts) => ts.to_tokens(tokens),
512537
}
513538
}

pytests/stubs/pyclasses.pyi

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ class ClassWithDecorators:
1717
@staticmethod
1818
def static_method() -> int: ...
1919

20+
class ClassWithDict:
21+
def __new__(cls, /) -> None: ...
22+
2023
class ClassWithoutConstructor: ...
2124

2225
class EmptyClass:

0 commit comments

Comments
 (0)