Proc Macros to support and simplify testing #3984
Replies: 6 comments 1 reply
-
Hello, thanks for the suggestions! I don't know if we necessarily need this in the main The The |
Beta Was this translation helpful? Give feedback.
-
Some prior art in |
Beta Was this translation helpful? Give feedback.
-
I've done some experimenting in a sandbox (and learnt a lot about proc-macros, syn and parse along the way)... I agree that the better solution is not to include any import info in the proc_macro attribute. I need to pull it out of the remaining function though, or trying to put the code initiating python in the right place becomes tricky and restrictive. The approach that looks most promising to me would be to turn: ...
#[pymodule]
#[pyo3(name = "fizzbuzzo3")]
fn py_fizzbuzzo3(module: &Bound<'_, PyModule>) -> PyResult<()> {
module.add_function(wrap_pyfunction!(py_fizzbuzz, module)?)?;
Ok(())
}
...
#[cfg(test)]
mod tests {
use pyo3::exceptions::PyTypeError;
use super::*;
#[test]
fn test_fizzbuzz() {
pyo3::append_to_inittab!(py_fizzbuzzo3);
pyo3::prepare_freethreaded_python();
Python::with_gil(|py| {
let fizzbuzzo3 = py
.import_bound("fizzbuzzo3")
.expect("Failed to import fizzbuzzo3");
let fizzbuzz = fizzbuzzo3
.getattr("fizzbuzz")
.expect("Failed to get fizzbuzz function");
let result: PyResult<String> = match fizzbuzz.call1((1i32,)) {
Ok(r) => r.extract(),
Err(e) => Err(e),
};
let result = result.unwrap();
let expected_result = "1";
assert_eq!(result, expected_result);
});
}
} into: ...
#[pymodule]
#[pyo3(name = "fizzbuzzo3")]
fn py_fizzbuzzo3(module: &Bound<'_, PyModule>) -> PyResult<()> {
module.add_function(wrap_pyfunction!(py_fizzbuzz, module)?)?;
Ok(())
}
...
#[cfg(test)]
mod tests {
use pyo3::exceptions::PyTypeError;
use super::*;
#[pyo3test]
#[pyo3(import = py_fizzbuzzo3: "from fizzbuzzo3 import fizzbuzz")]
fn test_fizzbuzz() {
let result = pyo3_call!(fizzbuzz, 1i32);
let expected_result = "1";
assert_eq!(result, expected_result);
});
}
} Question 1: I ended up starting to rebuild a lot of what you already have in Question 2: Would that fit your ideas and overall design? Question 3: I would intend to put the macro_rules and a reexport of the proc_macro into pyo3-testing, the proc_macro into pyo3-testing-macros and add the custom keyword directly to pyo3-macros-backend to avoid a third crate. Does that make sense? All of these ideas can change, based on how the code progresses and your thoughts on the drafts, just looking for some starting guidance if there is anything you feel strongly about... I'll have a look at the |
Beta Was this translation helpful? Give feedback.
-
I finally had a good chance to sit down an look at this... I'd really welcome your views on rough implementation here: https://github.com/MusicalNinjaDad/pyo3/tree/pyo3-testing
I'm completely new to rust, so not yet up to speed on the main idioms and it's been over 20 years since I last did any real work with a low-level language - so feedback is very welcome. Any pointers on the kind of error to raise in unexpected cases, ability to streamline the code etc., improvements to the API to make it easy to recognise for both rust and python coders alike, ... |
Beta Was this translation helpful? Give feedback.
-
Hi @davidhewitt - I've refactored, polished and finished the first basic version end-to-end. I'd really welcome your views on the overall design, how to best integrate it (I was thinking a feature which is enabled by default; a subsection 2.2.3 in the docs "Testing python functions") and which other functionality would be needed for this to be worthy of a PR as pyo3-testing v0.1.0 What's the easiest way to share with you? Raising a PR and then continuing to work or sharing my working branch directly? |
Beta Was this translation helpful? Give feedback.
-
Hi @davidhewitt , I understand you're busy and that the next major work is on technical improvements rather than polishing this bit of the UI. I hope you don't mind: I broke out pyo3-testing into an independent crate (for now) and added in the I'd be honoured if you did consider
and have no problems "handing over the keys", so to speak. |
Beta Was this translation helpful? Give feedback.
-
I'm just getting started learning rust with a view to porting a (currently private) mathematical library from native python to rust and making it public. Pyo3 is wonderful and has really made life easy for what I was expecting to be one of the most complicated aspects of the task.
One thing I have noticed is that unit testing the rust code after it is wrapped by pyo3 contains quite some boilerplate and was not so easy to work out how to do it.
Testing strategy I'm following:
I have tests on all 3 sections of code, each with a specific focus:
In order to simplify point (2) I'm about to start creating a few proc-macros, initially something like:
#[pyo3test(module,function)]
which will prefix the test code with:
maybe later also wrap
let result = function.call...
inmatch ... unwrap
#[pyo3raises(...)]
which will be the equivalent of:
My Question
Is this something you would be interested in potentially integrating into pyo3? In which case I will try to follow the same pattern you have for segregating proc_macros and their implementations, write the documentation in the same style etc... I'd be happy to help support maintaining and extending the testing functionality and documentation.
Or is this not really something you see in pyo3 in the near future? In which case I will think about packaging it up in a separate way and documenting it more in line with my other projects, which I am just beginning to make public.
I'm guessing that integration into pyo3 will lead to a faster uptake and interest in extending and improving the functionality due to the wider reach and will add value to both pyo3 and the testing macros by not expecting users to find and use an external solution for testing their code. At the same time it would add further complexity to the project, ...
What are your thoughts?
Beta Was this translation helpful? Give feedback.
All reactions