pure/
lib.rs

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