pure/
lib.rs

1#![allow(deprecated)]
2
3mod custom_exceptions;
4mod manual_submit;
5mod overloading;
6mod overriding;
7
8use custom_exceptions::*;
9use manual_submit::*;
10use overloading::*;
11use overriding::*;
12
13#[cfg_attr(target_os = "macos", doc = include_str!("../../../README.md"))]
14mod readme {}
15
16use ahash::RandomState;
17use pyo3::{prelude::*, types::*};
18use pyo3_stub_gen::{define_stub_info_gatherer, derive::*, module_doc, module_variable};
19use rust_decimal::Decimal;
20use std::{collections::HashMap, path::PathBuf};
21
22/// Returns the sum of two numbers as a string.
23#[gen_stub_pyfunction]
24#[pyfunction]
25fn sum(v: Vec<u32>) -> u32 {
26    v.iter().sum()
27}
28
29#[gen_stub_pyfunction]
30#[pyfunction]
31fn read_dict(dict: HashMap<usize, HashMap<usize, usize>>) {
32    for (k, v) in dict {
33        for (k2, v2) in v {
34            println!("{k} {k2} {v2}");
35        }
36    }
37}
38
39#[gen_stub_pyfunction]
40#[pyfunction]
41fn create_dict(n: usize) -> HashMap<usize, Vec<usize>> {
42    let mut dict = HashMap::new();
43    for i in 0..n {
44        dict.insert(i, (0..i).collect());
45    }
46    dict
47}
48
49/// Add two decimal numbers with high precision
50#[gen_stub_pyfunction]
51#[pyfunction]
52fn add_decimals(a: Decimal, b: Decimal) -> Decimal {
53    a + b
54}
55
56#[gen_stub_pyclass]
57#[pyclass(extends=PyDate)]
58struct MyDate;
59
60#[gen_stub_pyclass]
61#[pyclass(subclass)]
62#[derive(Debug)]
63struct A {
64    #[gen_stub(default = A::default().x)]
65    #[pyo3(get, set)]
66    x: usize,
67
68    #[pyo3(get)]
69    y: usize,
70}
71
72impl Default for A {
73    fn default() -> Self {
74        Self { x: 2, y: 10 }
75    }
76}
77
78#[gen_stub_pymethods]
79#[pymethods]
80impl A {
81    /// This is a constructor of :class:`A`.
82    #[new]
83    fn new(x: usize) -> Self {
84        Self { x, y: 10 }
85    }
86    /// class attribute NUM1
87    #[classattr]
88    #[pyo3(name = "NUM")]
89    const NUM1: usize = 2;
90
91    /// deprecated class attribute NUM3 (will show warning)
92    #[deprecated(since = "1.0.0", note = "This constant is deprecated")]
93    #[classattr]
94    const NUM3: usize = 3;
95    /// class attribute NUM2
96    #[expect(non_snake_case)]
97    #[classattr]
98    fn NUM2() -> usize {
99        2
100    }
101    #[classmethod]
102    fn classmethod_test1(cls: &Bound<'_, PyType>) {
103        _ = cls;
104    }
105
106    #[deprecated(since = "1.0.0", note = "This classmethod is deprecated")]
107    #[classmethod]
108    fn deprecated_classmethod(cls: &Bound<'_, PyType>) {
109        _ = cls;
110    }
111
112    #[classmethod]
113    fn classmethod_test2(_: &Bound<'_, PyType>) {}
114
115    fn show_x(&self) {
116        println!("x = {}", self.x);
117    }
118
119    fn ref_test<'a>(&self, x: Bound<'a, PyDict>) -> Bound<'a, PyDict> {
120        x
121    }
122
123    async fn async_get_x(&self) -> usize {
124        self.x
125    }
126
127    #[gen_stub(skip)]
128    fn need_skip(&self) {}
129
130    #[deprecated(since = "1.0.0", note = "This method is deprecated")]
131    fn deprecated_method(&self) {
132        println!("This method is deprecated");
133    }
134
135    #[deprecated(since = "1.0.0", note = "This method is deprecated")]
136    #[getter]
137    fn deprecated_getter(&self) -> usize {
138        self.x
139    }
140
141    #[deprecated(since = "1.0.0", note = "This setter is deprecated")]
142    #[setter]
143    fn set_y(&mut self, value: usize) {
144        self.y = value;
145    }
146
147    #[deprecated(since = "1.0.0", note = "This staticmethod is deprecated")]
148    #[staticmethod]
149    fn deprecated_staticmethod() -> usize {
150        42
151    }
152}
153
154#[gen_stub_pyfunction]
155#[pyfunction]
156#[pyo3(signature = (x = 2))]
157fn create_a(x: usize) -> A {
158    A { x, y: 10 }
159}
160
161#[gen_stub_pyclass]
162#[pyclass(extends=A)]
163#[derive(Debug)]
164struct B;
165
166/// `C` only impl `FromPyObject`
167#[derive(Debug)]
168struct C {
169    x: usize,
170}
171#[gen_stub_pyfunction]
172#[pyfunction(signature = (c=None))]
173fn print_c(c: Option<C>) {
174    if let Some(c) = c {
175        println!("{}", c.x);
176    } else {
177        println!("None");
178    }
179}
180impl FromPyObject<'_> for C {
181    fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<Self> {
182        Ok(C { x: ob.extract()? })
183    }
184}
185impl pyo3_stub_gen::PyStubType for C {
186    fn type_output() -> pyo3_stub_gen::TypeInfo {
187        usize::type_output()
188    }
189}
190
191/// Returns the length of the string.
192#[gen_stub_pyfunction]
193#[pyfunction]
194fn str_len(x: &str) -> PyResult<usize> {
195    Ok(x.len())
196}
197
198#[gen_stub_pyfunction]
199#[pyfunction]
200fn echo_path(path: PathBuf) -> PyResult<PathBuf> {
201    Ok(path)
202}
203
204#[gen_stub_pyfunction]
205#[pyfunction]
206fn ahash_dict() -> HashMap<String, i32, RandomState> {
207    let mut map: HashMap<String, i32, RandomState> = HashMap::with_hasher(RandomState::new());
208    map.insert("apple".to_string(), 3);
209    map.insert("banana".to_string(), 2);
210    map.insert("orange".to_string(), 5);
211    map
212}
213
214#[gen_stub_pyclass_enum]
215#[pyclass(eq, eq_int)]
216#[derive(Debug, Clone, PartialEq, Eq, Hash)]
217pub enum Number {
218    #[pyo3(name = "FLOAT")]
219    Float,
220    #[pyo3(name = "INTEGER")]
221    Integer,
222}
223
224#[gen_stub_pyclass_enum]
225#[pyclass(eq, eq_int)]
226#[pyo3(rename_all = "UPPERCASE")]
227#[derive(Debug, Clone, PartialEq, Eq, Hash)]
228pub enum NumberRenameAll {
229    /// Float variant
230    Float,
231    Integer,
232}
233
234#[gen_stub_pyclass_complex_enum]
235#[pyclass]
236#[pyo3(rename_all = "UPPERCASE")]
237#[derive(Debug, Clone)]
238pub enum NumberComplex {
239    /// Float variant
240    Float(f64),
241    /// Integer variant
242    #[pyo3(constructor = (int=2))]
243    Integer {
244        /// The integer value
245        int: i32,
246    },
247}
248
249/// Example from PyO3 documentation for complex enum
250/// https://pyo3.rs/v0.25.1/class.html#complex-enums
251#[gen_stub_pyclass_complex_enum]
252#[pyclass]
253enum Shape1 {
254    Circle { radius: f64 },
255    Rectangle { width: f64, height: f64 },
256    RegularPolygon(u32, f64),
257    Nothing {},
258}
259
260/// Example from PyO3 documentation for complex enum
261/// https://pyo3.rs/v0.25.1/class.html#complex-enums
262#[gen_stub_pyclass_complex_enum]
263#[pyclass]
264enum Shape2 {
265    #[pyo3(constructor = (radius=1.0))]
266    Circle {
267        radius: f64,
268    },
269    #[pyo3(constructor = (*, width, height))]
270    Rectangle {
271        width: f64,
272        height: f64,
273    },
274    #[pyo3(constructor = (side_count, radius=1.0))]
275    RegularPolygon {
276        side_count: u32,
277        radius: f64,
278    },
279    Nothing {},
280}
281
282#[gen_stub_pymethods]
283#[pymethods]
284impl Number {
285    #[getter]
286    /// Whether the number is a float.
287    fn is_float(&self) -> bool {
288        matches!(self, Self::Float)
289    }
290
291    #[getter]
292    /// Whether the number is an integer.
293    fn is_integer(&self) -> bool {
294        matches!(self, Self::Integer)
295    }
296}
297
298#[gen_stub_pyclass]
299#[pyclass]
300pub struct DecimalHolder {
301    #[pyo3(get)]
302    value: Decimal,
303}
304
305#[gen_stub_pymethods]
306#[pymethods]
307impl DecimalHolder {
308    #[new]
309    fn new(value: Decimal) -> Self {
310        Self { value }
311    }
312}
313
314module_variable!("pure", "MY_CONSTANT1", usize);
315module_variable!("pure", "MY_CONSTANT2", usize, 123);
316
317#[gen_stub_pyfunction]
318#[pyfunction]
319async fn async_num() -> i32 {
320    123
321}
322
323#[gen_stub_pyfunction]
324#[pyfunction]
325#[deprecated(since = "1.0.0", note = "This function is deprecated")]
326fn deprecated_function() {
327    println!("This function is deprecated");
328}
329
330// Test if non-any PyObject Target can be a default value
331#[gen_stub_pyfunction]
332#[pyfunction]
333#[pyo3(signature = (num = Number::Float))]
334fn default_value(num: Number) -> Number {
335    num
336}
337
338// These are the tests to test the treatment of `*args` and `**kwargs` in functions
339
340/// Test struct for eq and ord comparison methods
341#[gen_stub_pyclass]
342#[pyclass(eq, ord)]
343#[derive(Debug, Clone, PartialEq, PartialOrd)]
344pub struct ComparableStruct {
345    #[pyo3(get)]
346    pub value: i32,
347}
348
349#[gen_stub_pymethods]
350#[pymethods]
351impl ComparableStruct {
352    #[new]
353    fn new(value: i32) -> Self {
354        Self { value }
355    }
356}
357
358/// Test struct for hash and str methods
359#[gen_stub_pyclass]
360#[pyclass(eq, hash, frozen, str)]
361#[derive(Debug, Clone, Hash, PartialEq)]
362pub struct HashableStruct {
363    #[pyo3(get)]
364    pub name: String,
365}
366
367impl std::fmt::Display for HashableStruct {
368    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
369        write!(f, "HashableStruct({})", self.name)
370    }
371}
372
373#[gen_stub_pymethods]
374#[pymethods]
375impl HashableStruct {
376    #[new]
377    fn new(name: String) -> Self {
378        Self { name }
379    }
380}
381
382/// Takes a variable number of arguments and returns their string representation.
383#[gen_stub_pyfunction]
384#[pyfunction]
385#[pyo3(signature = (*args))]
386fn func_with_star_arg(args: &Bound<PyTuple>) -> String {
387    args.to_string()
388}
389
390/// Takes a variable number of keyword arguments and does nothing
391#[gen_stub_pyfunction]
392#[pyfunction]
393#[pyo3(signature = (**kwargs))]
394fn func_with_kwargs(kwargs: Option<&Bound<PyDict>>) -> bool {
395    kwargs.is_some()
396}
397
398module_doc!("pure", "Document for {} ...", env!("CARGO_PKG_NAME"));
399
400/// Initializes the Python module
401#[pymodule]
402fn pure(m: &Bound<PyModule>) -> PyResult<()> {
403    m.add("MY_CONSTANT1", 19937)?;
404    m.add("MY_CONSTANT2", 123)?;
405    m.add_class::<A>()?;
406    m.add_class::<B>()?;
407    m.add_class::<MyDate>()?;
408    m.add_class::<Number>()?;
409    m.add_class::<NumberRenameAll>()?;
410    m.add_class::<NumberComplex>()?;
411    m.add_class::<Shape1>()?;
412    m.add_class::<Shape2>()?;
413    m.add_class::<Incrementer>()?;
414    m.add_class::<Incrementer2>()?;
415    m.add_class::<OverrideType>()?;
416    m.add_class::<ComparableStruct>()?;
417    m.add_class::<HashableStruct>()?;
418    m.add_class::<DecimalHolder>()?;
419    m.add_function(wrap_pyfunction!(sum, m)?)?;
420    m.add_function(wrap_pyfunction!(create_dict, m)?)?;
421    m.add_function(wrap_pyfunction!(read_dict, m)?)?;
422    m.add_function(wrap_pyfunction!(create_a, m)?)?;
423    m.add_function(wrap_pyfunction!(print_c, m)?)?;
424    m.add_function(wrap_pyfunction!(str_len, m)?)?;
425    m.add_function(wrap_pyfunction!(echo_path, m)?)?;
426    m.add_function(wrap_pyfunction!(ahash_dict, m)?)?;
427    m.add_function(wrap_pyfunction!(async_num, m)?)?;
428    m.add_function(wrap_pyfunction!(deprecated_function, m)?)?;
429    m.add_function(wrap_pyfunction!(default_value, m)?)?;
430    m.add_function(wrap_pyfunction!(fn_override_type, m)?)?;
431    m.add_function(wrap_pyfunction!(fn_with_python_param, m)?)?;
432    m.add_function(wrap_pyfunction!(fn_with_python_stub, m)?)?;
433    m.add_function(wrap_pyfunction!(overload_example_1, m)?)?;
434    m.add_function(wrap_pyfunction!(overload_example_2, m)?)?;
435    m.add_function(wrap_pyfunction!(add_decimals, m)?)?;
436    // Test-cases for `*args` and `**kwargs`
437    m.add_function(wrap_pyfunction!(func_with_star_arg, m)?)?;
438    m.add_function(wrap_pyfunction!(func_with_kwargs, m)?)?;
439
440    // Test cases for type: ignore functionality
441    m.add_function(wrap_pyfunction!(test_type_ignore_specific, m)?)?;
442    m.add_function(wrap_pyfunction!(test_type_ignore_all, m)?)?;
443    m.add_function(wrap_pyfunction!(test_type_ignore_pyright, m)?)?;
444    m.add_function(wrap_pyfunction!(test_type_ignore_custom, m)?)?;
445    m.add_function(wrap_pyfunction!(test_type_ignore_no_comment_all, m)?)?;
446    m.add_function(wrap_pyfunction!(test_type_ignore_no_comment_specific, m)?)?;
447
448    // Test case for custom exceptions
449    m.add("MyError", m.py().get_type::<MyError>())?;
450    m.add_class::<NotIntError>()?;
451
452    // Test class for type: ignore functionality
453    m.add_class::<TypeIgnoreTest>()?;
454    Ok(())
455}
456
457/// Test function with type: ignore for specific rules
458#[gen_stub_pyfunction]
459#[gen_stub(type_ignore = ["arg-type", "return-value"])]
460#[pyfunction]
461fn test_type_ignore_specific() -> i32 {
462    42
463}
464
465/// Test function with type: ignore (without equals for catch-all)
466#[gen_stub_pyfunction]
467#[gen_stub(type_ignore)]
468#[pyfunction]
469fn test_type_ignore_all() -> i32 {
470    42
471}
472
473/// Test function with Pyright diagnostic rules
474#[gen_stub_pyfunction]
475#[gen_stub(type_ignore = ["reportGeneralTypeIssues", "reportReturnType"])]
476#[pyfunction]
477fn test_type_ignore_pyright() -> i32 {
478    42
479}
480
481/// Test function with custom (unknown) rule
482#[gen_stub_pyfunction]
483#[gen_stub(type_ignore = ["custom-rule", "attr-defined"])]
484#[pyfunction]
485fn test_type_ignore_custom() -> i32 {
486    42
487}
488
489// NOTE: Doc-comment MUST NOT be added to the next function,
490// as it tests if `type_ignore` without no doccomment is handled correctly;
491// i.e. it emits comment after `...`, not before.
492
493#[gen_stub_pyfunction]
494#[gen_stub(type_ignore)]
495#[pyfunction]
496fn test_type_ignore_no_comment_all() -> i32 {
497    42
498}
499
500#[gen_stub_pyfunction]
501#[gen_stub(type_ignore=["arg-type", "reportIncompatibleMethodOverride"])]
502#[pyfunction]
503fn test_type_ignore_no_comment_specific() -> i32 {
504    42
505}
506
507/// Test class for method type: ignore functionality
508#[gen_stub_pyclass]
509#[pyclass]
510pub struct TypeIgnoreTest {}
511
512#[gen_stub_pymethods]
513#[pymethods]
514impl TypeIgnoreTest {
515    #[new]
516    fn new() -> Self {
517        Self {}
518    }
519
520    /// Test method with type: ignore for specific rules
521    #[gen_stub(type_ignore = ["union-attr", "return-value"])]
522    fn test_method_ignore(&self, value: i32) -> i32 {
523        value * 2
524    }
525
526    /// Test method with type: ignore (without equals for catch-all)
527    #[gen_stub(type_ignore)]
528    fn test_method_all_ignore(&self) -> i32 {
529        42
530    }
531}
532
533define_stub_info_gatherer!(stub_info);
534
535/// Test of unit test for testing link problem
536#[cfg(test)]
537mod test {
538    #[test]
539    fn test() {
540        assert_eq!(2 + 2, 4);
541    }
542}