Skip to content

Commit 9c05487

Browse files
committed
Don't allow bindings to shadow builtins until reached
1 parent 6e18e9b commit 9c05487

File tree

4 files changed

+263
-2
lines changed

4 files changed

+263
-2
lines changed

Diff for: src/ssort/_builtins.py

+183
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,186 @@
1+
MODULE_BUILTINS = {
2+
"_",
3+
"__annotations__",
4+
"__build_class__",
5+
"__builtins__",
6+
"__cached__",
7+
"__debug__",
8+
"__doc__",
9+
"__file__",
10+
"__import__",
11+
"__loader__",
12+
"__name__",
13+
"__package__",
14+
"__path__",
15+
"__spec__",
16+
"ArithmeticError",
17+
"AssertionError",
18+
"AttributeError",
19+
"BaseException",
20+
"BaseExceptionGroup",
21+
"BlockingIOError",
22+
"BrokenPipeError",
23+
"BufferError",
24+
"BytesWarning",
25+
"ChildProcessError",
26+
"ConnectionAbortedError",
27+
"ConnectionError",
28+
"ConnectionRefusedError",
29+
"ConnectionResetError",
30+
"DeprecationWarning",
31+
"Ellipsis",
32+
"EncodingWarning",
33+
"EnvironmentError",
34+
"EOFError",
35+
"Exception",
36+
"ExceptionGroup",
37+
"False",
38+
"FileExistsError",
39+
"FileNotFoundError",
40+
"FloatingPointError",
41+
"FutureWarning",
42+
"GeneratorExit",
43+
"ImportError",
44+
"ImportWarning",
45+
"IndentationError",
46+
"IndexError",
47+
"InterruptedError",
48+
"IOError",
49+
"IsADirectoryError",
50+
"KeyboardInterrupt",
51+
"KeyError",
52+
"LookupError",
53+
"MemoryError",
54+
"ModuleNotFoundError",
55+
"NameError",
56+
"None",
57+
"NotADirectoryError",
58+
"NotImplemented",
59+
"NotImplementedError",
60+
"OSError",
61+
"OverflowError",
62+
"PendingDeprecationWarning",
63+
"PermissionError",
64+
"ProcessLookupError",
65+
"RecursionError",
66+
"ReferenceError",
67+
"ResourceWarning",
68+
"RuntimeError",
69+
"RuntimeWarning",
70+
"StandardError",
71+
"StopAsyncIteration",
72+
"StopIteration",
73+
"SyntaxError",
74+
"SyntaxWarning",
75+
"SystemError",
76+
"SystemExit",
77+
"TabError",
78+
"TimeoutError",
79+
"True",
80+
"TypeError",
81+
"UnboundLocalError",
82+
"UnicodeDecodeError",
83+
"UnicodeEncodeError",
84+
"UnicodeError",
85+
"UnicodeTranslateError",
86+
"UnicodeWarning",
87+
"UserWarning",
88+
"ValueError",
89+
"Warning",
90+
"WindowsError",
91+
"ZeroDivisionError",
92+
"abs",
93+
"aiter",
94+
"all",
95+
"anext",
96+
"any",
97+
"apply",
98+
"ascii",
99+
"basestring",
100+
"bin",
101+
"bool",
102+
"breakpoint",
103+
"buffer",
104+
"bytearray",
105+
"bytes",
106+
"callable",
107+
"chr",
108+
"classmethod",
109+
"cmp",
110+
"coerce",
111+
"compile",
112+
"complex",
113+
"copyright",
114+
"credits",
115+
"delattr",
116+
"dict",
117+
"dir",
118+
"divmod",
119+
"enumerate",
120+
"eval",
121+
"exec",
122+
"execfile",
123+
"exit",
124+
"file",
125+
"filter",
126+
"float",
127+
"format",
128+
"frozenset",
129+
"getattr",
130+
"globals",
131+
"hasattr",
132+
"hash",
133+
"help",
134+
"hex",
135+
"id",
136+
"input",
137+
"int",
138+
"intern",
139+
"isinstance",
140+
"issubclass",
141+
"iter",
142+
"len",
143+
"license",
144+
"list",
145+
"locals",
146+
"long",
147+
"map",
148+
"max",
149+
"memoryview",
150+
"min",
151+
"next",
152+
"object",
153+
"oct",
154+
"open",
155+
"ord",
156+
"pow",
157+
"print",
158+
"property",
159+
"quit",
160+
"range",
161+
"raw_input",
162+
"reduce",
163+
"reload",
164+
"repr",
165+
"reversed",
166+
"round",
167+
"set",
168+
"setattr",
169+
"slice",
170+
"sorted",
171+
"staticmethod",
172+
"str",
173+
"sum",
174+
"super",
175+
"tuple",
176+
"type",
177+
"unichr",
178+
"unicode",
179+
"vars",
180+
"xrange",
181+
"zip",
182+
}
183+
1184
CLASS_BUILTINS = {
2185
"__module__",
3186
"__qualname__",

Diff for: src/ssort/_dependencies.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from ssort._builtins import MODULE_BUILTINS
12
from ssort._graphs import Graph
23

34

@@ -26,6 +27,11 @@ def module_statements_graph(statements):
2627

2728
if requirement.name in scope:
2829
resolved[requirement] = scope[requirement.name]
30+
continue
31+
32+
if requirement.name in MODULE_BUILTINS:
33+
resolved[requirement] = None
34+
continue
2935

3036
for name in statement.bindings():
3137
scope[name] = statement
@@ -46,7 +52,7 @@ def module_statements_graph(statements):
4652

4753
for statement in statements:
4854
for requirement in statement.requirements():
49-
if requirement in resolved:
55+
if resolved.get(requirement) is not None:
5056
graph.add_dependency(statement, resolved[requirement])
5157

5258
# Add links between statements that overwrite the same binding to make sure

Diff for: tests/test_builtins.py

+58-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1-
from ssort._builtins import CLASS_BUILTINS
1+
import ast
2+
import builtins
3+
import os.path
4+
import subprocess
5+
6+
from ssort._builtins import CLASS_BUILTINS, MODULE_BUILTINS
7+
8+
9+
def _get_builtins():
10+
return set(dir(builtins))
211

312

413
def _get_class_builtins():
@@ -8,6 +17,54 @@ class Cls:
817
return set(Cls.builtins)
918

1019

20+
def _get_init_builtins():
21+
import _builtins
22+
23+
return set(dir(_builtins))
24+
25+
26+
def _get_main_builtins():
27+
return set(
28+
ast.literal_eval(
29+
subprocess.run(
30+
["python3", "_builtins"],
31+
capture_output=True,
32+
check=True,
33+
cwd=os.path.dirname(__file__),
34+
encoding="utf-8",
35+
).stdout
36+
)
37+
)
38+
39+
40+
def _get_module_builtins():
41+
from _builtins import module
42+
43+
return set(dir(module))
44+
45+
46+
def test_no_new_builtins():
47+
# Checks that python hasn't introduced any new builtins that aren't in our
48+
# list.
49+
missing = _get_builtins() - MODULE_BUILTINS
50+
assert not missing
51+
52+
1153
def test_no_new_class_builtins():
1254
missing = _get_class_builtins() - {"builtins", *CLASS_BUILTINS}
1355
assert not missing
56+
57+
58+
def test_no_new_init_builtins():
59+
missing = _get_init_builtins() - MODULE_BUILTINS
60+
assert not missing
61+
62+
63+
def test_no_new_main_builtins():
64+
missing = _get_main_builtins() - MODULE_BUILTINS
65+
assert not missing
66+
67+
68+
def test_no_new_module_builtins():
69+
missing = _get_module_builtins() - MODULE_BUILTINS
70+
assert not missing

Diff for: tests/test_ssort.py

+15
Original file line numberDiff line numberDiff line change
@@ -653,3 +653,18 @@ def f():
653653

654654
actual = ssort(original)
655655
assert actual == expected
656+
657+
658+
def test_shadow_builtin():
659+
original = _clean(
660+
"""
661+
print("Hello, World!")
662+
663+
def print(message):
664+
pass
665+
"""
666+
)
667+
expected = original
668+
669+
actual = ssort(original)
670+
assert actual == expected

0 commit comments

Comments
 (0)