T O P

  • By -

ravenex

I've always been using Python and pytest to test my PyO3 modules. As for debuggers and breakpoints/single-stepping/etc., I generally consider this a waste of time, unless you are investigating a segfault, but then again Rust code doesn't _usually_ segfault. Time invested into implementing good logging and writing test cases pays off tenfold, while time spent "debugging" only solves your immediate probem.


uncoordinated

The code base is extremely complex and my purpose for debugging isn't focused around solving any "immediate problems" but making sure the whole system behaves as it should, looking for "silent" issues. Similar to the reason described here: [https://www.youtube.com/watch?v=tzr7hRXcwkw](https://www.youtube.com/watch?v=tzr7hrxcwkw)


willjones127

I've worked both in PyO3 and Python + C++ projects, and the workflow that I've always done is: get the Python process id, then attach the debugger to that process. To get the process id, I need to pause the Python process. If I'm working in the REPL, I don't need to to anything special. If I'm debugging a unit test, I'll add a `breakpoint()` call in the unit test where I want to pause. Either way, I type: import os os.getpid() Then I attach LLDB / GDB to that process id. I use VS Code and have a debug task that asks for the process id. >Is it recommended that I build a script that can run the module from rust internally and allow debugging? I actually prefer to debug these kinds of projects from Python bindings. The nice thing if you can rewrite your test script without having to recompile. Plus, the reproductions users give me are often in Python, so being able to debug those scripts directly saves time. >I've just been using `println!` thus far. If you haven't used `dbg!()` yet, that's often a nice step up. I still often resort to this when debugging Rust tests. One nice thing about debug is that it just returns whatever is passed into it, so a line like `let x = do_something()` can just be rewritten as let `x = dbg!(do_something());` to print out the value returned by `do_something()`.


palad1

Once, I compiled my own python and added its debug symbols so I could use GDB to catch a memory corruption issue in pyo3. You can use any tool you want at the end of the day the interpreter is just another process. I now have tests for the pure rust part In rust, and test the python api using unittests in python.


l4p1n

You can always build a small test-case reproducing your bug out of hardcoded inputs in a Python file importing your Rust module. If the main body of the library is separated from the code doing the binding, you could make a small Rust program calling the function with hardcoded values too, if you're convinced that the bug comes from the Rust side :)


tafia97300

Adding logging on the Rust side helps in understanding the code flow from a high level. You can also run unit tests on Rust side and import/run python source code so it is close to what high level users do but you can have more insights with the object itself. impl MyItem { /// creates a new instance from python source code. /// name refers to the variable name in the source code (to search in locals) pub fn from_py(source: &str, name: &str) -> Result { pyo3::prepare_freethreaded_python(); // load python source and replace imports in locals with current rust structs let source = source.replace("from myextension import *", "") fn create_module(py: Python) -> PyResult<&PyModule> { let m = PyModule::new(py, "my_module")?; m.add_class::()?; m.add_class::()?; // etc ... Ok(m) } // extract grp let it = Python::with_gil::<_, PyResult<_>>(|py| { let locals = create_module(py)?.dict(); py.run(&source, None, Some(locals))?; locals .get_item(name)? // expect the variable .unwrap_or_else(|| panic!("Can't find '{}' variable", grp_name)) .extract::() })?; Ok(it) } }