-
-
Notifications
You must be signed in to change notification settings - Fork 33.7k
bpo-32225: Implementation of PEP 562 #4731
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
Conversation
serhiy-storchaka
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a What's New entry and document this feature in corresponding places.
Objects/moduleobject.c
Outdated
| } | ||
| return _PyObject_FastCall(getattr, stack, 1); | ||
| } | ||
| else if (PyErr_Occurred()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This never happen with _PyDict_GetItemId().
I prefer to use _PyDict_GetItemIdWithError() and don't silence errors, but this is a different large issue.
| "module __getattr__ must be callable"); | ||
| return NULL; | ||
| } | ||
| return _PyObject_FastCall(getattr, stack, 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please test the following case?
def __getattr__(name):
if name != 'delgetattr':
raise AttributeError
del globals()['__getattr__']
# do something moreI suspect that it can crash (but not sure).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for review! I think I addressed all the comments, but there is a little problem. This test that I added as test_module_getattr_tricky fails with AttributeError in refleak hunting mode -R 3:3 (it passes in normal mode). Presumably this is because the module stays in memory between sequential runs and __getattr__ is already deleted after the first test. How do I fix this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the test didn't crash then perhaps we shouldn't add it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it makes sense to keep something like this just in case. I simplified the test (__getattr__ always raises AttributeError, but will also delete itself). This way it works also in refleak hunting mode.
Objects/moduleobject.c
Outdated
| result = NULL; | ||
| } | ||
| else { | ||
| result = _PyObject_FastCall(dirfunc, NULL, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_PyObject_CallNoArg()
Objects/moduleobject.c
Outdated
| if (PyDict_Check(dict)) { | ||
| PyObject *dirfunc = PyDict_GetItemString(dict, "__dir__"); | ||
| if (dirfunc) { | ||
| if (!PyCallable_Check(dirfunc)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no a special check for the callability of a regular __dir__.
Lib/test/test_module.py
Outdated
| self.assertEqual(test, "There is test") | ||
| self.assertEqual(gga.x, 1) | ||
| self.assertEqual(gga.y, 2) | ||
| with self.assertRaises(AttributeError): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test the error message.
| @@ -0,0 +1,2 @@ | |||
| PEP 562: Add support for module __getattr__ and __dir__. Implemented by Ivan | |||
| Levkivskyi. | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Escape the underscores to make this valid reST:
module ``__getattr__`` and ``__dir__``There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, fixed!
|
@serhiy-storchaka @merwok is something left to do here? |
|
@ilevkivskyi Nick made a comment on the ticket about adding documentation. @serhiy-storchaka and @merwok would need to let you know if anything else was needed. |
Objects/moduleobject.c
Outdated
| _Py_IDENTIFIER(__getattr__); | ||
| getattr = _PyDict_GetItemId(m->md_dict, &PyId___getattr__); | ||
| if (getattr) { | ||
| if (!PyCallable_Check(getattr)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no a special check for callability of a regular __getattr__ (as well as other special methods).
>>> class A:
... __getattr__ = "Surprise!"
...
>>> A().x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object is not callable
>>> A()[1]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object is not callable
>>> A() + 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object is not callable
Of course special checks can be added in all these cases. This would help recognition marginal mistakes. But this will increase the size of the code and can slow down it (because optimizing this code can worsen the optimization of the normal path) and decrease readability. I would defer this change to a separate issue if it is worth to do it at all.
|
The documentation is required. @ncoghlan given a good suggestion. |
|
OK, I added docs as proposed by @ncoghlan (I also mentioned |
| returned. :func:`dir` converts the returned sequence to a list and sorts it. | ||
|
|
||
|
|
||
| Customizing module attribute access |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add the index entries:
.. index::
single: __getattr__ (module attribute)
single: __dir__ (module attribute)
single: __class__ (module attribute)
| computed value or raise an :exc:`AttributeError`. If an attribute is | ||
| not found on a module object through the normal lookup, i.e. | ||
| :meth:`object.__getattribute__`, then ``__getattr__`` is searched in | ||
| the module ``__dict__`` before raising an :exc:`AttributeError`. If found, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nice to make a reference to __dict__ as the module attribute (but not to the object attribute) if possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure if it is possible. The sub-section "Modules" itself refers to object.__dict__.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is an index entry for __dict__ as the module attribute. But I don't know if it is possible to refer to the target of an index entry.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe you can you use :data: directive. Here's the definition in the Documentation Guide:
data
Describes global data in a module, including both variables and values used as
“defined constants.” Class and object attributes are not documented using this directive.
Where the __dict__ is defined:
.. index:: single: __dict__ (module attribute)
.. data:: __dict__
Special read-only attribute: :attr:`~object.__dict__` is the module's
namespace as a dictionary object.
and then where you want to link:
:meth:`object.__getattribute__`, then ``__getattr__`` is searched in
the module :data:`__dict__` before raising an :exc:`AttributeError`.
As you mentioned, this may require fixing other links for module.__dict__ that now point to object.__dict__.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure this is applicable here. In any case we can experiment with this in a separate issue. I prefer to keep the current markup for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In any case we can experiment with this in a separate issue. I prefer to keep the current markup for now.
I also think it can be deferred to a separate issue. IIUC the only thing remaining here is to get an OK from @ncoghlan
|
And please document PEP 562 in the What's New document. |
serhiy-storchaka
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. But perhaps @ncoghlan has other suggestions about the documentation.
Thanks, this is a nice improvement.
| computed value or raise an :exc:`AttributeError`. If an attribute is | ||
| not found on a module object through the normal lookup, i.e. | ||
| :meth:`object.__getattribute__`, then ``__getattr__`` is searched in | ||
| the module ``__dict__`` before raising an :exc:`AttributeError`. If found, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is an index entry for __dict__ as the module attribute. But I don't know if it is possible to refer to the target of an index entry.
|
+1 to merging from me (I haven't actually reviewed the docs updates yet,
but I trust the reviews you've already received, and if I turn out to have
comments after I get back from holidays we can always make further
adjustments later)
|
OK, then I am going to merge this now. |
|
Whee!
|
This is a basic implementation of the PEP. Suggestions are welcome.
@gvanrossum @serhiy-storchaka Can one of you (or both?) please review?
https://bugs.python.org/issue32225