pure/
lib.rs

1#![allow(deprecated)]
2
3mod chrono_types;
4mod custom_exceptions;
5mod float_values;
6mod ip_types;
7mod manual_overloading;
8mod manual_submit;
9mod overloading;
10mod overriding;
11mod rust_type_marker;
12mod skip_stub_type_test;
13mod time_types;
14
15use chrono_types::*;
16use custom_exceptions::*;
17use float_values::*;
18use ip_types::*;
19use manual_overloading::*;
20use manual_submit::*;
21use overloading::*;
22use overriding::*;
23use rust_type_marker::*;
24use skip_stub_type_test::*;
25use time_types::*;
26
27#[cfg_attr(target_os = "macos", doc = include_str!("../../../README.md"))]
28mod readme {}
29
30use ahash::RandomState;
31use pyo3::{prelude::*, types::*};
32use pyo3_stub_gen::{
33    define_stub_info_gatherer, derive::*, module_doc, module_variable,
34    runtime::PyModuleTypeAliasExt, type_alias,
35};
36use rust_decimal::Decimal;
37use std::{collections::HashMap, path::PathBuf};
38
39/// Returns the sum of two numbers as a string.
40#[gen_stub_pyfunction]
41#[pyfunction]
42fn sum(v: Vec<u32>) -> u32 {
43    v.iter().sum()
44}
45
46#[gen_stub_pyfunction]
47#[pyfunction]
48fn read_dict(dict: HashMap<usize, HashMap<usize, usize>>) {
49    for (k, v) in dict {
50        for (k2, v2) in v {
51            println!("{k} {k2} {v2}");
52        }
53    }
54}
55
56#[gen_stub_pyfunction]
57#[pyfunction]
58fn create_dict(n: usize) -> HashMap<usize, Vec<usize>> {
59    let mut dict = HashMap::new();
60    for i in 0..n {
61        dict.insert(i, (0..i).collect());
62    }
63    dict
64}
65
66/// Add two decimal numbers with high precision
67#[gen_stub_pyfunction]
68#[pyfunction]
69fn add_decimals(a: Decimal, b: Decimal) -> Decimal {
70    a + b
71}
72
73#[gen_stub_pyclass]
74#[pyclass(extends=PyDate)]
75struct MyDate;
76
77#[gen_stub_pyclass]
78#[pyclass(subclass)]
79#[derive(Debug)]
80struct A {
81    #[gen_stub(default = A::default().x)]
82    #[pyo3(get, set)]
83    x: usize,
84
85    #[pyo3(get)]
86    y: usize,
87}
88
89impl Default for A {
90    fn default() -> Self {
91        Self { x: 2, y: 10 }
92    }
93}
94
95#[gen_stub_pymethods]
96#[pymethods]
97impl A {
98    /// This is a constructor of :class:`A`.
99    #[new]
100    fn new(x: usize) -> Self {
101        Self { x, y: 10 }
102    }
103    /// class attribute NUM1
104    #[classattr]
105    #[pyo3(name = "NUM")]
106    const NUM1: usize = 2;
107
108    /// deprecated class attribute NUM3 (will show warning)
109    #[deprecated(since = "1.0.0", note = "This constant is deprecated")]
110    #[classattr]
111    const NUM3: usize = 3;
112    /// class attribute NUM2
113    #[expect(non_snake_case)]
114    #[classattr]
115    fn NUM2() -> usize {
116        2
117    }
118    #[classmethod]
119    fn classmethod_test1(cls: &Bound<'_, PyType>) {
120        _ = cls;
121    }
122
123    #[deprecated(since = "1.0.0", note = "This classmethod is deprecated")]
124    #[classmethod]
125    fn deprecated_classmethod(cls: &Bound<'_, PyType>) {
126        _ = cls;
127    }
128
129    #[classmethod]
130    fn classmethod_test2(_: &Bound<'_, PyType>) {}
131
132    // Qualified-path classmethod receiver: `cls` must still be detected
133    // as a receiver when the user writes `pyo3::types::PyType` instead of
134    // the unqualified `PyType`.
135    #[classmethod]
136    fn classmethod_test_qualified(cls: &Bound<'_, pyo3::types::PyType>) {
137        _ = cls;
138    }
139
140    fn show_x(&self) {
141        println!("x = {}", self.x);
142    }
143
144    // Test cases: every self-receiver shape `#[pymethods]` accepts
145    // should be rendered as `self` in the generated stub.
146    fn show_x_pyref(slf: PyRef<'_, Self>) -> usize {
147        slf.x
148    }
149
150    fn show_x_pyrefmut(slf: PyRefMut<'_, Self>) -> usize {
151        slf.x
152    }
153
154    fn show_x_bound(slf: Bound<'_, Self>) -> PyResult<usize> {
155        Ok(slf.borrow().x)
156    }
157
158    fn show_x_bound_ref(slf: &Bound<'_, Self>) -> PyResult<usize> {
159        Ok(slf.borrow().x)
160    }
161
162    fn show_x_py(slf: Py<Self>, py: Python<'_>) -> PyResult<usize> {
163        Ok(slf.borrow(py).x)
164    }
165
166    fn ref_test<'a>(&self, x: Bound<'a, PyDict>) -> Bound<'a, PyDict> {
167        x
168    }
169
170    async fn async_get_x(&self) -> usize {
171        self.x
172    }
173
174    #[gen_stub(skip)]
175    fn need_skip(&self) {}
176
177    #[deprecated(since = "1.0.0", note = "This method is deprecated")]
178    fn deprecated_method(&self) {
179        println!("This method is deprecated");
180    }
181
182    #[deprecated(since = "1.0.0", note = "This method is deprecated")]
183    #[getter]
184    fn deprecated_getter(&self) -> usize {
185        self.x
186    }
187
188    #[deprecated(since = "1.0.0", note = "This setter is deprecated")]
189    #[setter]
190    fn set_y(&mut self, value: usize) {
191        self.y = value;
192    }
193
194    #[deprecated(since = "1.0.0", note = "This staticmethod is deprecated")]
195    #[staticmethod]
196    fn deprecated_staticmethod() -> usize {
197        42
198    }
199
200    #[getter]
201    /// Always returns `42`.
202    fn forty_two(&self) -> i32 {
203        42
204    }
205
206    // Test case: #[pyo3(name = "...")] should override the function name for getter
207    #[getter]
208    #[pyo3(name = "renamed_getter")]
209    fn py_renamed_getter(&self) -> i32 {
210        100
211    }
212}
213
214/// Test that setter stubs use `type_input` (e.g. `Sequence`) while getter stubs use `type_output` (e.g. `list`).
215///
216/// For `Vec<i32>`:
217/// - getter should produce `-> list[int]`
218/// - setter should produce `value: Sequence[int]`
219#[gen_stub_pyclass]
220#[pyclass]
221struct GetterSetterTypeTest {
222    values: Vec<i32>,
223}
224
225#[gen_stub_pymethods]
226#[pymethods]
227impl GetterSetterTypeTest {
228    #[new]
229    fn new(values: Vec<i32>) -> Self {
230        Self { values }
231    }
232
233    #[getter]
234    fn values(&self) -> Vec<i32> {
235        self.values.clone()
236    }
237
238    #[setter]
239    fn set_values(&mut self, values: Vec<i32>) {
240        self.values = values;
241    }
242}
243
244#[gen_stub_pyfunction]
245#[pyfunction]
246#[pyo3(signature = (x = 2))]
247fn create_a(x: usize) -> A {
248    A { x, y: 10 }
249}
250
251// Negative regression tests: free `#[pyfunction]`s whose first argument
252// is `Bound<'_, A>` / `&Bound<'_, A>` / `Py<A>` must NOT be mistaken for
253// self receivers — only the literal `Self` spelling is a receiver.
254#[gen_stub_pyfunction]
255#[pyfunction]
256fn echo_a_bound_ref<'py>(a: &Bound<'py, A>) -> Bound<'py, A> {
257    a.clone()
258}
259
260#[gen_stub_pyfunction]
261#[pyfunction]
262fn echo_a_bound(a: Bound<'_, A>) -> Bound<'_, A> {
263    a
264}
265
266#[gen_stub_pyfunction]
267#[pyfunction]
268fn echo_a_py(a: Py<A>) -> Py<A> {
269    a
270}
271
272#[gen_stub_pyclass]
273#[pyclass(extends=A)]
274#[derive(Debug)]
275struct B;
276
277/// `C` only impl `FromPyObject`
278#[derive(Debug)]
279struct C {
280    x: usize,
281}
282#[gen_stub_pyfunction]
283#[pyfunction(signature = (c=None))]
284fn print_c(c: Option<C>) {
285    if let Some(c) = c {
286        println!("{}", c.x);
287    } else {
288        println!("None");
289    }
290}
291impl FromPyObject<'_, '_> for C {
292    type Error = PyErr;
293
294    fn extract(ob: Borrowed<'_, '_, PyAny>) -> std::result::Result<Self, Self::Error> {
295        Ok(C { x: ob.extract()? })
296    }
297}
298impl pyo3_stub_gen::PyStubType for C {
299    fn type_output() -> pyo3_stub_gen::TypeInfo {
300        usize::type_output()
301    }
302}
303impl pyo3_stub_gen::runtime::PyRuntimeType for C {
304    fn runtime_type_object(
305        py: ::pyo3::Python<'_>,
306    ) -> ::pyo3::PyResult<::pyo3::Bound<'_, ::pyo3::PyAny>> {
307        usize::runtime_type_object(py)
308    }
309}
310
311/// Returns the length of the string.
312#[gen_stub_pyfunction]
313#[pyfunction]
314fn str_len(x: &str) -> PyResult<usize> {
315    Ok(x.len())
316}
317
318#[gen_stub_pyfunction]
319#[pyfunction]
320fn echo_path(path: PathBuf) -> PyResult<PathBuf> {
321    Ok(path)
322}
323
324#[gen_stub_pyfunction]
325#[pyfunction]
326fn ahash_dict() -> HashMap<String, i32, RandomState> {
327    let mut map: HashMap<String, i32, RandomState> = HashMap::with_hasher(RandomState::new());
328    map.insert("apple".to_string(), 3);
329    map.insert("banana".to_string(), 2);
330    map.insert("orange".to_string(), 5);
331    map
332}
333
334#[gen_stub_pyclass_enum]
335#[pyclass(eq, eq_int, from_py_object)]
336#[derive(Debug, Clone, PartialEq, Eq, Hash)]
337pub enum Number {
338    #[pyo3(name = "FLOAT")]
339    Float,
340    #[pyo3(name = "INTEGER")]
341    Integer,
342}
343
344#[gen_stub_pyclass_enum]
345#[pyclass(eq, eq_int, from_py_object)]
346#[pyo3(rename_all = "UPPERCASE")]
347#[derive(Debug, Clone, PartialEq, Eq, Hash)]
348pub enum NumberRenameAll {
349    /// Float variant
350    Float,
351    Integer,
352}
353
354#[gen_stub_pyclass_complex_enum]
355#[pyclass(from_py_object)]
356#[pyo3(rename_all = "UPPERCASE")]
357#[derive(Debug, Clone)]
358pub enum NumberComplex {
359    /// Float variant
360    Float(f64),
361    /// Integer variant
362    #[pyo3(constructor = (int=2))]
363    Integer {
364        /// The integer value
365        int: i32,
366    },
367}
368
369/// Example from PyO3 documentation for complex enum
370/// https://pyo3.rs/v0.25.1/class.html#complex-enums
371#[gen_stub_pyclass_complex_enum]
372#[pyclass]
373enum Shape1 {
374    Circle { radius: f64 },
375    Rectangle { width: f64, height: f64 },
376    RegularPolygon(u32, f64),
377    Nothing {},
378}
379
380/// Example from PyO3 documentation for complex enum
381/// https://pyo3.rs/v0.25.1/class.html#complex-enums
382#[gen_stub_pyclass_complex_enum]
383#[pyclass]
384enum Shape2 {
385    #[pyo3(constructor = (radius=1.0))]
386    Circle {
387        radius: f64,
388    },
389    #[pyo3(constructor = (*, width, height))]
390    Rectangle {
391        width: f64,
392        height: f64,
393    },
394    #[pyo3(constructor = (side_count, radius=1.0))]
395    RegularPolygon {
396        side_count: u32,
397        radius: f64,
398    },
399    Nothing {},
400}
401
402#[gen_stub_pymethods]
403#[pymethods]
404impl Number {
405    #[getter]
406    /// Whether the number is a float.
407    fn is_float(&self) -> bool {
408        matches!(self, Self::Float)
409    }
410
411    #[getter]
412    /// Whether the number is an integer.
413    fn is_integer(&self) -> bool {
414        matches!(self, Self::Integer)
415    }
416}
417
418#[gen_stub_pyclass]
419#[pyclass]
420pub struct DecimalHolder {
421    #[pyo3(get)]
422    value: Decimal,
423}
424
425#[gen_stub_pymethods]
426#[pymethods]
427impl DecimalHolder {
428    #[new]
429    fn new(value: Decimal) -> Self {
430        Self { value }
431    }
432}
433
434module_variable!("pure", "MY_CONSTANT1", usize);
435module_variable!("pure", "MY_CONSTANT2", usize, 123);
436
437#[gen_stub_pyfunction]
438#[pyfunction]
439async fn async_num() -> i32 {
440    123
441}
442
443#[gen_stub_pyfunction]
444#[pyfunction]
445#[deprecated(since = "1.0.0", note = "This function is deprecated")]
446fn deprecated_function() {
447    println!("This function is deprecated");
448}
449
450// Test if non-any Py<PyAny> target can be a default value
451#[gen_stub_pyfunction]
452#[pyfunction]
453#[pyo3(signature = (num = Number::Float))]
454fn default_value(num: Number) -> Number {
455    num
456}
457
458// These are the tests to test the treatment of `*args` and `**kwargs` in functions
459
460/// Test struct for eq and ord comparison methods
461#[gen_stub_pyclass]
462#[pyclass(eq, ord, from_py_object)]
463#[derive(Debug, Clone, PartialEq, PartialOrd)]
464pub struct ComparableStruct {
465    #[pyo3(get)]
466    pub value: i32,
467}
468
469#[gen_stub_pymethods]
470#[pymethods]
471impl ComparableStruct {
472    #[new]
473    fn new(value: i32) -> Self {
474        Self { value }
475    }
476}
477
478/// Test struct for hash and str methods
479#[gen_stub_pyclass]
480#[pyclass(eq, hash, frozen, str, from_py_object)]
481#[derive(Debug, Clone, Hash, PartialEq)]
482pub struct HashableStruct {
483    #[pyo3(get)]
484    pub name: String,
485}
486
487impl std::fmt::Display for HashableStruct {
488    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
489        write!(f, "HashableStruct({})", self.name)
490    }
491}
492
493#[gen_stub_pymethods]
494#[pymethods]
495impl HashableStruct {
496    #[new]
497    fn new(name: String) -> Self {
498        Self { name }
499    }
500}
501
502/// Takes a variable number of arguments and returns their string representation.
503#[gen_stub_pyfunction]
504#[pyfunction]
505#[pyo3(signature = (*args))]
506fn func_with_star_arg_typed(
507    #[gen_stub(override_type(type_repr = "str"))] args: &Bound<PyTuple>,
508) -> String {
509    args.to_string()
510}
511
512/// Takes a variable number of arguments and returns their string representation.
513#[gen_stub_pyfunction]
514#[pyfunction]
515#[pyo3(signature = (*args))]
516fn func_with_star_arg(args: &Bound<PyTuple>) -> String {
517    args.to_string()
518}
519
520/// Takes a variable number of keyword arguments and does nothing
521#[gen_stub_pyfunction]
522#[pyfunction]
523#[pyo3(signature = (**kwargs))]
524fn func_with_kwargs(kwargs: Option<&Bound<PyDict>>) -> bool {
525    kwargs.is_some()
526}
527
528module_doc!("pure", "Document for {} ...", env!("CARGO_PKG_NAME"));
529
530/// Initializes the Python module
531#[pymodule]
532fn pure(m: &Bound<PyModule>) -> PyResult<()> {
533    m.add("MY_CONSTANT1", 19937)?;
534    m.add("MY_CONSTANT2", 123)?;
535    m.add_class::<A>()?;
536    m.add_class::<B>()?;
537    m.add_class::<MyDate>()?;
538    m.add_class::<Number>()?;
539    m.add_class::<NumberRenameAll>()?;
540    m.add_class::<NumberComplex>()?;
541    m.add_class::<Shape1>()?;
542    m.add_class::<Shape2>()?;
543    m.add_class::<ManualSubmit>()?;
544    m.add_class::<PartialManualSubmit>()?;
545    m.add_class::<OverrideType>()?;
546    m.add_class::<ComparableStruct>()?;
547    m.add_class::<HashableStruct>()?;
548    m.add_class::<DecimalHolder>()?;
549    m.add_class::<DataContainer>()?;
550    m.add_class::<Placeholder>()?;
551    m.add_class::<Calculator>()?;
552    m.add_class::<InstanceValue>()?;
553    m.add_class::<Problem>()?;
554    m.add_class::<CustomStubType>()?;
555    m.add_class::<NormalClass>()?;
556    m.add_class::<CustomEnum>()?;
557    m.add_class::<CustomComplexEnum>()?;
558    m.add_class::<GetterSetterTypeTest>()?;
559    m.add_function(wrap_pyfunction!(sum, m)?)?;
560    m.add_function(wrap_pyfunction!(create_dict, m)?)?;
561    m.add_function(wrap_pyfunction!(read_dict, m)?)?;
562    m.add_function(wrap_pyfunction!(create_a, m)?)?;
563    m.add_function(wrap_pyfunction!(echo_a_bound_ref, m)?)?;
564    m.add_function(wrap_pyfunction!(echo_a_bound, m)?)?;
565    m.add_function(wrap_pyfunction!(echo_a_py, m)?)?;
566    m.add_function(wrap_pyfunction!(print_c, m)?)?;
567    m.add_function(wrap_pyfunction!(str_len, m)?)?;
568    m.add_function(wrap_pyfunction!(echo_path, m)?)?;
569    m.add_function(wrap_pyfunction!(ahash_dict, m)?)?;
570    m.add_function(wrap_pyfunction!(async_num, m)?)?;
571    m.add_function(wrap_pyfunction!(deprecated_function, m)?)?;
572    m.add_function(wrap_pyfunction!(default_value, m)?)?;
573    m.add_function(wrap_pyfunction!(fn_override_type, m)?)?;
574    m.add_function(wrap_pyfunction!(fn_with_python_param, m)?)?;
575    m.add_function(wrap_pyfunction!(fn_with_python_stub, m)?)?;
576    m.add_function(wrap_pyfunction!(overload_example_1, m)?)?;
577    m.add_function(wrap_pyfunction!(overload_example_2, m)?)?;
578    m.add_function(wrap_pyfunction!(as_tuple, m)?)?;
579    m.add_function(wrap_pyfunction!(manual_overload_example_1, m)?)?;
580    m.add_function(wrap_pyfunction!(manual_overload_example_2, m)?)?;
581    m.add_function(wrap_pyfunction!(manual_overload_as_tuple, m)?)?;
582    m.add_function(wrap_pyfunction!(add_decimals, m)?)?;
583    m.add_function(wrap_pyfunction!(process_container, m)?)?;
584    m.add_function(wrap_pyfunction!(sum_list, m)?)?;
585    m.add_function(wrap_pyfunction!(create_containers, m)?)?;
586    // Test-cases for `*args` and `**kwargs`
587    m.add_function(wrap_pyfunction!(func_with_star_arg, m)?)?;
588    m.add_function(wrap_pyfunction!(func_with_star_arg_typed, m)?)?;
589    m.add_function(wrap_pyfunction!(func_with_kwargs, m)?)?;
590
591    // Test cases for type: ignore functionality
592    m.add_function(wrap_pyfunction!(test_type_ignore_specific, m)?)?;
593    m.add_function(wrap_pyfunction!(test_type_ignore_all, m)?)?;
594    m.add_function(wrap_pyfunction!(test_type_ignore_pyright, m)?)?;
595    m.add_function(wrap_pyfunction!(test_type_ignore_custom, m)?)?;
596    m.add_function(wrap_pyfunction!(test_type_ignore_no_comment_all, m)?)?;
597    m.add_function(wrap_pyfunction!(test_type_ignore_no_comment_specific, m)?)?;
598
599    // Test case for custom exceptions
600    m.add("MyError", m.py().get_type::<MyError>())?;
601    m.add_class::<NotIntError>()?;
602
603    // Test case for runtime type alias (type_alias! macro)
604    m.add_type_alias::<RuntimeNumberOrString>()?;
605
606    // Test class for type: ignore functionality
607    m.add_class::<TypeIgnoreTest>()?;
608
609    // Test cases for time crate types
610    m.add_function(wrap_pyfunction!(get_date, m)?)?;
611    m.add_function(wrap_pyfunction!(get_time, m)?)?;
612    m.add_function(wrap_pyfunction!(get_duration, m)?)?;
613    m.add_function(wrap_pyfunction!(get_primitive_datetime, m)?)?;
614    m.add_function(wrap_pyfunction!(get_offset_datetime, m)?)?;
615    m.add_function(wrap_pyfunction!(get_utc_offset, m)?)?;
616    m.add_function(wrap_pyfunction!(get_utc_datetime, m)?)?;
617    m.add_function(wrap_pyfunction!(add_duration_to_date, m)?)?;
618    m.add_function(wrap_pyfunction!(time_difference, m)?)?;
619
620    // Test cases for chrono crate types
621    m.add_function(wrap_pyfunction!(get_naive_date, m)?)?;
622    m.add_function(wrap_pyfunction!(get_naive_time, m)?)?;
623    m.add_function(wrap_pyfunction!(get_naive_datetime, m)?)?;
624    m.add_function(wrap_pyfunction!(get_datetime_utc, m)?)?;
625    m.add_function(wrap_pyfunction!(get_datetime_fixed_offset, m)?)?;
626    m.add_function(wrap_pyfunction!(get_chrono_duration, m)?)?;
627    m.add_function(wrap_pyfunction!(get_fixed_offset, m)?)?;
628    m.add_function(wrap_pyfunction!(get_utc, m)?)?;
629    m.add_function(wrap_pyfunction!(add_chrono_duration_to_date, m)?)?;
630    m.add_function(wrap_pyfunction!(naive_time_difference, m)?)?;
631
632    // Test cases for std::net IP address types
633    m.add_function(wrap_pyfunction!(ipv4_localhost, m)?)?;
634    m.add_function(wrap_pyfunction!(ipv6_localhost, m)?)?;
635    m.add_function(wrap_pyfunction!(parse_ip, m)?)?;
636    m.add_function(wrap_pyfunction!(is_loopback, m)?)?;
637
638    // Test cases for f64 special values (INFINITY, NEG_INFINITY, NAN)
639    m.add_class::<FloatValues>()?;
640    m.add_function(wrap_pyfunction!(with_infinity_default, m)?)?;
641    m.add_function(wrap_pyfunction!(with_neg_infinity_default, m)?)?;
642    m.add_function(wrap_pyfunction!(with_nan_default, m)?)?;
643    m.add_function(wrap_pyfunction!(with_float_default, m)?)?;
644    Ok(())
645}
646
647/// Test function with type: ignore for specific rules
648#[gen_stub_pyfunction]
649#[gen_stub(type_ignore = ["arg-type", "return-value"])]
650#[pyfunction]
651fn test_type_ignore_specific() -> i32 {
652    42
653}
654
655/// Test function with type: ignore (without equals for catch-all)
656#[gen_stub_pyfunction]
657#[gen_stub(type_ignore)]
658#[pyfunction]
659fn test_type_ignore_all() -> i32 {
660    42
661}
662
663/// Test function with Pyright diagnostic rules
664#[gen_stub_pyfunction]
665#[gen_stub(type_ignore = ["reportGeneralTypeIssues", "reportReturnType"])]
666#[pyfunction]
667fn test_type_ignore_pyright() -> i32 {
668    42
669}
670
671/// Test function with custom (unknown) rule
672#[gen_stub_pyfunction]
673#[gen_stub(type_ignore = ["custom-rule", "attr-defined"])]
674#[pyfunction]
675fn test_type_ignore_custom() -> i32 {
676    42
677}
678
679// NOTE: Doc-comment MUST NOT be added to the next function,
680// as it tests if `type_ignore` without no doccomment is handled correctly;
681// i.e. it emits comment after `...`, not before.
682
683#[gen_stub_pyfunction]
684#[gen_stub(type_ignore)]
685#[pyfunction]
686fn test_type_ignore_no_comment_all() -> i32 {
687    42
688}
689
690#[gen_stub_pyfunction]
691#[gen_stub(type_ignore=["arg-type", "reportIncompatibleMethodOverride"])]
692#[pyfunction]
693fn test_type_ignore_no_comment_specific() -> i32 {
694    42
695}
696
697/// Test class for method type: ignore functionality
698#[gen_stub_pyclass]
699#[pyclass]
700pub struct TypeIgnoreTest {}
701
702#[gen_stub_pymethods]
703#[pymethods]
704impl TypeIgnoreTest {
705    #[new]
706    fn new() -> Self {
707        Self {}
708    }
709
710    /// Test method with type: ignore for specific rules
711    #[gen_stub(type_ignore = ["union-attr", "return-value"])]
712    fn test_method_ignore(&self, value: i32) -> i32 {
713        value * 2
714    }
715
716    /// Test method with type: ignore (without equals for catch-all)
717    #[gen_stub(type_ignore)]
718    fn test_method_all_ignore(&self) -> i32 {
719        42
720    }
721}
722
723// Test type aliases WITHOUT docstrings (backward compatibility)
724pyo3_stub_gen::type_alias!("pure", SimpleAlias = Option<usize>);
725pyo3_stub_gen::type_alias!("pure", StrIntMap = HashMap<String, i32>);
726
727// Type alias referring to locally defined class
728pyo3_stub_gen::type_alias!(
729    "pure",
730    MaybeDecimal = Option<Bound<'static, DecimalHolder>>
731);
732
733// Direct union type syntax (no impl_stub_type! needed)
734pyo3_stub_gen::type_alias!("pure", NumberOrStringAlias = i32 | String);
735
736// Union of locally defined types using direct syntax
737pyo3_stub_gen::type_alias!("pure", StructUnion = Bound<'static, ComparableStruct> | Bound<'static, HashableStruct>);
738
739// Additional test cases for the new syntax
740pyo3_stub_gen::type_alias!("pure", TripleUnion = i32 | String | bool);
741pyo3_stub_gen::type_alias!("pure", GenericUnion = Option<i32> | Vec<String>);
742pyo3_stub_gen::type_alias!("pure", SingleTypeAlias = Option<usize>); // Backward compatibility test
743
744// Test type aliases WITH docstrings
745pyo3_stub_gen::type_alias!(
746    "pure",
747    DocumentedAlias = Option<usize>,
748    "This is a simple type alias with documentation"
749);
750
751pyo3_stub_gen::type_alias!(
752    "pure",
753    DocumentedUnion = i32 | String,
754    "A union type with documentation"
755);
756
757pyo3_stub_gen::type_alias!(
758    "pure",
759    DocumentedMap = HashMap<String, Vec<i32>>,
760    "A map type alias with detailed documentation.\n\nThis can have multiple lines of documentation."
761);
762
763// Test runtime type alias using type_alias! macro
764// This type alias is available both in stubs AND at runtime
765// Uses Rust types which are mapped to Python types via PyRuntimeType::runtime_type_object
766type_alias!(
767    "pure",
768    RuntimeNumberOrString = i32 | String,
769    "Either an integer or a string, available at runtime."
770);
771
772// Test type aliases using Python syntax (without docstrings)
773pyo3_stub_gen::derive::gen_type_alias_from_python!(
774    "pure",
775    r#"
776    from typing import TypeAlias
777    import collections.abc
778
779    CallbackType: TypeAlias = collections.abc.Callable[[str], None]
780    OptionalCallback: TypeAlias = collections.abc.Callable[[str], None] | None
781    SequenceOfInts: TypeAlias = collections.abc.Sequence[int]
782    "#
783);
784
785// Test type aliases using Python syntax (with docstrings)
786pyo3_stub_gen::derive::gen_type_alias_from_python!(
787    "pure",
788    r#"
789    from typing import TypeAlias
790    import collections.abc
791
792    DocumentedCallback: TypeAlias = collections.abc.Callable[[str], None]
793    """A callback function that takes a string and returns nothing"""
794
795    UndocumentedCallback: TypeAlias = collections.abc.Callable[[int], bool]
796
797    MultiLineDocCallback: TypeAlias = collections.abc.Callable[[str, int], bool]
798    """
799    A callback with multi-line documentation.
800
801    This callback takes a string and an integer, and returns a boolean.
802    """
803    "#
804);
805
806// Test RustType markers in type aliases (TypeAlias syntax)
807pyo3_stub_gen::derive::gen_type_alias_from_python!(
808    "pure",
809    r#"
810    from typing import TypeAlias
811
812    SimpleContainer: TypeAlias = pyo3_stub_gen.RustType["DataContainer"]
813    ContainerList: TypeAlias = list[pyo3_stub_gen.RustType["DataContainer"]]
814    ContainerMap: TypeAlias = dict[str, pyo3_stub_gen.RustType["DataContainer"]]
815    OptionalContainer: TypeAlias = pyo3_stub_gen.RustType["DataContainer"] | None
816    "#
817);
818
819// Test RustType markers in type aliases (Python 3.12+ type statement syntax)
820pyo3_stub_gen::derive::gen_type_alias_from_python!(
821    "pure",
822    r#"
823    type ContainerTuple = tuple[pyo3_stub_gen.RustType["DataContainer"], pyo3_stub_gen.RustType["DataContainer"]]
824    type NestedContainer = list[list[pyo3_stub_gen.RustType["DataContainer"]]]
825    "#
826);
827
828define_stub_info_gatherer!(stub_info);
829
830/// Test of unit test for testing link problem
831#[cfg(test)]
832mod test {
833    #[test]
834    fn test() {
835        assert_eq!(2 + 2, 4);
836    }
837}