Skip to content

Commit

Permalink
switch to &CStr
Browse files Browse the repository at this point in the history
  • Loading branch information
Icxolu committed Aug 13, 2024
1 parent 6810fea commit c316ff1
Show file tree
Hide file tree
Showing 48 changed files with 340 additions and 216 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,14 +146,15 @@ Example program displaying the value of `sys.version` and the current user name:
```rust
use pyo3::prelude::*;
use pyo3::types::IntoPyDict;
use pyo3::ffi::c_str;

fn main() -> PyResult<()> {
Python::with_gil(|py| {
let sys = py.import("sys")?;
let version: String = sys.getattr("version")?.extract()?;

let locals = [("os", py.import("os")?)].into_py_dict(py);
let code = "os.getenv('USER') or os.getenv('USERNAME') or 'Unknown'";
let code = c_str!("os.getenv('USER') or os.getenv('USERNAME') or 'Unknown'");
let user: String = py.eval(code, None, Some(&locals))?.extract()?;

println!("Hello {}, I'm Python {}", user, version);
Expand Down
4 changes: 2 additions & 2 deletions guide/src/class/numeric.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ fn my_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<Number>()?;
Ok(())
}
# const SCRIPT: &'static str = r#"
# const SCRIPT: &'static std::ffi::CStr = pyo3::ffi::c_str!(r#"
# def hash_djb2(s: str):
# n = Number(0)
# five = Number(5)
Expand Down Expand Up @@ -379,7 +379,7 @@ fn my_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
# pass
# assert Number(1337).__str__() == '1337'
# assert Number(1337).__repr__() == 'Number(1337)'
"#;
"#);

#
# use pyo3::PyTypeInfo;
Expand Down
2 changes: 1 addition & 1 deletion guide/src/conversions/traits.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ struct RustyStruct {
#
# fn main() -> PyResult<()> {
# Python::with_gil(|py| -> PyResult<()> {
# let py_dict = py.eval("{'foo': 'foo', 'bar': 'bar', 'foobar': 'foobar'}", None, None)?;
# let py_dict = py.eval(pyo3::ffi::c_str!("{'foo': 'foo', 'bar': 'bar', 'foobar': 'foobar'}"), None, None)?;
# let rustystruct: RustyStruct = py_dict.extract()?;
# assert_eq!(rustystruct.foo, "foo");
# assert_eq!(rustystruct.bar, "bar");
Expand Down
3 changes: 2 additions & 1 deletion guide/src/module.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,11 @@ fn func() -> String {
# Python::with_gil(|py| {
# use pyo3::wrap_pymodule;
# use pyo3::types::IntoPyDict;
# use pyo3::ffi::c_str;
# let parent_module = wrap_pymodule!(parent_module)(py);
# let ctx = [("parent_module", parent_module)].into_py_dict(py);
#
# py.run("assert parent_module.child_module.func() == 'func'", None, Some(&ctx)).unwrap();
# py.run(c_str!("assert parent_module.child_module.func() == 'func'"), None, Some(&ctx)).unwrap();
# })
```

Expand Down
12 changes: 8 additions & 4 deletions guide/src/python-from-rust/calling-existing-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@ and return the evaluated value as a `Bound<'py, PyAny>` object.

```rust
use pyo3::prelude::*;
use pyo3::ffi::c_str;

# fn main() -> Result<(), ()> {
Python::with_gil(|py| {
let result = py
.eval("[i * 10 for i in range(5)]", None, None)
.eval(c_str!("[i * 10 for i in range(5)]"), None, None)
.map_err(|e| {
e.print_and_set_sys_last_vars(py);
})?;
Expand Down Expand Up @@ -152,6 +153,7 @@ As an example, the below adds the module `foo` to the embedded interpreter:

```rust
use pyo3::prelude::*;
use pyo3::ffi::c_str;

#[pyfunction]
fn add_one(x: i64) -> i64 {
Expand All @@ -166,7 +168,7 @@ fn foo(foo_module: &Bound<'_, PyModule>) -> PyResult<()> {

fn main() -> PyResult<()> {
pyo3::append_to_inittab!(foo);
Python::with_gil(|py| Python::run(py, "import foo; foo.add_one(6)", None, None))
Python::with_gil(|py| Python::run(py, c_str!("import foo; foo.add_one(6)"), None, None))
}
```

Expand All @@ -177,6 +179,7 @@ and insert it manually into `sys.modules`:
```rust
use pyo3::prelude::*;
use pyo3::types::PyDict;
use pyo3::ffi::c_str;

#[pyfunction]
pub fn add_one(x: i64) -> i64 {
Expand All @@ -197,7 +200,7 @@ fn main() -> PyResult<()> {
py_modules.set_item("foo", foo_module)?;

// Now we can import + run our python code
Python::run(py, "import foo; foo.add_one(6)", None, None)
Python::run(py, c_str!("import foo; foo.add_one(6)"), None, None)
})
}
```
Expand Down Expand Up @@ -316,6 +319,7 @@ Use context managers by directly invoking `__enter__` and `__exit__`.

```rust
use pyo3::prelude::*;
use pyo3::ffi::c_str;

fn main() {
Python::with_gil(|py| {
Expand Down Expand Up @@ -344,7 +348,7 @@ class House(object):

house.call_method0("__enter__").unwrap();

let result = py.eval("undefined_variable + 1", None, None);
let result = py.eval(c_str!("undefined_variable + 1"), None, None);

// If the eval threw an exception we'll pass it through to the context manager.
// Otherwise, __exit__ is called with empty arguments (Python "None").
Expand Down
1 change: 1 addition & 0 deletions newsfragments/4435.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Reintroduced `Python::eval` and `Python::run` now take a `&CStr` instead of `&str`.
4 changes: 2 additions & 2 deletions src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,7 @@ mod tests {
#[test]
fn test_debug() {
Python::with_gil(|py| {
let bytes = py.eval("b'abcde'", None, None).unwrap();
let bytes = py.eval(ffi::c_str!("b'abcde'"), None, None).unwrap();
let buffer: PyBuffer<u8> = PyBuffer::get_bound(&bytes).unwrap();
let expected = format!(
concat!(
Expand Down Expand Up @@ -847,7 +847,7 @@ mod tests {
#[test]
fn test_bytes_buffer() {
Python::with_gil(|py| {
let bytes = py.eval("b'abcde'", None, None).unwrap();
let bytes = py.eval(ffi::c_str!("b'abcde'"), None, None).unwrap();
let buffer = PyBuffer::get_bound(&bytes).unwrap();
assert_eq!(buffer.dimensions(), 1);
assert_eq!(buffer.item_count(), 5);
Expand Down
10 changes: 7 additions & 3 deletions src/conversions/anyhow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ impl From<anyhow::Error> for PyErr {
#[cfg(test)]
mod test_anyhow {
use crate::exceptions::{PyRuntimeError, PyValueError};
use crate::prelude::*;
use crate::types::IntoPyDict;
use crate::{ffi, prelude::*};

use anyhow::{anyhow, bail, Context, Result};

Expand All @@ -147,7 +147,9 @@ mod test_anyhow {

Python::with_gil(|py| {
let locals = [("err", pyerr)].into_py_dict(py);
let pyerr = py.run("raise err", None, Some(&locals)).unwrap_err();
let pyerr = py
.run(ffi::c_str!("raise err"), None, Some(&locals))
.unwrap_err();
assert_eq!(pyerr.value_bound(py).to_string(), expected_contents);
})
}
Expand All @@ -164,7 +166,9 @@ mod test_anyhow {

Python::with_gil(|py| {
let locals = [("err", pyerr)].into_py_dict(py);
let pyerr = py.run("raise err", None, Some(&locals)).unwrap_err();
let pyerr = py
.run(ffi::c_str!("raise err"), None, Some(&locals))
.unwrap_err();
assert_eq!(pyerr.value_bound(py).to_string(), expected_contents);
})
}
Expand Down
6 changes: 4 additions & 2 deletions src/conversions/chrono.rs
Original file line number Diff line number Diff line change
Expand Up @@ -794,13 +794,14 @@ mod tests {
// tzdata there to make this work.
#[cfg(all(Py_3_9, not(target_os = "windows")))]
fn test_zoneinfo_is_not_fixed_offset() {
use crate::ffi;
use crate::types::any::PyAnyMethods;
use crate::types::dict::PyDictMethods;

Python::with_gil(|py| {
let locals = crate::types::PyDict::new(py);
py.run(
"import zoneinfo; zi = zoneinfo.ZoneInfo('Europe/London')",
ffi::c_str!("import zoneinfo; zi = zoneinfo.ZoneInfo('Europe/London')"),
None,
Some(&locals),
)
Expand Down Expand Up @@ -1320,6 +1321,7 @@ mod tests {
use crate::tests::common::CatchWarnings;
use crate::types::IntoPyDict;
use proptest::prelude::*;
use std::ffi::CString;

proptest! {

Expand All @@ -1330,7 +1332,7 @@ mod tests {

let globals = [("datetime", py.import("datetime").unwrap())].into_py_dict(py);
let code = format!("datetime.datetime.fromtimestamp({}).replace(tzinfo=datetime.timezone(datetime.timedelta(seconds={})))", timestamp, timedelta);
let t = py.eval(&code, Some(&globals), None).unwrap();
let t = py.eval(&CString::new(code).unwrap(), Some(&globals), None).unwrap();

// Get ISO 8601 string from python
let py_iso_str = t.call_method0("isoformat").unwrap();
Expand Down
10 changes: 7 additions & 3 deletions src/conversions/eyre.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ impl From<eyre::Report> for PyErr {
#[cfg(test)]
mod tests {
use crate::exceptions::{PyRuntimeError, PyValueError};
use crate::prelude::*;
use crate::types::IntoPyDict;
use crate::{ffi, prelude::*};

use eyre::{bail, eyre, Report, Result, WrapErr};

Expand All @@ -152,7 +152,9 @@ mod tests {

Python::with_gil(|py| {
let locals = [("err", pyerr)].into_py_dict(py);
let pyerr = py.run("raise err", None, Some(&locals)).unwrap_err();
let pyerr = py
.run(ffi::c_str!("raise err"), None, Some(&locals))
.unwrap_err();
assert_eq!(pyerr.value_bound(py).to_string(), expected_contents);
})
}
Expand All @@ -169,7 +171,9 @@ mod tests {

Python::with_gil(|py| {
let locals = [("err", pyerr)].into_py_dict(py);
let pyerr = py.run("raise err", None, Some(&locals)).unwrap_err();
let pyerr = py
.run(ffi::c_str!("raise err"), None, Some(&locals))
.unwrap_err();
assert_eq!(pyerr.value_bound(py).to_string(), expected_contents);
})
}
Expand Down
4 changes: 3 additions & 1 deletion src/conversions/num_bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,9 @@ mod tests {
let index = python_index_class(py);
let locals = PyDict::new(py);
locals.set_item("index", index).unwrap();
let ob = py.eval("index.C(10)", None, Some(&locals)).unwrap();
let ob = py
.eval(ffi::c_str!("index.C(10)"), None, Some(&locals))
.unwrap();
let _: BigInt = ob.extract().unwrap();
});
}
Expand Down
14 changes: 8 additions & 6 deletions src/conversions/num_rational.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ mod tests {
Python::with_gil(|py| {
let locals = PyDict::new(py);
py.run(
"import fractions\npy_frac = fractions.Fraction(-0.125)",
ffi::c_str!("import fractions\npy_frac = fractions.Fraction(-0.125)"),
None,
Some(&locals),
)
Expand All @@ -133,7 +133,7 @@ mod tests {
Python::with_gil(|py| {
let locals = PyDict::new(py);
py.run(
"not_fraction = \"contains_incorrect_atts\"",
ffi::c_str!("not_fraction = \"contains_incorrect_atts\""),
None,
Some(&locals),
)
Expand All @@ -148,7 +148,9 @@ mod tests {
Python::with_gil(|py| {
let locals = PyDict::new(py);
py.run(
"import fractions\npy_frac = fractions.Fraction(fractions.Fraction(10))",
ffi::c_str!(
"import fractions\npy_frac = fractions.Fraction(fractions.Fraction(10))"
),
None,
Some(&locals),
)
Expand All @@ -165,7 +167,7 @@ mod tests {
Python::with_gil(|py| {
let locals = PyDict::new(py);
py.run(
"import fractions\n\nfrom decimal import Decimal\npy_frac = fractions.Fraction(Decimal(\"1.1\"))",
ffi::c_str!("import fractions\n\nfrom decimal import Decimal\npy_frac = fractions.Fraction(Decimal(\"1.1\"))"),
None,
Some(&locals),
)
Expand All @@ -182,7 +184,7 @@ mod tests {
Python::with_gil(|py| {
let locals = PyDict::new(py);
py.run(
"import fractions\npy_frac = fractions.Fraction(10,5)",
ffi::c_str!("import fractions\npy_frac = fractions.Fraction(10,5)"),
None,
Some(&locals),
)
Expand Down Expand Up @@ -247,7 +249,7 @@ mod tests {
Python::with_gil(|py| {
let locals = PyDict::new(py);
let py_bound = py.run(
"import fractions\npy_frac = fractions.Fraction(\"Infinity\")",
ffi::c_str!("import fractions\npy_frac = fractions.Fraction(\"Infinity\")"),
None,
Some(&locals),
);
Expand Down
17 changes: 10 additions & 7 deletions src/conversions/rust_decimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,9 @@ mod test_rust_decimal {
use super::*;
use crate::types::dict::PyDictMethods;
use crate::types::PyDict;
use std::ffi::CString;

use crate::ffi;
#[cfg(not(target_arch = "wasm32"))]
use proptest::prelude::*;

Expand All @@ -135,10 +137,11 @@ mod test_rust_decimal {
locals.set_item("rs_dec", &rs_dec).unwrap();
// Checks if Rust Decimal -> Python Decimal conversion is correct
py.run(
&format!(
&CString::new(format!(
"import decimal\npy_dec = decimal.Decimal({})\nassert py_dec == rs_dec",
$py
),
))
.unwrap(),
None,
Some(&locals),
)
Expand Down Expand Up @@ -176,9 +179,9 @@ mod test_rust_decimal {
let locals = PyDict::new(py);
locals.set_item("rs_dec", &rs_dec).unwrap();
py.run(
&format!(
&CString::new(format!(
"import decimal\npy_dec = decimal.Decimal(\"{}\")\nassert py_dec == rs_dec",
num),
num)).unwrap(),
None, Some(&locals)).unwrap();
let roundtripped: Decimal = rs_dec.extract(py).unwrap();
assert_eq!(num, roundtripped);
Expand All @@ -201,7 +204,7 @@ mod test_rust_decimal {
Python::with_gil(|py| {
let locals = PyDict::new(py);
py.run(
"import decimal\npy_dec = decimal.Decimal(\"NaN\")",
ffi::c_str!("import decimal\npy_dec = decimal.Decimal(\"NaN\")"),
None,
Some(&locals),
)
Expand All @@ -217,7 +220,7 @@ mod test_rust_decimal {
Python::with_gil(|py| {
let locals = PyDict::new(py);
py.run(
"import decimal\npy_dec = decimal.Decimal(\"1e3\")",
ffi::c_str!("import decimal\npy_dec = decimal.Decimal(\"1e3\")"),
None,
Some(&locals),
)
Expand All @@ -234,7 +237,7 @@ mod test_rust_decimal {
Python::with_gil(|py| {
let locals = PyDict::new(py);
py.run(
"import decimal\npy_dec = decimal.Decimal(\"Infinity\")",
ffi::c_str!("import decimal\npy_dec = decimal.Decimal(\"Infinity\")"),
None,
Some(&locals),
)
Expand Down
Loading

0 comments on commit c316ff1

Please sign in to comment.