Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: major rewrite for Bound API #3906

Merged
merged 18 commits into from
Mar 10, 2024
Merged

Conversation

davidhewitt
Copy link
Member

I started these on the weekend but ran out of time to go any further, I'll try to continue with these in the next couple of days...

guide/src/python_from_rust.md Outdated Show resolved Hide resolved
guide/src/python_from_rust.md Outdated Show resolved Hide resolved
guide/src/python_from_rust.md Outdated Show resolved Hide resolved
Copy link

codspeed-hq bot commented Mar 8, 2024

CodSpeed Performance Report

Merging #3906 will not alter performance

Comparing davidhewitt:bound-docs (0547c63) with main (14d1d2a)

🎉 Hooray! pytest-codspeed just leveled up to 2.2.0!

A heads-up, this is a breaking change and it might affect your current performance baseline a bit. But here's the exciting part - it's packed with new, cool features and promises improved result stability 🥳!
Curious about what's new? Visit our releases page to delve into all the awesome details about this new version.

Summary

✅ 67 untouched benchmarks


* [`call`]({{#PYO3_DOCS_URL}}/pyo3/prelude/trait.PyAnyMethods#tymethod.call) - call any callable Python object.
* [`call_method`]({{#PYO3_DOCS_URL}}/pyo3/prelude/trait.PyAnyMethods#tymethod.call_method) - call a method on the Python object.
* It provides global APIs for the Python interpreter, such as [`py.eval()`][eval] and [`py.import()`][import].
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* It provides global APIs for the Python interpreter, such as [`py.eval()`][eval] and [`py.import()`][import].
* It provides global APIs for the Python interpreter, such as [`py.eval_bound()`][eval] and [`py.import_bound()`][import].

guide/src/python_from_rust.md Outdated Show resolved Hide resolved
guide/src/python_from_rust.md Outdated Show resolved Hide resolved

## The Python GIL, mutability, and Rust types
These smart pointers have different numbers of lifetime parameters, which defines how they behave. `Py<T>` has no lifetime parameters, `Bound<'py, T>` has a lifetime parameter `'py`, and `Borrowed<'a, 'py, T>` has two lifetime parameters `'a` and `'py`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we make a point here about these, we should also explain what these lifetimes describe, not just that they are there.
What does 'py describe? Is it the same for Bound and Borrowed? What about 'a?

Or we remove this here and defer to the detail sections below.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried to expand on this a bit.

guide/src/types.md Outdated Show resolved Hide resolved
guide/src/python_from_rust/calling-existing-code.md Outdated Show resolved Hide resolved

By having the binding to the `'py` lifetime, `Bound<'py, T>` can offer the complete PyO3 API at maximum efficiency. This means that in almost all cases where `Py<T>` is not necessary for lifetime reasons, `Bound<'py, T>` should be used.

`Bound<'py, T>` engages in Python reference counting. This means that `Bound<'py, T>` owns a Python object. Rust code which just wants to borrow a Python object should use a shared reference `&Bound<'py, T>`. Just like `std::sync::Arg`, using `.clone()` and `drop()` will cheaply implement and decrement the reference count of the object (just in this case, the reference counting is implemented by the Python interpreter itself).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`Bound<'py, T>` engages in Python reference counting. This means that `Bound<'py, T>` owns a Python object. Rust code which just wants to borrow a Python object should use a shared reference `&Bound<'py, T>`. Just like `std::sync::Arg`, using `.clone()` and `drop()` will cheaply implement and decrement the reference count of the object (just in this case, the reference counting is implemented by the Python interpreter itself).
`Bound<'py, T>` engages in Python reference counting. This means that `Bound<'py, T>` owns a Python object. Rust code which just wants to borrow a Python object should use a shared reference `&Bound<'py, T>`. Just like `std::sync::Arg`, using `.clone()` and `drop()` will cheaply increment and decrement the reference count of the object (just in this case, the reference counting is implemented by the Python interpreter itself).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, drive by comment: Arg should be Arc

@MarshalX
Copy link

MarshalX commented Mar 10, 2024

I feel the lack of understanding how #[pyfunction] works. What should I specify as return type. Same with args. I believe that there is a hidden type conversation behind the scene. So I'm confused should I return Bound<> or PyObject... which will not cause type conversation. Should I use PyResult<&str> or return PyResult of Bound PyString. Should I accept args as refs of not. Unfortunately, most code snippets utilize PyResult<()> as the return type :( I will be happy to find answers to these questions in the docs

Speaking of v0.20 I was surprised to gain a huge performance bust by
changing argument type:

-fn _decode_dag_cbor(data: Vec<u8>) -> Result<Ipld> {
+fn _decode_dag_cbor(data: &[u8]) -> Result<Ipld> {

I wish to not make this mistake again

@davidhewitt
Copy link
Member Author

Thanks, I've added a new section specifically talking about the function argument lifetimes to help indicate to use Bound (and why Py is sometimes ok too).

I'd like Vec<u8> to keep to separate issues like #2888, there is definitely discussion to have there.

@davidhewitt davidhewitt changed the title docs: WIP update for bound docs: major rewrite for Bound API Mar 10, 2024
@@ -484,7 +484,7 @@ If the input is neither a string nor an integer, the error message will be:
- `pyo3(from_py_with = "...")`
- apply a custom function to convert the field from Python the desired Rust type.
- the argument must be the name of the function as a string.
- the function signature must be `fn(&PyAny) -> PyResult<T>` where `T` is the Rust type of the argument.
- the function signature must be `fn(Bound<PyAny>) -> PyResult<T>` where `T` is the Rust type of the argument.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can also take a &Bound<PyAny> I believe

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is actually just &Bound<PyAny> for now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to keep this section referencing the gil-refs for now, we probably should backport the the examples again, otherwise this does not make a lot of sense

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea 👍

guide/src/performance.md Outdated Show resolved Hide resolved
src/lib.rs Outdated Show resolved Hide resolved
guide/src/types.md Outdated Show resolved Hide resolved
@davidhewitt davidhewitt marked this pull request as ready for review March 10, 2024 14:43
Copy link
Contributor

@Icxolu Icxolu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good 🚀 ! There is still calling-existing-code.md to rework, but I think it's fine to merge this as is for the beta and then finish the docs towards final release.

@davidhewitt
Copy link
Member Author

davidhewitt commented Mar 10, 2024

Thanks everyone for the reviews! Definitely there's more the docs can have improved, though I think this PR really did shift the bulk of them onwards and upwards 💹

@davidhewitt davidhewitt enabled auto-merge March 10, 2024 15:38
@davidhewitt davidhewitt added this pull request to the merge queue Mar 10, 2024
Merged via the queue into PyO3:main with commit 9145fcf Mar 10, 2024
40 of 41 checks passed
@davidhewitt davidhewitt deleted the bound-docs branch March 10, 2024 16:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants