Skip to content
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

Check bare raise occurs in except clause #57

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion pyflakes/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,17 @@ def getParent(self, node):
if not hasattr(node, 'elts') and not hasattr(node, 'ctx'):
return node

def has_parent_type(self, node, cls):
"""Check if node has any parent of type cls."""
while node:
try:
node = self.getParent(node)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the difference between node.parent and self.getParent(node)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ask because CONTINUE below uses node.parent. I can't remember if I didn't use getParent on purpose or not.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getParent also skips some parents when if not hasattr(node, 'elts') and not hasattr(node, 'ctx') is not True.

However, not isnt really needed in this case, and inline-ing getParent will like be faster, or at least more clear anyway.

Also, this routine is probably better named has_ancestor_type.

except AttributeError:
return False
if isinstance(node, cls):
return True
return False

def getCommonAncestor(self, lnode, rnode, stop):
if stop in (lnode, rnode) or not (hasattr(lnode, 'parent') and
hasattr(rnode, 'parent')):
Expand Down Expand Up @@ -746,7 +757,7 @@ def ignore(self, node):

# "stmt" type nodes
DELETE = PRINT = FOR = ASYNCFOR = WHILE = IF = WITH = WITHITEM = \
ASYNCWITH = ASYNCWITHITEM = RAISE = TRYFINALLY = EXEC = \
ASYNCWITH = ASYNCWITHITEM = TRYFINALLY = EXEC = \
EXPR = ASSIGN = handleChildren

PASS = ignore
Expand Down Expand Up @@ -1071,6 +1082,19 @@ def IMPORTFROM(self, node):
importation = Importation(name, node)
self.addBinding(node, importation)

def RAISE(self, node):
try:
next(iter_child_nodes(node))
has_children = True
except StopIteration:
has_children = False

if not has_children:
if not self.has_parent_type(node, ast.ExceptHandler):
self.report(messages.ReraiseOutsideExcept, node)

self.handleChildren(node)

def TRY(self, node):
handler_names = []
# List the exception handlers
Expand Down
7 changes: 7 additions & 0 deletions pyflakes/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,13 @@ class ContinueInFinally(Message):
message = '\'continue\' not supported inside \'finally\' clause'


class ReraiseOutsideExcept(Message):
"""
A bare raise used outside an except clause.
"""
message = '\'raise\' not supported outside \'except\' clause'


class DefaultExceptNotLast(Message):
"""
Indicates an except: block as not the last exception handler.
Expand Down
63 changes: 63 additions & 0 deletions pyflakes/test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -885,6 +885,69 @@ def test_defaultExceptNotLast(self):
pass
''', m.DefaultExceptNotLast, m.DefaultExceptNotLast)

def test_raise_name(self):
self.flakes('''
raise Exception
''')

def test_raise_object(self):
self.flakes('''
raise Exception()
''')

def test_raise_object_in_try(self):
self.flakes('''
try:
raise Exception()
except Exception:
pass
''')

def test_reraise_in_except(self):
self.flakes('''
try:
int('x')
except ValueError:
raise
''')

def test_reraise_in_except_with_if(self):
self.flakes('''
try:
int('x')
except ValueError as e:
if True:
raise
''')

def test_reraise_in_try(self):
self.flakes('''
try:
raise
except Exception:
pass
''', m.ReraiseOutsideExcept)

def test_reraise_in_finally(self):
self.flakes('''
try:
int('x')
except ValueError:
pass
finally:
raise
''', m.ReraiseOutsideExcept)

def test_reraise_in_else(self):
self.flakes('''
try:
int('x')
except ValueError:
pass
else:
raise
''', m.ReraiseOutsideExcept)

@skipIf(version_info < (3,), "Python 3 only")
def test_starredAssignmentNoError(self):
"""
Expand Down