-
-
Notifications
You must be signed in to change notification settings - Fork 32k
bpo-6691: Support for nested classes and functions in pyclbr #2503
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
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.
After the patch, the biggest flaw of the module, in my opinion, will be that is discards the line order of statements. If a collections.OrderedDict is forward compatible with dicts, I would like to make the replacement. I want to ask Raymond H about this.
Cheryl, there are a couple of questions about the isinstance checks that might be related to the test failure and fix you mentioned.
Doc/library/pyclbr.rst
Outdated
@@ -39,78 +39,77 @@ modules. | |||
path. |
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 presume that the opening paragraph should be revised. 'top-level' removed. I don't know what a 'traditional three-pane class browser' is, The addition of functions makes this a module browser.
readmodule_ex: since there is not another function added, I presume that the return of readmodule_ex has been expanded so that 'top-level' is obsolete here too. I will look at the actual behavior before editing.
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.
Fixed, a72
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 guessing that I cannot hide this as outdated because there was no subsequent change at this location. I will try approving and then making a new review.
Doc/library/pyclbr.rst
Outdated
-------------- | ||
The class :class:`Object` is the base class for the classes | ||
:class:`Class` and :class:`Function`. It provides the following | ||
data members: |
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.
An added in 3.7 note is needed. I am not sure if it goes here or after attributes. In any case, most attributes were already common to Class and Function.
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.
Addition notes looks good to me, but I am not clear on the rules. See comment below about core-mentorship question.
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.
Approval did not collapse these.
Doc/library/pyclbr.rst
Outdated
|
||
.. attribute:: Class.lineno | ||
The parent of this object, if any. |
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.
New attribute added in 3.7
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.
Comment misplaced. Actual new attribute now tagged.
Doc/library/pyclbr.rst
Outdated
|
||
.. _pyclbr-function-objects: | ||
A dictionary mapping object names to the objects that are defined inside the | ||
namespace created by the current object. |
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.
New attribute added in 3.7. By inheritance, added in 3.7 to Class and Function. As I say below, this new attribute should be 'children'.
Doc/library/pyclbr.rst
Outdated
classes of the class being described. Classes which are named as | ||
superclasses but which are not discoverable by :func:`readmodule` are | ||
listed as a string with the class name instead of as :class:`Class` | ||
objects. |
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 and 'methods' below, with the 'old' attributes in Object, constitute the old set of attributes for Class.
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.
No specific fix needed.
Lib/pyclbr.py
Outdated
fname, lineno) | ||
stack.append((None, thisindent)) # Marker for nested fns | ||
cur_func = Function(fullmodule, meth_name, fname, lineno) | ||
dict[meth_name] = cur_func |
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.
140 # Initialize the dict for this module's contents
141 dict = {}
I dislike this and would like to fix it. 'obs'? If '.objects' is renamed 'children', we could rename the master dict 'objects'. Or to be more specific, 'heads'. Since each top-level object is parentless and the head of a tree in the forest. 'tree' or 'trees' and 'forest' are also possibilities. "tree['f'] = Function(..,'f',...)" makes some sense.
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.
dict changed to tree
Lib/pyclbr.py
Outdated
def _main(): | ||
# Main program for testing. | ||
import os | ||
from operator import itemgetter | ||
mod = sys.argv[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.
sys.argv[1] may not exist if run from command line and generally will not if run from an editor. To default it to running on itself:
try:
mod = sys.argv[1]
except:
mod = file
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.
Awesome! I was calling this a lot when testing.
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.
And I did so thrice more. Fixed.
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.
Thnks. Im still lost
Lib/test/test_pyclbr.py
Outdated
@@ -150,6 +154,121 @@ def test_decorators(self): | |||
# | |||
self.checkModule('test.pyclbr_input', ignore=['om']) | |||
|
|||
def test_nested(self): | |||
|
|||
def clbr_from_tuple(t, store, parent=None, lineno=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.
tup instead of t
@@ -2,10 +2,14 @@ | |||
Test cases for pyclbr.py | |||
Nick Mathewson | |||
''' | |||
|
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.
The unpatched code, which I cannot directly comment on, has multiple print calls. I believe that production test code should not have these. Will ask on core-mentorship.
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, another issue.
Lib/test/test_pyclbr.py
Outdated
def pickp(name): | ||
return pclass if name.startswith('class') else pfunc | ||
|
||
# Create a module for storing the Python code. |
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.
_readmodule is about 200 lines. The first 58 lines turn module name, path, and package in source and fname. The rest turn source and fname into the dict to be returned. The first part is tested elsewhere. If we sensibly split is into private functions as indicated above, we could drop all this bother of turning source into a temp file and just pass it to the 2nd new function.
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.
Split done. Test not yet simplified because _create_tree needs 5 arguments, not 2. I would still like to simplify by new tests, but since they are working, this could wait for a separate pyclbr tests issue.
Some of the changes I suggested are outside the scope of this issue and should be left for another. Using DefaultDict is one issue. Removing print calls from tests is another. I think splitting the private function into two to simplify the test of the new feature can be included, but as a separate commit. |
I made a separate commit for splitting _readmodule into two functions. It's only 5 lines, including whitespace and docstring. It didn't pass the tests at first and I've been debugging it for hours. It ended up that the calls back to Thanks! |
I believe _Object should be private, if not removed. I asked on core-mentorship about the current added/changed notes as well as what is allowed in the doc if Object is removed. |
In a previous version of this comment, I said It is still true that there is no test of readmodule, and that one might be added in a new test revision issue. With a test, the implementation could be reduced to a dict comprehension. |
Find in Files says neither _getname nor _getnamelist appear in test_pyclbr. Adding some would be another issue. Without knowing in detail what tokenizer emits, I am not sure they will handle legal multiline imports like |
I assumed that any missing parameters would immediately result in NameErrors. If the test failed because of a missing parameter, and did not raise NameError, I would be surprised and puzzled. Looking at the test, one problem I see is that test_others() does multiple cm tests in sequence, so first to fail stops testing of the rest. If different files exercise different pathways with different needed parameters, it would take several runs to get them all. I am sorry that this was much more troublesome that I expected. This function should be revised to use subtests (self.subTest) so all run regardless of failure. This is at least the fourth thing to fix in this file. Prints, temporary file, and missing tests are another. |
After review, I think the only thing left for this issue is to make Object private. If I get no response on core mentorship, I will copy the current redundant doc format, but put Function first and note on Class that it has 2 more. The doc should also mention that Class.methods is redundant with Class.children in that the former lists the Functions in the latter. It is only kept for back-compatibility. |
This wasn't a problem at all. Every chance I get to debug and track something down is more learning for me, so it's all good. I apologize if it sounded like I was complaining. I just brought it up because I was surprised I didn't get a warning while running the tests and to find if there were any tricks (or tools) that would have helped me reach the endpoint sooner. |
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 changed my mind about the new test. It is so opaque that I trust it less than the code. The function that creates code I cannot see from a tuple structure and the the custom equality function that compares the nested output to nodes created from the tuple structure need tests themselves ;-).
Transparent would be a chunk of code that is fed to readmodule_ex and a constructed dict that is obviously correct for the code, which is compared to the output of readmodule_ex. Python already has tested dict equality comparison.
The tricky part of the construction is the double linkage, which is completed with _add_child. This new method should be tested (easy).
Doc/library/pyclbr.rst
Outdated
|
||
Class Objects | ||
------------- | ||
Object Objects |
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 want to make Object private and delete this part of the doc.
Lib/pyclbr.py
Outdated
'''Class to represent a Python class.''' | ||
def __init__(self, module, name, super, file, lineno): | ||
|
||
class Object: |
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.
_Object here and below.
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 I need to figure out how to pull in your commit if you would like me to make these changes.
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.
The docstrings and comments are already part of your branch, and visible when I hit [files] at the top. I have already done the _Object changes, the _create_tree docstring and setup for the revised test. I am working on the hard part -- a revised comparison function. I hope to push all within an hour.
|
||
def _create_tree(fullmodule, path, fname, source, tree, inpackage): |
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 needs docstring with explaination of parameters, which is needed to make direct call in test.
Thanks for making all those changes. I know you said it needs more, but it looks awesome! |
I think I did what I said was essential. Anyway, there were two conceptual improvements.
Added now: I believe the helpers should not just call .add_child, but should be incorporated into it, and the Class override.
Side node: general tree equality is hard because it is not initially known which child to pair with which. Here, the children for each node are uniquely labelled and the labels must match. Added now. This is still clumsy. A possible solution: rename _Object as _Node. Replace None as the mod-level parent with top, an instance of _Node. Prime the _create_tree pump by initializing stack with [(top, -1)]. Since top is never popped, stack is never empty and we dump the stack test. Add all Function and Class node with (stack[-1][0]).add_child. The creation of 'expected' in the test start with top = _Node(...) and all the addition would be with something.add_child. The compare function would have two parameters, node1 and node2, and would be .eq of _Node (where it could be tested). _Node would then need one more method, .export_children. It would change the parent of all the children to None and then return the dict to be returned to users. Just where to do this would be a detail to be worked out after checking the tests. I am not going to do this, unless revision is needed for IDLE. I would rather work on applying the lesson of matching code to concepts to IDLE. |
* Revert "bpo-30854: Fix compile error when --without-threads (#2581)" This reverts commit 0c31163. * Revert "NEWS for 30777 (#2576)" This reverts commit aaa917f. * Revert "bpo-21624: IDLE -- minor htest fixes (#2575)" This reverts commit 2000150. * Revert "bpo-30777: IDLE: configdialog - add docstrings and improve comments (#2440)" This reverts commit 7eb5883. * Revert "bpo-30319: socket.close() now ignores ECONNRESET (#2565)" This reverts commit 67e1478. * Revert "bpo-30789: Use a single memory block for co_extra. (#2555)" This reverts commit 378ebb6. * Revert "bpo-30845: Enhance test_concurrent_futures cleanup (#2564)" This reverts commit 3df9dec. * Revert "bpo-29293: multiprocessing.Condition.notify() lacks parameter `n` (#2480)" This reverts commit 4835041. * Revert "Remove outdated FOX from GUI FAQ (GH-2538)" This reverts commit d3ed287. * Revert "bpo-6691: Pyclbr now reports nested classes and functions. (#2503)" This reverts commit 246ff3b. * Revert "bpo-29464: Rename METH_FASTCALL to METH_FASTCALL|METH_KEYWORDS and make (#1955)" This reverts commit 6969eaf. * Revert "bpo-30832: Remove own implementation for thread-local storage (#2537)" This reverts commit aa0aa04. * Revert "bpo-30764: Fix regrtest --fail-env-changed --forever (#2536)" This reverts commit 5e87592. * Revert "bpo-30822: Deduplicate ZoneInfoTest classes in test_datetime. (#2534)" This reverts commit 34b5487. * Revert "bpo-30822: Fix testing of datetime module. (#2530)" This reverts commit 98b6bc3.
Based on the patch by Guilherme Polo.