You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
pydoc renders from builtins.type note, even if it is incorrect #97959
Python Library Documentation: class My in module __main__
class My(builtins.object)
| Class methods defined here:
|
| __init_subclass__(*args, **kwargs) from builtins.type
| This method is called when a class is subclassed.
|
| The default implementation does nothing. It may be
| overridden to extend subclasses.
|
| custom() from builtins.type
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
Take a look at these two entries:
__init_subclass__(*args, **kwargs) from builtins.type
custom() from builtins.type
While type has __init_subclass__, there's no type.custom. But, help says that there is!
I think that it is incorrect and can lead to confusion.
Instead it should be:
| __init_subclass__(*args, **kwargs) from builtins.type
| This method is called when a class is subclassed.
|
| The default implementation does nothing. It may be
| overridden to extend subclasses.
|
| custom()
Python Library Documentation: class My in module __main__
class My(builtins.object)
| Methods defined here:
|
| some(*args, **kwargs) from builtins.int
I think that classmethods should just completely skip this part.
It brings more confusion than value. So, it should be:
| __init_subclass__(*args, **kwargs)
| This method is called when a class is subclassed.
|
| The default implementation does nothing. It may be
| overridden to extend subclasses.
|
| custom()
I found only one: when MethodType is defined in a class body:
But where is it used in the real code?
If make this code raising an error instead of adding "from ..." and run pydoc for all stdlib modules, what will fail? Investigate also the history of that code, commit message or issue text can explain its purpose.
Running pydoc tests with raise ValueError instead of note = ' from ' + classname(imclass, mod) showed this:
2 tests failed:
test_enum test_pydoc
Only pydoc itself and enum are affected.
Basically, the same modules I've changed in #98120
history
There are two commits that touch this line:
15 years ago, when unbound methods were removed: ff73795
22 years ago, when b7a4830 was merged. It does not have a description. Its title is Fix linking to classes (in class tree, and add links on unbound methods)
So, I can conclude that these lines have something to do with old unbound methods.
Will users be able to decrypt that x is a MethodType bound to int from this short message? I don't think so. I've spent several hours trying to decode what it means and how it works.
lib usage
Let's take a look at the enum case: it kinda is using this feature.
Is it useful here? Again, I don't think so: we already have Methods inherited from enum.EnumType: part which clearly says where these methods come from.
In my opinion, it brings more confusion than value.
solution
I think that we should at least protect @classmethod from been reported incorrectly, as I did in #98120
Or we can completely remove note = ' from ' + classname(imclass, mod) part, because:
It is an artifact from another era: when unbound methods were a thing
It does not bring any value to existing Lib/ code
It is quite cryptic to understand for regular users
It is easy to do, nothing will probably break because of that
This issue is much much more complex than it looked at first glance. If you look at the code, you will see other issues with notes for routines. For example an "unbound method" note is never added to unbound methods. It does not work with methods of builtin classes. It seems that this code was written to handle classic classes, and the support of new-style classes was never complete. Some changes was made later to fix runtime errors, but it was not checked that the result is correct and that all cases are supported.
Other issue, is that class members that are bound methods were included in the methods section. But they behave rather like static methods, because they do not accept the self argument.
There are still unfixed issues, for example nested classes are not supported (#85799), and unbound methods of builtin classes are not visible as module members (#113942), but this PR is already large.
| Methods inherited from enum.EnumType:
|
| __contains__(value) from enum.EnumType
| Return True if `value` is in `cls`.
First, "from enum.EnumType" is redundant, because there is "from enum.EnumType" in the section title. Usually "from" notes are not repeated for every method.
Second, the section title is not completely correct. They are not normal methods. The first argument of normal arguments is self (whatever its name). You cannot call enuminstance.__contains__(). You should call enumclass.__contains__(value) -- this is the behavior of static or class method. It is difficult to distinguish them at that level, so for now they are qualified as static methods.
BTW, for the "from" example look at the enum.StrEnum help. Now it contains:
| Methods defined here:
...
| __repr__(self) from enum.Enum
| Return repr(self).
|
| __str__(self, /) from builtins.str
| Return str(self).
Note that __str__ is "from builtins.str", and __repr__ is "from enum.Enum", despite both are "methods defined here". You can see that __str__ is the same as for strings, and __repr__ is implemented in Enum, and that they override methods inherited from base classes.
Activity
sobolevn commentedon Oct 6, 2022
A couple of extra thoughts after reading some more source code:
https://github.com/python/cpython/blame/e39ae6bef2c357a88e232dcab2e4b4c0f367544b/Lib/pydoc.py#L1032-L1042
Ok, looks like this is in sync with the following example:
which outputs:
I think that
classmethods should just completely skip this part.It brings more confusion than value. So, it should be:
pythongh-97959: [pydoc] now `help()` does not show extra notes for `c…
serhiy-storchaka commentedon Oct 10, 2022
Good catch.
Why this "from ..." was added in the first case? In what cases it was useful.
sobolevn commentedon Oct 10, 2022
I found only one: when
MethodTypeis defined in a class body:Plus,
Enumusesfrom enum.EnumTypeon some methods. I am still testing other things.serhiy-storchaka commentedon Oct 10, 2022
But where is it used in the real code?
If make this code raising an error instead of adding "from ..." and run pydoc for all stdlib modules, what will fail? Investigate also the history of that code, commit message or issue text can explain its purpose.
sobolevn commentedon Nov 7, 2022
So, I got back to it. Here's what I found.
test results
Running
pydoctests withraise ValueErrorinstead ofnote = ' from ' + classname(imclass, mod)showed this:Only
pydocitself andenumare affected.Basically, the same modules I've changed in #98120
history
There are two commits that touch this line:
Fix linking to classes (in class tree, and add links on unbound methods)So, I can conclude that these lines have something to do with old unbound methods.
= MethodType()usageThere are some usage of it:
But, lets think about whether is this important to show
from ...part. Compare these two (with and withoutfrom):Without:
xis aMethodTypebound tointfrom this short message? I don't think so. I've spent several hours trying to decode what it means and how it works.lib usage
Let's take a look at the
enumcase: it kinda is using this feature.Is it useful here? Again, I don't think so: we already have
Methods inherited from enum.EnumType:part which clearly says where these methods come from.In my opinion, it brings more confusion than value.
solution
I think that we should at least protect
@classmethodfrom been reported incorrectly, as I did in #98120Or we can completely remove
note = ' from ' + classname(imclass, mod)part, because:Lib/code@serhiy-storchaka what do you think? :)
ethanfurman commentedon Dec 9, 2022
I do not see any value in the
from some_module.some_type. I'd like to see it removed.ethanfurman commentedon Dec 9, 2022
Not sure if this is related, but
help(enum.Enum)shows:It seems to me that instead of
builtins.objectthat should sayenum.EnumType.pythongh-97959: Fix rendering of routines in pydoc
serhiy-storchaka commentedon Jan 11, 2024
This issue is much much more complex than it looked at first glance. If you look at the code, you will see other issues with notes for routines. For example an "unbound method" note is never added to unbound methods. It does not work with methods of builtin classes. It seems that this code was written to handle classic classes, and the support of new-style classes was never complete. Some changes was made later to fix runtime errors, but it was not checked that the result is correct and that all cases are supported.
Other issue, is that class members that are bound methods were included in the methods section. But they behave rather like static methods, because they do not accept the self argument.
#113941 fixes these and other issues.
There are still unfixed issues, for example nested classes are not supported (#85799), and unbound methods of builtin classes are not visible as module members (#113942), but this PR is already large.
gh-97959: Fix rendering of routines in pydoc (GH-113941)
[3.12] pythongh-97959: Fix rendering of routines in pydoc (pythonGH-1…
[3.12] gh-97959: Fix rendering of routines in pydoc (GH-113941) (GH-1…
[3.11] [3.12] pythongh-97959: Fix rendering of routines in pydoc (pyt…
[3.11] [3.12] gh-97959: Fix rendering of routines in pydoc (GH-113941) (
serhiy-storchaka commentedon Feb 11, 2024
About the
enum.Enumcase. It was:First, "from enum.EnumType" is redundant, because there is "from enum.EnumType" in the section title. Usually "from" notes are not repeated for every method.
Second, the section title is not completely correct. They are not normal methods. The first argument of normal arguments is
self(whatever its name). You cannot callenuminstance.__contains__(). You should callenumclass.__contains__(value)-- this is the behavior of static or class method. It is difficult to distinguish them at that level, so for now they are qualified as static methods.BTW, for the "from" example look at the enum.StrEnum help. Now it contains:
Note that
__str__is "from builtins.str", and__repr__is "from enum.Enum", despite both are "methods defined here". You can see that__str__is the same as for strings, and__repr__is implemented in Enum, and that they override methods inherited from base classes.pythongh-97959: Fix rendering of routines in pydoc (pythonGH-113941)