Skip to content

feat: Adds the Bound<'_, PyMappingProxy> type#4644

Merged
davidhewitt merged 14 commits intoPyO3:mainfrom
KLMatlock:feature/mappingproxy
Oct 29, 2024
Merged

feat: Adds the Bound<'_, PyMappingProxy> type#4644
davidhewitt merged 14 commits intoPyO3:mainfrom
KLMatlock:feature/mappingproxy

Conversation

@KLMatlock
Copy link
Copy Markdown
Contributor

Continuation of #2435 and #3521. Implement the PyMappingProxy type (called PyDictProxy in CPython).

Resolves #2108.

@davidhewitt
Copy link
Copy Markdown
Member

Thanks for the PR! This is in draft status; is there more you want to add before review?

@KLMatlock
Copy link
Copy Markdown
Contributor Author

KLMatlock commented Oct 25, 2024

Thanks for the PR! This is in draft status; is there more you want to add before review?

Nope! Just got busy yesterday and wanted to do a final look through. Please review when ready.

@KLMatlock KLMatlock marked this pull request as ready for review October 25, 2024 15:35
Copy link
Copy Markdown
Member

@davidhewitt davidhewitt left a comment

Choose a reason for hiding this comment

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

Thanks for picking this up; overall this looks close to merge. While I have added a bunch of comments, they're primarily small tweaks where I think the design is relatively clear 😄

Comment thread pyo3-ffi/src/descrobject.rs Outdated
Comment on lines +71 to +74
#[inline]
pub unsafe fn PyDictProxy_Check(op: *mut PyObject) -> c_int {
PyObject_TypeCheck(op, std::ptr::addr_of_mut!(PyDictProxy_Type))
}
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.

This function doesn't exist in https://github.com/python/cpython/blob/3.13/Include/descrobject.h (indeed I couldn't find it anywhere). So we shouldn't add this.

Suggested change
#[inline]
pub unsafe fn PyDictProxy_Check(op: *mut PyObject) -> c_int {
PyObject_TypeCheck(op, std::ptr::addr_of_mut!(PyDictProxy_Type))
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed!

Comment thread src/types/dict.rs Outdated

/// Represents a tuple which can be used as a PyDict item.
trait PyDictItem<'py> {
pub trait PyDictItem<'py> {
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.

This looks a possibly unintentional change

Suggested change
pub trait PyDictItem<'py> {
trait PyDictItem<'py> {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed! For some reason I thought I needed this for one of the tests.

Comment thread src/types/mappingproxy.rs Outdated
Comment on lines +18 to +19
#checkfunction=ffi::PyDictProxy_Check
);
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 think rather than the ffi function (as discussed, it doesn't seem to exist in CPython), we should just have fn PyDictProxy_Check defined in this file.

Suggested change
#checkfunction=ffi::PyDictProxy_Check
);
#checkfunction=PyDictProxy_Check
);
#[inline]
unsafe fn PyDictProxy_Check(op: *mut PyObject) -> c_int {
ffi::PyObject_TypeCheck(op, std::ptr::addr_of_mut!(PyDictProxy_Type))
}

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.

Also, it looks like subclassing mappingproxy is not allowed so we can probably just do Py_IS_TYPE here.

>>> class F(types.MappingProxyType):
...     pass
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: type 'mappingproxy' is not an acceptable base type

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done, I changed the check function to the following:

#[inline]
unsafe fn dict_proxy_check(op: *mut ffi::PyObject) -> c_int {
    ffi::Py_IS_TYPE(op, std::ptr::addr_of_mut!(ffi::PyDictProxy_Type))
}

Comment thread src/types/mappingproxy.rs Outdated
Comment on lines +68 to +71
fn copy(&self) -> PyResult<Bound<'_, PyMappingProxy>> {
let res = self.call_method0("copy")?;
unsafe { Ok(res.downcast_into_unchecked::<PyMappingProxy>()) }
}
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 think the type here is incorrect, it looks like .copy() returns a copy of the underlying mapping, perhaps?

>>> type(types.MappingProxyType({}).copy())
<class 'dict'>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I changed this to return PyMapping instead.

Comment thread src/types/mappingproxy.rs
Comment on lines +77 to +102
#[inline]
fn keys(&self) -> PyResult<Bound<'py, PySequence>> {
unsafe {
Ok(ffi::PyMapping_Keys(self.as_ptr())
.assume_owned_or_err(self.py())?
.downcast_into_unchecked())
}
}

#[inline]
fn values(&self) -> PyResult<Bound<'py, PySequence>> {
unsafe {
Ok(ffi::PyMapping_Values(self.as_ptr())
.assume_owned_or_err(self.py())?
.downcast_into_unchecked())
}
}

#[inline]
fn items(&self) -> PyResult<Bound<'py, PySequence>> {
unsafe {
Ok(ffi::PyMapping_Items(self.as_ptr())
.assume_owned_or_err(self.py())?
.downcast_into_unchecked())
}
}
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.

It looks like since 3.7 (our minimum supported version) these FFI functions are now guaranteed to return lists: https://docs.python.org/3/c-api/mapping.html#c.PyMapping_Keys

So I think we can use PyList as the return type. It would allow for performance gains versus going through the sequence abstraction.

Similarly, would you be willing to update PyMappingMethods in the same way (maybe as a separate PR)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done! Made the changes and opened #4661

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 open to considering the conversion changes in this file, but these changes might break existing users' code silently. I'm unsure both if it's worth the churn and also what the performance cost might be.

If we move ahead with these we should also include the corresponding changes to the index map and hash brown conversions.

At a minimum I would at like to see these changes split off into a separate PR, although I will be honest that there is a chance we might decide not to have these. Was it critical for your use case?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Went ahead and removed the conversion changes. It isn't necessary for me, I just had it in there because of the previous PR.

Copy link
Copy Markdown
Member

@davidhewitt davidhewitt left a comment

Choose a reason for hiding this comment

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

This looks great to me, thanks very much for helping get this one finally over the line!

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.

Add support for mappingproxy? (standard library read-only dict)

2 participants