Skip to main content

pyo3/impl_/
introspection.rs

1use crate::conversion::IntoPyObject;
2use crate::inspect::PyStaticExpr;
3
4/// Seals `PyReturnType` so that types outside PyO3 cannot implement it.
5mod return_type {
6    use crate::{impl_::introspection::PyReturnType, IntoPyObject};
7
8    pub trait Sealed {}
9
10    impl<'a, T: IntoPyObject<'a>> Sealed for T {}
11    impl<T: PyReturnType, E> Sealed for Result<T, E> {}
12}
13
14/// Trait to guess a function Python return type
15///
16/// It is useful to properly get the return type `T` when the Rust implementation returns e.g. `PyResult<T>`
17pub trait PyReturnType: return_type::Sealed {
18    /// The function return type
19    const OUTPUT_TYPE: PyStaticExpr;
20}
21
22impl<'a, T: IntoPyObject<'a>> PyReturnType for T {
23    const OUTPUT_TYPE: PyStaticExpr = T::OUTPUT_TYPE;
24}
25
26impl<T: PyReturnType, E> PyReturnType for Result<T, E> {
27    const OUTPUT_TYPE: PyStaticExpr = T::OUTPUT_TYPE;
28}
29
30#[diagnostic::on_unimplemented(
31    message = "`{Self}` cannot be converted to a Python object",
32    label = "required by `#[pyo3(get)]` to create a readable property from a field of type `{Self}`",
33    note = "implement `IntoPyObject` for `&{Self}` or `IntoPyObject + Clone` for `{Self}` to define the conversion"
34)]
35pub trait PyIntoPyObjectMaybeRefType<const REF_IMPL_EXISTS: bool> {
36    const OUTPUT_TYPE: PyStaticExpr;
37}
38
39impl<'a, 'py, T: 'a> PyIntoPyObjectMaybeRefType<true> for T
40where
41    &'a T: IntoPyObject<'py>,
42{
43    const OUTPUT_TYPE: PyStaticExpr = <&T as IntoPyObject<'_>>::OUTPUT_TYPE;
44}
45
46impl<'py, T: IntoPyObject<'py>> PyIntoPyObjectMaybeRefType<false> for T {
47    const OUTPUT_TYPE: PyStaticExpr = <T as IntoPyObject<'_>>::OUTPUT_TYPE;
48}
49
50#[repr(C)]
51pub struct SerializedIntrospectionFragment<const LEN: usize> {
52    pub length: u32,
53    pub fragment: [u8; LEN],
54}
55
56/// Escapes a string to be valid JSON. Does not add quotes around it
57///
58/// Returns the number of written bytes
59pub const fn escape_json_string(input: &str, output: &mut [u8]) -> usize {
60    let input = input.as_bytes();
61    let mut input_i = 0;
62    let mut output_i = 0;
63    while input_i < input.len() {
64        match input[input_i] {
65            b'\\' => {
66                output[output_i] = b'\\';
67                output_i += 1;
68                output[output_i] = b'\\';
69                output_i += 1;
70            }
71            b'"' => {
72                output[output_i] = b'\\';
73                output_i += 1;
74                output[output_i] = b'"';
75                output_i += 1;
76            }
77            0x08 => {
78                output[output_i] = b'\\';
79                output_i += 1;
80                output[output_i] = b'b';
81                output_i += 1;
82            }
83            0x0C => {
84                output[output_i] = b'\\';
85                output_i += 1;
86                output[output_i] = b'f';
87                output_i += 1;
88            }
89            b'\n' => {
90                output[output_i] = b'\\';
91                output_i += 1;
92                output[output_i] = b'n';
93                output_i += 1;
94            }
95            b'\r' => {
96                output[output_i] = b'\\';
97                output_i += 1;
98                output[output_i] = b'r';
99                output_i += 1;
100            }
101            b'\t' => {
102                output[output_i] = b'\\';
103                output_i += 1;
104                output[output_i] = b't';
105                output_i += 1;
106            }
107            c @ 0..32 => {
108                output[output_i] = b'\\';
109                output_i += 1;
110                output[output_i] = b'u';
111                output_i += 1;
112                output[output_i] = b'0';
113                output_i += 1;
114                output[output_i] = b'0';
115                output_i += 1;
116                output[output_i] = b'0' + (c / 16);
117                output_i += 1;
118                let remainder = c % 16;
119                output[output_i] = if remainder >= 10 {
120                    b'a' + remainder - 10
121                } else {
122                    b'0' + remainder
123                };
124                output_i += 1;
125            }
126            c => {
127                output[output_i] = c;
128                output_i += 1;
129            }
130        }
131        input_i += 1;
132    }
133    output_i
134}
135
136/// Number of bytes written by [`escape_json_string`]
137pub const fn escaped_json_string_len(input: &str) -> usize {
138    let input = input.as_bytes();
139    let mut len = 0;
140    let mut i = 0;
141    while i < input.len() {
142        len += match input[i] {
143            b'\\' | b'"' | 0x08 | 0x0C | b'\n' | b'\r' | b'\t' => 2,
144            0..32 => 6,
145            _ => 1,
146        };
147        i += 1;
148    }
149    len
150}
⚠️ Internal Docs ⚠️ Not Public API 👉 Official Docs Here