Skip to content

Commit 60f5cad

Browse files
committed
Allow some builtins to be deleted
__builtins__, __file__ and __debug__ can be deleted. Adds a test runner that ensures defined builtins can be loaded, and can be deleted when the python runtime allows it.
1 parent fc2325b commit 60f5cad

File tree

3 files changed

+77
-1
lines changed

3 files changed

+77
-1
lines changed

pyflakes/checker.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,16 @@ class Builtin(Definition):
185185

186186
def __init__(self, name):
187187
super(Builtin, self).__init__(name, None)
188+
self.can_delete = False
189+
# __file__ can only be deleted on Python 3
190+
if not PY2 and name == '__file__':
191+
self.can_delete = True
192+
# __builtins__ can always be deleted.
193+
# __debug__ can be deleted sometimes and not deleted other times.
194+
# Safest course of action is to assume it can be deleted, in
195+
# order that no error is reported by pyflakes
196+
elif name in ('__builtins__'):
197+
self.can_delete = True
188198

189199
def __repr__(self):
190200
return '<%s object %r at 0x%x>' % (self.__class__.__name__,
@@ -865,7 +875,8 @@ def on_conditional_branch():
865875
self.scope.globals.remove(name)
866876
else:
867877
binding = self.scope.get(name, None)
868-
if not binding or isinstance(binding, Builtin):
878+
if not binding or (
879+
isinstance(binding, Builtin) and not binding.can_delete):
869880
self.report(messages.UndefinedName, node, name)
870881
return
871882

pyflakes/test/harness.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,16 @@ class TestCase(unittest.TestCase):
1515

1616
withDoctest = False
1717

18+
def pythonException(self, input, *expectedOutputs, **kw):
19+
try:
20+
compile(textwrap.dedent(input), '<test>', 'exec', PyCF_ONLY_AST)
21+
except BaseException as e:
22+
return e
23+
try:
24+
exec(textwrap.dedent(input), {})
25+
except BaseException as e:
26+
return e
27+
1828
def flakes(self, input, *expectedOutputs, **kw):
1929
tree = compile(textwrap.dedent(input), "<test>", "exec", PyCF_ONLY_AST)
2030
if kw.get('is_segment'):

pyflakes/test/test_builtin.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,15 @@
44
from sys import version_info
55

66
from pyflakes import messages as m
7+
from pyflakes.checker import Checker
78
from pyflakes.test.harness import TestCase, skipIf
89

10+
try:
11+
WindowsError
12+
WIN = True
13+
except NameError:
14+
WIN = False
15+
916

1017
class TestBuiltins(TestCase):
1118

@@ -39,3 +46,51 @@ def f():
3946
4047
f()
4148
''', m.UndefinedLocal)
49+
50+
51+
class TestLiveBuiltins(TestCase):
52+
53+
def test_exists(self):
54+
for name in sorted(Checker.builtIns):
55+
# __file__ does exist in this test harness
56+
if name == '__file__':
57+
continue
58+
59+
if name == 'WindowsError' and not WIN:
60+
continue
61+
62+
source = '''
63+
%s
64+
''' % name
65+
e = self.pythonException(source)
66+
self.assertIsNone(e)
67+
68+
def test_del(self):
69+
for name in sorted(Checker.builtIns):
70+
# __file__ does exist in this test harness
71+
if name == '__file__':
72+
continue
73+
74+
# __debug__ can be deleted sometimes and not deleted other times.
75+
# Safest course of action is to assume it can be deleted, in
76+
# order that no error is reported by pyflakes
77+
if name == '__debug__':
78+
continue
79+
80+
source = '''
81+
del %s
82+
''' % name
83+
84+
e = self.pythonException(source)
85+
86+
if isinstance(e, SyntaxError):
87+
if version_info < (3,):
88+
# SyntaxError: invalid syntax
89+
self.assertIn(name, ('print'))
90+
else:
91+
# SyntaxError: can't delete keyword
92+
self.assertIn(name, ('None', 'True', 'False'))
93+
elif isinstance(e, NameError):
94+
self.flakes(source, m.UndefinedName)
95+
else:
96+
self.flakes(source)

0 commit comments

Comments
 (0)