Skip to content

Add support for PyFrozenDict#6174

Open
MatthieuDartiailh wants to merge 13 commits into
PyO3:mainfrom
MatthieuDartiailh:frozendict
Open

Add support for PyFrozenDict#6174
MatthieuDartiailh wants to merge 13 commits into
PyO3:mainfrom
MatthieuDartiailh:frozendict

Conversation

@MatthieuDartiailh

Copy link
Copy Markdown
Contributor

No description provided.

@MatthieuDartiailh MatthieuDartiailh force-pushed the frozendict branch 2 times, most recently from ee55a45 to fd12ac9 Compare July 2, 2026 20:05
@MatthieuDartiailh

Copy link
Copy Markdown
Contributor Author

I am a bit at a loss when it comes to the ffi checks. Pointers welcome

@alex

alex commented Jul 2, 2026

Copy link
Copy Markdown
Member

AFAIK the header that contains this in CPython is Include/internal/pycore_dict.h, so you'd need to include that path.

(Not opining on any part of this patch.)

@MatthieuDartiailh

Copy link
Copy Markdown
Contributor Author

Thanks I will try that.

@MatthieuDartiailh

Copy link
Copy Markdown
Contributor Author

So the ffi-check situtation is a bit more complex since accessing the core headers requires Py_BUILD_CORE define. Is this something pyo3 maintainers would be willing to accept ?

@Icxolu Icxolu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm also not an expert on the FFI layer, but here are some comment from my side.

(I haven't looked at the wrapping code, as we first need to get the FFI bindings correct.)

Comment thread pyo3-ffi/src/cpython/dictobject.rs Outdated
Comment thread pyo3-ffi/src/dictobject.rs Outdated
@davidhewitt

Copy link
Copy Markdown
Member

So the ffi-check situtation is a bit more complex since accessing the core headers requires Py_BUILD_CORE define. Is this something pyo3 maintainers would be willing to accept ?

We should not be using any symbols from Include/internal.

Comment thread pyo3-ffi-check/macro/src/lib.rs Outdated
@MatthieuDartiailh

MatthieuDartiailh commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

I think this should now look better. I squashed the previous change to reduce the noise. so running just the lib test was too light. I will continue tomorrow.

@Icxolu Icxolu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you, I think we are getting closer. I went through the safe wrapper as well and left some comments below.

Comment thread pyo3-ffi/src/cpython/dictobject.rs Outdated
#[inline]
#[cfg(Py_3_15)]
pub unsafe fn PyAnyDict_CheckExact(op: *mut PyObject) -> c_int {
(Py_TYPE(op) == &raw mut PyDict_Type || Py_TYPE(op) == &raw mut PyFrozenDict_Type) as c_int

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
(Py_TYPE(op) == &raw mut PyDict_Type || Py_TYPE(op) == &raw mut PyFrozenDict_Type) as c_int
(PyDict_CheckExact(op) != 0 || PyFrozenDict_CheckExact(op) != 0) as c_int

To be consistent I think we should so this instead.

Comment thread pyo3-ffi/src/cpython/dictobject.rs
Comment thread src/types/frozendict.rs
Comment thread src/types/frozendict.rs Outdated
Comment thread src/types/frozendict.rs Outdated
Comment thread src/types/frozendict.rs Outdated
Comment thread src/types/frozendict.rs Outdated
Comment thread src/types/frozendict.rs Outdated
@MatthieuDartiailh

Copy link
Copy Markdown
Contributor Author

Thanks for the review. I have been fighthing a bit cfg when it comes to the limited API. I will have to look into your suggestions.

@MatthieuDartiailh

Copy link
Copy Markdown
Contributor Author

I did a first pass in addressing the review and got clean clippy and test execution locally. The wrapper is now available without the limited API and the cfg are a bit more tiddy. I will check the CI run tomorrow and see if I missed anything else.

@codspeed-hq

codspeed-hq Bot commented Jul 8, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 140 untouched benchmarks


Comparing MatthieuDartiailh:frozendict (85acaf7) with main (8fcf8fc)

Open in CodSpeed

@Icxolu Icxolu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you, some smaller suggestions and some more cfg massaging is needed, but not too much more until we can land this I would say.

Comment thread src/types/frozendict.rs Outdated
Comment thread src/types/frozendict.rs Outdated
Comment thread src/types/frozendict.rs Outdated
Comment thread src/types/frozendict.rs Outdated
@MatthieuDartiailh MatthieuDartiailh marked this pull request as ready for review July 10, 2026 22:34
@MatthieuDartiailh

Copy link
Copy Markdown
Contributor Author

Thanks for the early reviews @Icxolu

I think I now have everything in order (including the cfgs).

@Icxolu Icxolu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you, looking good! Just one small comment regarding the constructor after which I think this is good to go.

Comment thread src/types/frozendict.rs
Comment on lines 55 to 58
pub fn new<'py, T>(py: Python<'py>, iterable: T) -> PyResult<Bound<'py, PyFrozenDict>>
where
T: IntoPyObject<'py>,
err::PyErr: core::convert::From<<T as conversion::IntoPyObject<'py>>::Error>,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Typically the new constructor for similar types takes a Rust iterator. See for example PyFrozenSet or PyTuple. To be consistent I think we should follow that here as well.

I can see that it could be useful to build a PyFrozenDict directly from a Python. The equivalent on dict is called from_sequence, we can use the same here. Similarly we should not make that generic to make users aware that they need to check what they are passing in.


I also saw that for PyFrozenSet we have a PyFrozenSetBuilder. This could be useful for PyFrozenDict as well, out of scope for this PR, but could be a possible followup.

Comment thread src/types/frozendict.rs
}
}

#[cfg(all(Py_3_15, test))]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
#[cfg(all(Py_3_15, test))]
#[cfg(test)]

Comment thread src/types/mod.rs

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

#[cfg(Py_3_15)] on all entries should be enough here.

@bschoenmaeckers

Copy link
Copy Markdown
Member

As a possible followup we can add support for a PyAnyDict, where both PyDict & PyFrozenDict have a Deref impl to PyAnyDict. This will remove some duplications from both types.

#[inline]
#[cfg(Py_3_15)]
pub unsafe fn PyFrozenDict_CheckExact(op: *mut PyObject) -> c_int {
(Py_TYPE(op) == &raw mut PyFrozenDict_Type) as c_int

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
(Py_TYPE(op) == &raw mut PyFrozenDict_Type) as c_int
Py_IS_TYPE(op, &raw mut PyFrozenDict_Type)

Comment on lines +64 to +65
(Py_TYPE(op) == &raw mut PyFrozenDict_Type
|| PyType_IsSubtype(Py_TYPE(op), &raw mut PyFrozenDict_Type) != 0) as c_int

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
(Py_TYPE(op) == &raw mut PyFrozenDict_Type
|| PyType_IsSubtype(Py_TYPE(op), &raw mut PyFrozenDict_Type) != 0) as c_int
PyObject_TypeCheck(op, &raw mut PyFrozenDict_Type)

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.

5 participants