Compare commits
3 Commits
c7cf66d2fe
...
d1f07419c7
Author | SHA1 | Date |
---|---|---|
Miss Islington (bot) | d1f07419c7 | |
Miss Islington (bot) | 40b92f1cc0 | |
Miss Islington (bot) | 81ac030d03 |
|
@ -864,7 +864,12 @@ def findsource(object):
|
|||
lnum = object.co_firstlineno - 1
|
||||
pat = re.compile(r'^(\s*def\s)|(\s*async\s+def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
|
||||
while lnum > 0:
|
||||
if pat.match(lines[lnum]): break
|
||||
try:
|
||||
line = lines[lnum]
|
||||
except IndexError:
|
||||
raise OSError('lineno is out of bounds')
|
||||
if pat.match(line):
|
||||
break
|
||||
lnum = lnum - 1
|
||||
return lines, lnum
|
||||
raise OSError('could not find code object')
|
||||
|
@ -926,6 +931,7 @@ class BlockFinder:
|
|||
self.indecorator = False
|
||||
self.decoratorhasargs = False
|
||||
self.last = 1
|
||||
self.body_col0 = None
|
||||
|
||||
def tokeneater(self, type, token, srowcol, erowcol, line):
|
||||
if not self.started and not self.indecorator:
|
||||
|
@ -957,6 +963,8 @@ class BlockFinder:
|
|||
elif self.passline:
|
||||
pass
|
||||
elif type == tokenize.INDENT:
|
||||
if self.body_col0 is None and self.started:
|
||||
self.body_col0 = erowcol[1]
|
||||
self.indent = self.indent + 1
|
||||
self.passline = True
|
||||
elif type == tokenize.DEDENT:
|
||||
|
@ -966,6 +974,10 @@ class BlockFinder:
|
|||
# not e.g. for "if: else:" or "try: finally:" blocks)
|
||||
if self.indent <= 0:
|
||||
raise EndOfBlock
|
||||
elif type == tokenize.COMMENT:
|
||||
if self.body_col0 is not None and srowcol[1] >= self.body_col0:
|
||||
# Include comments if indented at least as much as the block
|
||||
self.last = srowcol[0]
|
||||
elif self.indent == 0 and type not in (tokenize.COMMENT, tokenize.NL):
|
||||
# any other token on the same indentation level end the previous
|
||||
# block as well, except the pseudo-tokens COMMENT and NL.
|
||||
|
|
|
@ -91,3 +91,25 @@ class Callable:
|
|||
|
||||
custom_method = Callable().as_method_of(42)
|
||||
del Callable
|
||||
|
||||
# line 95
|
||||
class WhichComments:
|
||||
# line 97
|
||||
# before f
|
||||
def f(self):
|
||||
# line 100
|
||||
# start f
|
||||
return 1
|
||||
# line 103
|
||||
# end f
|
||||
# line 105
|
||||
# after f
|
||||
|
||||
# before asyncf - line 108
|
||||
async def asyncf(self):
|
||||
# start asyncf
|
||||
return 2
|
||||
# end asyncf
|
||||
# after asyncf - line 113
|
||||
# end of WhichComments - line 114
|
||||
# after WhichComments - line 115
|
||||
|
|
|
@ -388,6 +388,7 @@ class TestRetrievingSourceCode(GetSourceBase):
|
|||
('ParrotDroppings', mod.ParrotDroppings),
|
||||
('StupidGit', mod.StupidGit),
|
||||
('Tit', mod.MalodorousPervert),
|
||||
('WhichComments', mod.WhichComments),
|
||||
])
|
||||
tree = inspect.getclasstree([cls[1] for cls in classes])
|
||||
self.assertEqual(tree,
|
||||
|
@ -401,7 +402,8 @@ class TestRetrievingSourceCode(GetSourceBase):
|
|||
[(mod.FesteringGob, (mod.MalodorousPervert,
|
||||
mod.ParrotDroppings))
|
||||
]
|
||||
]
|
||||
],
|
||||
(mod.WhichComments, (object,),)
|
||||
]
|
||||
])
|
||||
tree = inspect.getclasstree([cls[1] for cls in classes], True)
|
||||
|
@ -413,7 +415,8 @@ class TestRetrievingSourceCode(GetSourceBase):
|
|||
[(mod.FesteringGob, (mod.MalodorousPervert,
|
||||
mod.ParrotDroppings))
|
||||
]
|
||||
]
|
||||
],
|
||||
(mod.WhichComments, (object,),)
|
||||
]
|
||||
])
|
||||
|
||||
|
@ -644,6 +647,18 @@ class TestOneliners(GetSourceBase):
|
|||
# as argument to another function.
|
||||
self.assertSourceEqual(mod2.anonymous, 55, 55)
|
||||
|
||||
class TestBlockComments(GetSourceBase):
|
||||
fodderModule = mod
|
||||
|
||||
def test_toplevel_class(self):
|
||||
self.assertSourceEqual(mod.WhichComments, 96, 114)
|
||||
|
||||
def test_class_method(self):
|
||||
self.assertSourceEqual(mod.WhichComments.f, 99, 104)
|
||||
|
||||
def test_class_async_method(self):
|
||||
self.assertSourceEqual(mod.WhichComments.asyncf, 109, 112)
|
||||
|
||||
class TestBuggyCases(GetSourceBase):
|
||||
fodderModule = mod2
|
||||
|
||||
|
@ -695,6 +710,17 @@ class TestBuggyCases(GetSourceBase):
|
|||
self.assertRaises(IOError, inspect.findsource, co)
|
||||
self.assertRaises(IOError, inspect.getsource, co)
|
||||
|
||||
def test_findsource_with_out_of_bounds_lineno(self):
|
||||
mod_len = len(inspect.getsource(mod))
|
||||
src = '\n' * 2* mod_len + "def f(): pass"
|
||||
co = compile(src, mod.__file__, "exec")
|
||||
g, l = {}, {}
|
||||
eval(co, g, l)
|
||||
func = l['f']
|
||||
self.assertEqual(func.__code__.co_firstlineno, 1+2*mod_len)
|
||||
with self.assertRaisesRegex(IOError, "lineno is out of bounds"):
|
||||
inspect.findsource(func)
|
||||
|
||||
def test_getsource_on_method(self):
|
||||
self.assertSourceEqual(mod2.ClassWithMethod.method, 118, 119)
|
||||
|
||||
|
@ -4016,8 +4042,8 @@ def foo():
|
|||
|
||||
def test_main():
|
||||
run_unittest(
|
||||
TestDecorators, TestRetrievingSourceCode, TestOneliners, TestBuggyCases,
|
||||
TestInterpreterStack, TestClassesAndFunctions, TestPredicates,
|
||||
TestDecorators, TestRetrievingSourceCode, TestOneliners, TestBlockComments,
|
||||
TestBuggyCases, TestInterpreterStack, TestClassesAndFunctions, TestPredicates,
|
||||
TestGetcallargsFunctions, TestGetcallargsMethods,
|
||||
TestGetcallargsUnboundMethods, TestGetattrStatic, TestGetGeneratorState,
|
||||
TestNoEOL, TestSignatureObject, TestSignatureBind, TestParameterObject,
|
||||
|
|
|
@ -1103,6 +1103,18 @@ class TestTracebackException(unittest.TestCase):
|
|||
self.assertEqual(exc_info[0], exc.exc_type)
|
||||
self.assertEqual(str(exc_info[1]), str(exc))
|
||||
|
||||
def test_no_refs_to_exception_and_traceback_objects(self):
|
||||
try:
|
||||
1/0
|
||||
except Exception:
|
||||
exc_info = sys.exc_info()
|
||||
|
||||
refcnt1 = sys.getrefcount(exc_info[1])
|
||||
refcnt2 = sys.getrefcount(exc_info[2])
|
||||
exc = traceback.TracebackException(*exc_info)
|
||||
self.assertEqual(sys.getrefcount(exc_info[1]), refcnt1)
|
||||
self.assertEqual(sys.getrefcount(exc_info[2]), refcnt2)
|
||||
|
||||
def test_comparison_basic(self):
|
||||
try:
|
||||
1/0
|
||||
|
@ -1152,6 +1164,16 @@ class TestTracebackException(unittest.TestCase):
|
|||
exc7 = traceback.TracebackException(*exc_info, limit=-2, capture_locals=True)
|
||||
self.assertNotEqual(exc6, exc7)
|
||||
|
||||
def test_comparison_equivalent_exceptions_are_equal(self):
|
||||
excs = []
|
||||
for _ in range(2):
|
||||
try:
|
||||
1/0
|
||||
except:
|
||||
excs.append(traceback.TracebackException(*sys.exc_info()))
|
||||
self.assertEqual(excs[0], excs[1])
|
||||
self.assertEqual(list(excs[0].format()), list(excs[1].format()))
|
||||
|
||||
def test_unhashable(self):
|
||||
class UnhashableException(Exception):
|
||||
def __eq__(self, other):
|
||||
|
|
|
@ -500,7 +500,6 @@ class TracebackException:
|
|||
_seen=_seen)
|
||||
else:
|
||||
context = None
|
||||
self.exc_traceback = exc_traceback
|
||||
self.__cause__ = cause
|
||||
self.__context__ = context
|
||||
self.__suppress_context__ = \
|
||||
|
@ -617,7 +616,7 @@ class TracebackException:
|
|||
not self.__suppress_context__):
|
||||
yield from self.__context__.format(chain=chain)
|
||||
yield _context_message
|
||||
if self.exc_traceback is not None:
|
||||
if self.stack:
|
||||
yield 'Traceback (most recent call last):\n'
|
||||
yield from self.stack.format()
|
||||
yield from self.format_exception_only()
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
:class:`~traceback.TracebackException` no longer holds a reference to the exception's traceback object. Consequently, instances of TracebackException for equivalent but non-equal exceptions now compare as equal.
|
|
@ -0,0 +1 @@
|
|||
Fix handling of trailing comments by :func:`inspect.getsource`.
|
|
@ -0,0 +1,4 @@
|
|||
:func:`inspect.findsource` now raises :exc:`OSError` instead of
|
||||
:exc:`IndexError` when :attr:`co_lineno` of a code object is greater than the
|
||||
file length. This can happen, for example, when a file is edited after it was
|
||||
imported. PR by Irit Katriel.
|
Loading…
Reference in New Issue