Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Properly handles del val.__dict__. #5576

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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

hbina
Copy link
Contributor

@hbina hbina commented Mar 1, 2025

Fixes #5355

@youknowone
Copy link
Member

see also #5509

@hbina hbina force-pushed the hbina-fix-issue-5355 branch 2 times, most recently from dbe0d21 to 16113a4 Compare March 2, 2025 14:24
@hbina
Copy link
Contributor Author

hbina commented Mar 2, 2025

Thanks for that PR, I have updated the PR based on that. If everything is good I will try to make my PR on top of the original one. Its much easier for me to work with squashed commits but I don't want to lose the work of the original PR.

@youknowone
Copy link
Member

@hbina Sure! That will be a good way. Thank you!

@hbina hbina force-pushed the hbina-fix-issue-5355 branch from 16113a4 to d964098 Compare March 8, 2025 11:19
@@ -711,14 +712,37 @@ impl PyObject {

/// Set the dict field. Returns `Err(dict)` if this object does not have a dict field
Copy link
Member

Choose a reason for hiding this comment

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

The doc-comment doesn't fit in actual implementation anymore

pub fn set_dict(&self, dict: PySetterValue<PyDictRef>) -> Option<()> {
// NOTE(hanif) - So far, this is the only error condition that I know of so we can use Option
// for now.
if self.payload_is::<crate::builtins::function::PyFunction>() {
Copy link
Member

Choose a reason for hiding this comment

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

I am sorry. I don't get how PyFunction specifically requires to be checked yet. Is this same in CPython?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because deleting dict of a function is not allowed.
I can't find the equivalent check inside of cpython...

class A:
    pass


def ff():
    return 0


# Deleting __dict__ of class is fine
a = A()
print(a.__dict__)
del a.__dict__

# Deleting __dict__ of function is not allowed
print(ff.__dict__)
del ff.__dict__

Yields

hbina085@akarin-thinkpad ~/g/RustPython (hbina-fix-issue-5355) [1]> python ggg_issue_5355.py                                                                                                                      (numbadev) 
{}
{}
Traceback (most recent call last):
  File "/home/hbina085/git/RustPython/ggg_issue_5355.py", line 16, in <module>
    del ff.__dict__
        ^^^^^^^^^^^
TypeError: cannot delete __dict__

Perhaps I should have added comments.

Copy link
Member

Choose a reason for hiding this comment

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

Instead of PyFunction, isn't it a similar, but different rule? e.g. static types, immutable types etc

Copy link
Member

Choose a reason for hiding this comment

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

Maybe instant_dict() can be simple None for those types.

Comment on lines +727 to +731
// self.0.dict = Some(InstanceDict::new(dict));
unsafe {
let ptr = self as *const _ as *mut PyObject;
(*ptr).0.dict = Some(InstanceDict::new(dict));
}
Copy link
Member

Choose a reason for hiding this comment

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

Is this necessarily to be unsafe? Is this guaranteed to be actually safe?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have no idea how to implement this without unsafe...All the methods only have immutable access to self. Can't figure out the head or tails of PyObjectRef either.

Copy link
Member

Choose a reason for hiding this comment

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

When it matched to None, I guess this object never can have __dict__. Then returning None will fit to it.

hbina and others added 6 commits March 23, 2025 11:18
This PR is based on the work by key262yek.
The original PR is here: RustPython#5509
- Add support for deleting object dictionaries
- Modify object_set_dict to handle dictionary deletion
- Update setter value handling to support delete operations
- Simplify object_set_dict method in object.rs
- Update set_dict method in core.rs to handle both assignment and deletion
- Consolidate dictionary modification logic
- Modify set_dict method to allow assigning dict to objects without existing dict
- Update error handling in object_set_dict to provide more precise error message
- Refactor type module to correctly handle dictionary assignment
This PR is based on the work by key262yek.

Signed-off-by: Hanif Ariffin <[email protected]>
Co-authored-by: Jeong, YunWon <[email protected]>
@hbina hbina force-pushed the hbina-fix-issue-5355 branch from a693b3b to 425fd4f Compare March 23, 2025 03:18
Comment on lines +727 to +731
// self.0.dict = Some(InstanceDict::new(dict));
unsafe {
let ptr = self as *const _ as *mut PyObject;
(*ptr).0.dict = Some(InstanceDict::new(dict));
}
Copy link
Member

Choose a reason for hiding this comment

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

When it matched to None, I guess this object never can have __dict__. Then returning None will fit to it.

Comment on lines +733 to +739
(Some(_), PySetterValue::Delete) => {
// self.0.dict = None;
unsafe {
let ptr = self as *const _ as *mut PyObject;
(*ptr).0.dict = None;
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
(Some(_), PySetterValue::Delete) => {
// self.0.dict = None;
unsafe {
let ptr = self as *const _ as *mut PyObject;
(*ptr).0.dict = None;
}
}
(Some(dict), PySetterValue::Delete) => {
dict.write().clear();
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can't do this because it only have &self.

pub fn set_dict(&self, dict: PySetterValue<PyDictRef>) -> Option<()> {
// NOTE(hanif) - So far, this is the only error condition that I know of so we can use Option
// for now.
if self.payload_is::<crate::builtins::function::PyFunction>() {
Copy link
Member

Choose a reason for hiding this comment

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

Maybe instant_dict() can be simple None for those types.

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.

Inconsist with cpython in deleting __dict__
3 participants