diff options
author | Aviral Srivastava <avi.srivastava254084@gmail.com> | 2021-02-01 09:38:44 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-02-01 12:38:44 -0500 |
commit | 000cde59847beaf5fa7b73633e1f3c898fe5bf90 (patch) | |
tree | 9b6036270daecbf44973288fed04215eeb8d24a5 | |
parent | bpo-42834: Fix _json internal caches for subinterpreters (GH-24121) (diff) | |
download | cpython-000cde59847beaf5fa7b73633e1f3c898fe5bf90.tar.gz cpython-000cde59847beaf5fa7b73633e1f3c898fe5bf90.tar.bz2 cpython-000cde59847beaf5fa7b73633e1f3c898fe5bf90.zip |
bpo-38307: Add end_lineno attribute to pyclbr Objects (GH-24348)
For back-compatibility, make the new constructor parameter for public classes Function and Class
keyword-only with a default of None.
Co-authored-by: Aviral Srivastava <aviralsrivastava@Avirals-MacBook-Air.local
Co-authored-by: Terry Jan Reedy <tjreedy@udel.edu>
-rw-r--r-- | Doc/whatsnew/3.10.rst | 8 | ||||
-rw-r--r-- | Lib/idlelib/idle_test/test_browser.py | 18 | ||||
-rw-r--r-- | Lib/pyclbr.py | 40 | ||||
-rw-r--r-- | Lib/test/test_pyclbr.py | 22 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2020-03-16-03-03-21.bpo-38307.2cmw2i.rst | 3 |
5 files changed, 56 insertions, 35 deletions
diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 3dccb7c5001..d80ceeca85a 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -435,6 +435,14 @@ py_compile Added ``--quiet`` option to command-line interface of :mod:`py_compile`. (Contributed by Gregory Schevchenko in :issue:`38731`.) +pyclbr +------ + +Added an ``end_lineno`` attribute to the ``Function`` and ``Class`` +objects in the tree returned by :func:`pyclbr.readline` and +:func:`pyclbr.readline_ex`. It matches the existing (start) ``lineno``. +(Contributed by Aviral Srivastava in :issue:`38307`.) + shelve ------ diff --git a/Lib/idlelib/idle_test/test_browser.py b/Lib/idlelib/idle_test/test_browser.py index 25d6dc66303..03a50f22ca1 100644 --- a/Lib/idlelib/idle_test/test_browser.py +++ b/Lib/idlelib/idle_test/test_browser.py @@ -61,15 +61,15 @@ class ModuleBrowserTest(unittest.TestCase): # Nested tree same as in test_pyclbr.py except for supers on C0. C1. mb = pyclbr module, fname = 'test', 'test.py' -C0 = mb.Class(module, 'C0', ['base'], fname, 1) -F1 = mb._nest_function(C0, 'F1', 3) -C1 = mb._nest_class(C0, 'C1', 6, ['']) -C2 = mb._nest_class(C1, 'C2', 7) -F3 = mb._nest_function(C2, 'F3', 9) -f0 = mb.Function(module, 'f0', fname, 11) -f1 = mb._nest_function(f0, 'f1', 12) -f2 = mb._nest_function(f1, 'f2', 13) -c1 = mb._nest_class(f0, 'c1', 15) +C0 = mb.Class(module, 'C0', ['base'], fname, 1, end_lineno=9) +F1 = mb._nest_function(C0, 'F1', 3, 5) +C1 = mb._nest_class(C0, 'C1', 6, 9, ['']) +C2 = mb._nest_class(C1, 'C2', 7, 9) +F3 = mb._nest_function(C2, 'F3', 9, 9) +f0 = mb.Function(module, 'f0', fname, 11, end_lineno=15) +f1 = mb._nest_function(f0, 'f1', 12, 14) +f2 = mb._nest_function(f1, 'f2', 13, 13) +c1 = mb._nest_class(f0, 'c1', 15, 15) mock_pyclbr_tree = {'C0': C0, 'f0': f0} # Adjust C0.name, C1.name so tests do not depend on order. diff --git a/Lib/pyclbr.py b/Lib/pyclbr.py index f0c8381946c..ebcc23c29da 100644 --- a/Lib/pyclbr.py +++ b/Lib/pyclbr.py @@ -21,6 +21,7 @@ has the following attributes: name -- name of the object; file -- file in which the object is defined; lineno -- line in the file where the object's definition starts; + end_lineno -- line in the file where the object's definition ends; parent -- parent of this object, if any; children -- nested objects contained in this object. The 'children' attribute is a dictionary mapping names to objects. @@ -52,40 +53,50 @@ _modules = {} # Initialize cache of modules we've seen. class _Object: "Information about Python class or function." - def __init__(self, module, name, file, lineno, parent): + def __init__(self, module, name, file, lineno, end_lineno, parent): self.module = module self.name = name self.file = file self.lineno = lineno + self.end_lineno = end_lineno self.parent = parent self.children = {} if parent is not None: parent.children[name] = self + +# Odd Function and Class signatures are for back-compatibility. class Function(_Object): "Information about a Python function, including methods." - def __init__(self, module, name, file, lineno, parent=None, is_async=False): - super().__init__(module, name, file, lineno, parent) + def __init__(self, module, name, file, lineno, + parent=None, is_async=False, *, end_lineno=None): + super().__init__(module, name, file, lineno, end_lineno, parent) self.is_async = is_async if isinstance(parent, Class): parent.methods[name] = lineno + class Class(_Object): "Information about a Python class." - def __init__(self, module, name, super_, file, lineno, parent=None): - super().__init__(module, name, file, lineno, parent) + def __init__(self, module, name, super_, file, lineno, + parent=None, *, end_lineno=None): + super().__init__(module, name, file, lineno, end_lineno, parent) self.super = super_ or [] self.methods = {} + # These 2 functions are used in these tests # Lib/test/test_pyclbr, Lib/idlelib/idle_test/test_browser.py -def _nest_function(ob, func_name, lineno, is_async=False): +def _nest_function(ob, func_name, lineno, end_lineno, is_async=False): "Return a Function after nesting within ob." - return Function(ob.module, func_name, ob.file, lineno, ob, is_async) + return Function(ob.module, func_name, ob.file, lineno, + parent=ob, is_async=is_async, end_lineno=end_lineno) -def _nest_class(ob, class_name, lineno, super=None): +def _nest_class(ob, class_name, lineno, end_lineno, super=None): "Return a Class after nesting within ob." - return Class(ob.module, class_name, super, ob.file, lineno, ob) + return Class(ob.module, class_name, super, ob.file, lineno, + parent=ob, end_lineno=end_lineno) + def readmodule(module, path=None): """Return Class objects for the top-level classes in module. @@ -108,6 +119,7 @@ def readmodule_ex(module, path=None): """ return _readmodule(module, path or []) + def _readmodule(module, path, inpackage=None): """Do the hard work for readmodule[_ex]. @@ -198,9 +210,8 @@ class _ModuleBrowser(ast.NodeVisitor): bases.append(name) parent = self.stack[-1] if self.stack else None - class_ = Class( - self.module, node.name, bases, self.file, node.lineno, parent - ) + class_ = Class(self.module, node.name, bases, self.file, node.lineno, + parent=parent, end_lineno=node.end_lineno) if parent is None: self.tree[node.name] = class_ self.stack.append(class_) @@ -209,9 +220,8 @@ class _ModuleBrowser(ast.NodeVisitor): def visit_FunctionDef(self, node, *, is_async=False): parent = self.stack[-1] if self.stack else None - function = Function( - self.module, node.name, self.file, node.lineno, parent, is_async - ) + function = Function(self.module, node.name, self.file, node.lineno, + parent, is_async, end_lineno=node.end_lineno) if parent is None: self.tree[node.name] = function self.stack.append(function) diff --git a/Lib/test/test_pyclbr.py b/Lib/test/test_pyclbr.py index 2c7afa994f3..82c1ebb5b07 100644 --- a/Lib/test/test_pyclbr.py +++ b/Lib/test/test_pyclbr.py @@ -176,15 +176,15 @@ class PyclbrTest(TestCase): actual = mb._create_tree(m, p, f, source, t, i) # Create descriptors, linked together, and expected dict. - f0 = mb.Function(m, 'f0', f, 1) - f1 = mb._nest_function(f0, 'f1', 2) - f2 = mb._nest_function(f1, 'f2', 3) - c1 = mb._nest_class(f0, 'c1', 5) - C0 = mb.Class(m, 'C0', None, f, 6) - F1 = mb._nest_function(C0, 'F1', 8) - C1 = mb._nest_class(C0, 'C1', 11) - C2 = mb._nest_class(C1, 'C2', 12) - F3 = mb._nest_function(C2, 'F3', 14) + f0 = mb.Function(m, 'f0', f, 1, end_lineno=5) + f1 = mb._nest_function(f0, 'f1', 2, 4) + f2 = mb._nest_function(f1, 'f2', 3, 3) + c1 = mb._nest_class(f0, 'c1', 5, 5) + C0 = mb.Class(m, 'C0', None, f, 6, end_lineno=14) + F1 = mb._nest_function(C0, 'F1', 8, 10) + C1 = mb._nest_class(C0, 'C1', 11, 14) + C2 = mb._nest_class(C1, 'C2', 12, 14) + F3 = mb._nest_function(C2, 'F3', 14, 14) expected = {'f0':f0, 'C0':C0} def compare(parent1, children1, parent2, children2): @@ -203,8 +203,8 @@ class PyclbrTest(TestCase): self.assertIs(ob.parent, parent2) for key in children1.keys(): o1, o2 = children1[key], children2[key] - t1 = type(o1), o1.name, o1.file, o1.module, o1.lineno - t2 = type(o2), o2.name, o2.file, o2.module, o2.lineno + t1 = type(o1), o1.name, o1.file, o1.module, o1.lineno, o1.end_lineno + t2 = type(o2), o2.name, o2.file, o2.module, o2.lineno, o2.end_lineno self.assertEqual(t1, t2) if type(o1) is mb.Class: self.assertEqual(o1.methods, o2.methods) diff --git a/Misc/NEWS.d/next/Library/2020-03-16-03-03-21.bpo-38307.2cmw2i.rst b/Misc/NEWS.d/next/Library/2020-03-16-03-03-21.bpo-38307.2cmw2i.rst new file mode 100644 index 00000000000..358089915fb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-03-16-03-03-21.bpo-38307.2cmw2i.rst @@ -0,0 +1,3 @@ +Add an 'end_lineno' attribute to the Class and Function objects that appear in the
+tree returned by pyclbr functions. This and the existing 'lineno'
+attribute define the extent of class and def statements. Patch by Aviral Srivastava.
|