Skip to content

deprecate PyRef(Mut) (Part 1) - #6120

Open
Icxolu wants to merge 1 commit into
PyO3:mainfrom
Icxolu:deprecate/pyref
Open

Icxolu wants to merge 1 commit into
PyO3:mainfrom
Icxolu:deprecate/pyref

Conversation

@Icxolu

@Icxolu Icxolu commented Jun 9, 2026

Copy link
Copy Markdown
Member

First step of deprecating PyRef and PyRefMut. This PR deprecates the types itself (targeting 0.30.0 as per #6083 (comment), so leaving as draft for now)

We will need a migration guide entry, but I would like to defer that to the second part when I deprecate the borrow methods.

See also #6083

@Icxolu
Icxolu force-pushed the deprecate/pyref branch from e227e36 to 296de11 Compare June 9, 2026 19:01
Comment thread tests/ui/traverse.rs

#[pymethods]
impl TraverseTriesToTakePyRef {
#[expect(deprecated)]

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I just expected these for now. PyClassGuard(Mut) does give the same error, PyClassGuard could be allowed I guess, since it is not mutable and does not give access to a Python token. Not sure if that's worth the effort though

@Icxolu
Icxolu force-pushed the deprecate/pyref branch 2 times, most recently from 629eb51 to a76cce0 Compare June 9, 2026 19:30
@codspeed-hq

codspeed-hq Bot commented Jun 9, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 8.32%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 1 improved benchmark
❌ 2 regressed benchmarks
✅ 138 untouched benchmarks
⏩ 6 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
into_u128_zero 816 ns 981.3 ns -16.84%
into_u128_small 876.1 ns 1,041.4 ns -15.87%
test_empty_class_init 31.6 µs 28.7 µs +10.14%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing Icxolu:deprecate/pyref (6922a94) with main (ee629fb)

Open in CodSpeed

Footnotes

  1. 6 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@Icxolu
Icxolu force-pushed the deprecate/pyref branch from a76cce0 to 62c855b Compare June 11, 2026 20:27
@Icxolu
Icxolu marked this pull request as ready for review June 11, 2026 20:27
@Icxolu Icxolu mentioned this pull request Jun 19, 2026
7 tasks
@davidhewitt

Copy link
Copy Markdown
Member

Sorry to be so slow to review this; it's on my mind, I've just been wading a bit through a ~LIFO PR stack.

Interestingly I noted when working on #6407 today that PyClassGuard::deref is potentially quite side-effectful. It goes through PyClassObjectLayout::get_ptr which performs a lot of the type-lookup machinery discussed in that PR (if using abi3 / abi3t variable object layout).

I somewhat wonder whether this suggests that we should calculate the contents ptr of PyClassGuard up-front and store it in the guard, similar to what I do with the PyClassTraverseGuard in #6407.

I'm not sure if it's too late and counts as breaking? Hopefully not, given PyClassGuard is still only semi-public.

@Icxolu

Icxolu commented Sep 12, 2026

Copy link
Copy Markdown
Member Author

No worries 🙂

I think we could cache it. We would loose the #[repr(transparent)] but since our field is private and we have not explicitly documented that, it is not part of our public API according to the Nomicon. Otherwise I think it would purely be an internal change (from a quick look).

I just wonder whether we should and if this would be the best place, Are you concerned because this is Deref specifically? Basically everything calling the any of the contents methods has the same side effects (which also includes the Deref for PyRef, #[pyo3(get)], ...). My main concern would be that it creates a tighter coupling with the quite complex internals of the class layouts, that could easily get missed if we ever refactor those again. But if you see enough benefit to caching I'd be ok with it. In any case that should be a separate precursor or followup PR.

@davidhewitt

Copy link
Copy Markdown
Member

I just wonder whether we should and if this would be the best place, Are you concerned because this is Deref specifically?

Yes, precisely because it's on Deref which I think most people will imagine to be cheap. I worry about code such as

let guard = obj.try_borrow_guard();
let x = guard.x;
let y = guard.y;
let z = guard.z;

... which seems like three cheap field accesses but really is re-resolving the Rust data each time. I concede that #[pyo3(get)] and similar access from Python would do that, however I think everyone expects Python attribute lookup to do all sorts of possible crazy 😬

One alternative to unblock this could be to remove the Deref implementation and add a .lookup_data() method which is explicit enough that it might do work? That would allow us to decide in the future whether to add caching into the guard. (Though it's not impossible that might break some users given PyClassGuard is reachable - possibly more breaking than adding the cache.)

I think I'd like to figure out what we do about this (or decide it's a non-issue) before deprecating PyRef, just so that users go through the API churn in one go.

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.

2 participants