Add a test to detect memory leak of bytes->vec conversion
This commit is contained in:
parent
7745622b43
commit
e911828b79
|
@ -2,3 +2,4 @@ pip>=19.1
|
|||
hypothesis>=3.55
|
||||
pytest>=3.5.0
|
||||
setuptools-rust>=0.10.2
|
||||
psutil>=5.6
|
||||
|
|
|
@ -97,6 +97,7 @@ setup(
|
|||
make_rust_extension("rustapi_module.datetime"),
|
||||
make_rust_extension("rustapi_module.subclassing"),
|
||||
make_rust_extension("rustapi_module.test_dict"),
|
||||
make_rust_extension("rustapi_module.buf_and_str"),
|
||||
],
|
||||
install_requires=install_requires,
|
||||
tests_require=tests_require,
|
||||
|
|
31
examples/rustapi_module/src/buf_and_str.rs
Normal file
31
examples/rustapi_module/src/buf_and_str.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
//! Objects related to PyBuffer and PyStr
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::{PyBytes, PyString};
|
||||
|
||||
/// This is for confirming that PyBuffer does not cause memory leak
|
||||
#[pyclass]
|
||||
struct BytesExtractor {}
|
||||
|
||||
#[pymethods]
|
||||
impl BytesExtractor {
|
||||
#[new]
|
||||
pub fn __new__(obj: &PyRawObject) {
|
||||
obj.init({ BytesExtractor {} });
|
||||
}
|
||||
|
||||
pub fn to_vec(&mut self, bytes: &PyBytes) -> PyResult<usize> {
|
||||
let byte_vec: Vec<u8> = bytes.extract().unwrap();
|
||||
Ok(byte_vec.len())
|
||||
}
|
||||
|
||||
pub fn to_str(&mut self, bytes: &PyString) -> PyResult<usize> {
|
||||
let byte_str: String = bytes.extract().unwrap();
|
||||
Ok(byte_str.len())
|
||||
}
|
||||
}
|
||||
|
||||
#[pymodule]
|
||||
pub fn buf_and_str(_py: Python, m: &PyModule) -> PyResult<()> {
|
||||
m.add_class::<BytesExtractor>()?;
|
||||
Ok(())
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
pub mod buf_and_str;
|
||||
pub mod datetime;
|
||||
pub mod dict_iter;
|
||||
pub mod othermod;
|
||||
|
|
30
examples/rustapi_module/tests/test_buf_and_str.py
Normal file
30
examples/rustapi_module/tests/test_buf_and_str.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
import os
|
||||
import psutil
|
||||
from rustapi_module.buf_and_str import BytesExtractor
|
||||
|
||||
|
||||
def test_pybuffer_doesnot_leak_memory():
|
||||
N = int(1e5)
|
||||
extractor = BytesExtractor()
|
||||
process = psutil.Process(os.getpid())
|
||||
|
||||
def memory_diff(f):
|
||||
before = process.memory_info().rss
|
||||
f()
|
||||
after = process.memory_info().rss
|
||||
return after - before
|
||||
|
||||
message_b = b'\\(-"-;) Praying that memory leak would not happen..'
|
||||
message_s = '\\(-"-;) Praying that memory leak would not happen..'
|
||||
|
||||
def to_vec():
|
||||
for i in range(N):
|
||||
extractor.to_vec(message_b)
|
||||
|
||||
def to_str():
|
||||
for i in range(N):
|
||||
extractor.to_str(message_s)
|
||||
|
||||
mv = memory_diff(to_vec)
|
||||
ms = memory_diff(to_str)
|
||||
assert abs(mv - ms) < 1000
|
|
@ -2,7 +2,7 @@ use pyo3::class::PyBufferProtocol;
|
|||
use pyo3::exceptions::BufferError;
|
||||
use pyo3::ffi;
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::IntoPyDict;
|
||||
use pyo3::types::{IntoPyDict, PyAny};
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::ptr;
|
||||
|
@ -76,3 +76,28 @@ fn test_buffer() {
|
|||
let d = [("ob", t)].into_py_dict(py);
|
||||
py.run("assert bytes(ob) == b' 23'", None, Some(d)).unwrap();
|
||||
}
|
||||
|
||||
fn byte_to_vec(py_byte_string: &PyAny) -> PyResult<Vec<u8>> {
|
||||
py_byte_string.extract()
|
||||
}
|
||||
|
||||
fn byte_to_str(py_byte_string: &PyAny) -> PyResult<String> {
|
||||
py_byte_string.extract()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_byte_conversion() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let bytes = py.eval("b'I AM BYTE-STRING'", None, None).unwrap();
|
||||
let answer = "I AM BYTE-STRING";
|
||||
assert_eq!(byte_to_vec(bytes).unwrap(), answer.as_bytes().to_vec());
|
||||
assert_eq!(
|
||||
byte_to_str(bytes)
|
||||
.map_err(|err| {
|
||||
err.print_and_set_sys_last_vars(py);
|
||||
})
|
||||
.unwrap(),
|
||||
answer.to_string()
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue