Show More
@@ -916,15 +916,15 b' class Completer(Configurable):' | |||
|
916 | 916 | .. deprecated:: 8.8 |
|
917 | 917 | Use :any:`evaluation` and :any:`auto_close_dict_keys` instead. |
|
918 | 918 | |
|
919 |
When |
|
|
919 | When enabled in IPython 8.8+ activates following settings for compatibility: | |
|
920 | 920 | - ``evaluation = 'unsafe'`` |
|
921 | 921 | - ``auto_close_dict_keys = True`` |
|
922 | 922 | """, |
|
923 | 923 | ).tag(config=True) |
|
924 | 924 | |
|
925 | 925 | evaluation = Enum( |
|
926 |
("forbidden", "minimal", "limit |
|
|
927 |
default_value="limit |
|
|
926 | ("forbidden", "minimal", "limited", "unsafe", "dangerous"), | |
|
927 | default_value="limited", | |
|
928 | 928 | help="""Code evaluation under completion. |
|
929 | 929 | |
|
930 | 930 | Successive options allow to enable more eager evaluation for more accurate completion suggestions, |
@@ -934,7 +934,7 b' class Completer(Configurable):' | |||
|
934 | 934 | Allowed values are: |
|
935 | 935 | - `forbidden`: no evaluation at all |
|
936 | 936 | - `minimal`: evaluation of literals and access to built-in namespaces; no item/attribute evaluation nor access to locals/globals |
|
937 |
- `limit |
|
|
937 | - `limited` (default): access to all namespaces, evaluation of hard-coded methods (``keys()``, ``__getattr__``, ``__getitems__``, etc) on allow-listed objects (e.g. ``dict``, ``list``, ``tuple``, ``pandas.Series``) | |
|
938 | 938 | - `unsafe`: evaluation of all methods and function calls but not of syntax with side-effects like `del x`, |
|
939 | 939 | - `dangerous`: completely arbitrary evaluation |
|
940 | 940 | """, |
@@ -1651,7 +1651,7 b' class IPCompleter(Completer):' | |||
|
1651 | 1651 | self.auto_close_dict_keys = True |
|
1652 | 1652 | self.splitter.delims = GREEDY_DELIMS |
|
1653 | 1653 | else: |
|
1654 |
self.evaluation = "limit |
|
|
1654 | self.evaluation = "limited" | |
|
1655 | 1655 | self.auto_close_dict_keys = False |
|
1656 | 1656 | self.splitter.delims = DELIMS |
|
1657 | 1657 |
@@ -207,7 +207,7 b' class EvaluationContext(NamedTuple):' | |||
|
207 | 207 | locals_: dict |
|
208 | 208 | globals_: dict |
|
209 | 209 | evaluation: Literal[ |
|
210 |
"forbidden", "minimal", "limit |
|
|
210 | "forbidden", "minimal", "limited", "unsafe", "dangerous" | |
|
211 | 211 | ] = "forbidden" |
|
212 | 212 | in_subscript: bool = False |
|
213 | 213 | |
@@ -260,13 +260,13 b' def eval_node(node: Union[ast.AST, None], context: EvaluationContext):' | |||
|
260 | 260 | |
|
261 | 261 | Applies evaluation restrictions defined in the context. |
|
262 | 262 | |
|
263 | Currently does not support evaluation of functions with arguments. | |
|
263 | Currently does not support evaluation of functions with keyword arguments. | |
|
264 | 264 | |
|
265 | 265 | Does not evaluate actions which always have side effects: |
|
266 | 266 | - class definitions (``class sth: ...``) |
|
267 | 267 | - function definitions (``def sth: ...``) |
|
268 | 268 | - variable assignments (``x = 1``) |
|
269 |
- aug |
|
|
269 | - augmented assignments (``x += 1``) | |
|
270 | 270 | - deletions (``del x``) |
|
271 | 271 | |
|
272 | 272 | Does not evaluate operations which do not return values: |
@@ -274,7 +274,7 b' def eval_node(node: Union[ast.AST, None], context: EvaluationContext):' | |||
|
274 | 274 | - pass (``pass``) |
|
275 | 275 | - imports (``import x``) |
|
276 | 276 | - control flow |
|
277 |
- conditionals (``if x:``) except for ter |
|
|
277 | - conditionals (``if x:``) except for ternary IfExp (``a if x else b``) | |
|
278 | 278 | - loops (``for`` and `while``) |
|
279 | 279 | - exception handling |
|
280 | 280 | |
@@ -393,7 +393,6 b' def eval_node(node: Union[ast.AST, None], context: EvaluationContext):' | |||
|
393 | 393 | return eval_node(node.orelse, context) |
|
394 | 394 | if isinstance(node, ast.Call): |
|
395 | 395 | func = eval_node(node.func, context) |
|
396 | print(node.keywords) | |
|
397 | 396 | if policy.can_call(func) and not node.keywords: |
|
398 | 397 | args = [eval_node(arg, context) for arg in node.args] |
|
399 | 398 | return func(*args) |
@@ -490,7 +489,7 b' EVALUATION_POLICIES = {' | |||
|
490 | 489 | allowed_calls=set(), |
|
491 | 490 | allow_any_calls=False, |
|
492 | 491 | ), |
|
493 |
"limit |
|
|
492 | "limited": SelectivePolicy( | |
|
494 | 493 | # TODO: |
|
495 | 494 | # - should reject binary and unary operations if custom methods would be dispatched |
|
496 | 495 | allowed_getitem=BUILTIN_GETITEM, |
@@ -1354,7 +1354,7 b' class TestCompleter(unittest.TestCase):' | |||
|
1354 | 1354 | with greedy_completion(): |
|
1355 | 1355 | completes_on_nested() |
|
1356 | 1356 | |
|
1357 |
with evaluation_level("limit |
|
|
1357 | with evaluation_level("limited"): | |
|
1358 | 1358 | completes_on_nested() |
|
1359 | 1359 | |
|
1360 | 1360 | with evaluation_level("minimal"): |
@@ -9,8 +9,8 b' from IPython.testing import decorators as dec' | |||
|
9 | 9 | import pytest |
|
10 | 10 | |
|
11 | 11 | |
|
12 |
def limit |
|
|
13 |
return EvaluationContext(locals_=kwargs, globals_={}, evaluation="limit |
|
|
12 | def limited(**kwargs): | |
|
13 | return EvaluationContext(locals_=kwargs, globals_={}, evaluation="limited") | |
|
14 | 14 | |
|
15 | 15 | |
|
16 | 16 | def unsafe(**kwargs): |
@@ -22,7 +22,7 b' def test_pandas_series_iloc():' | |||
|
22 | 22 | import pandas as pd |
|
23 | 23 | |
|
24 | 24 | series = pd.Series([1], index=["a"]) |
|
25 |
context = limit |
|
|
25 | context = limited(data=series) | |
|
26 | 26 | assert guarded_eval("data.iloc[0]", context) == 1 |
|
27 | 27 | |
|
28 | 28 | |
@@ -30,7 +30,7 b' def test_pandas_series_iloc():' | |||
|
30 | 30 | def test_pandas_series(): |
|
31 | 31 | import pandas as pd |
|
32 | 32 | |
|
33 |
context = limit |
|
|
33 | context = limited(data=pd.Series([1], index=["a"])) | |
|
34 | 34 | assert guarded_eval('data["a"]', context) == 1 |
|
35 | 35 | with pytest.raises(KeyError): |
|
36 | 36 | guarded_eval('data["c"]', context) |
@@ -49,7 +49,7 b' def test_pandas_bad_series():' | |||
|
49 | 49 | return "CUSTOM_ATTR" |
|
50 | 50 | |
|
51 | 51 | bad_series = BadItemSeries([1], index=["a"]) |
|
52 |
context = limit |
|
|
52 | context = limited(data=bad_series) | |
|
53 | 53 | |
|
54 | 54 | with pytest.raises(GuardRejection): |
|
55 | 55 | guarded_eval('data["a"]', context) |
@@ -65,7 +65,7 b' def test_pandas_bad_series():' | |||
|
65 | 65 | assert guarded_eval('data["a"]', context) == "CUSTOM_ITEM" |
|
66 | 66 | |
|
67 | 67 | bad_attr_series = BadAttrSeries([1], index=["a"]) |
|
68 |
context = limit |
|
|
68 | context = limited(data=bad_attr_series) | |
|
69 | 69 | assert guarded_eval('data["a"]', context) == 1 |
|
70 | 70 | with pytest.raises(GuardRejection): |
|
71 | 71 | guarded_eval("data.a", context) |
@@ -77,7 +77,7 b' def test_pandas_dataframe_loc():' | |||
|
77 | 77 | from pandas.testing import assert_series_equal |
|
78 | 78 | |
|
79 | 79 | data = pd.DataFrame([{"a": 1}]) |
|
80 |
context = limit |
|
|
80 | context = limited(data=data) | |
|
81 | 81 | assert_series_equal(guarded_eval('data.loc[:, "a"]', context), data["a"]) |
|
82 | 82 | |
|
83 | 83 | |
@@ -95,16 +95,16 b' def test_named_tuple():' | |||
|
95 | 95 | good = GoodNamedTuple(a="x") |
|
96 | 96 | bad = BadNamedTuple(a="x") |
|
97 | 97 | |
|
98 |
context = limit |
|
|
98 | context = limited(data=good) | |
|
99 | 99 | assert guarded_eval("data[0]", context) == "x" |
|
100 | 100 | |
|
101 |
context = limit |
|
|
101 | context = limited(data=bad) | |
|
102 | 102 | with pytest.raises(GuardRejection): |
|
103 | 103 | guarded_eval("data[0]", context) |
|
104 | 104 | |
|
105 | 105 | |
|
106 | 106 | def test_dict(): |
|
107 |
context = limit |
|
|
107 | context = limited(data={"a": 1, "b": {"x": 2}, ("x", "y"): 3}) | |
|
108 | 108 | assert guarded_eval('data["a"]', context) == 1 |
|
109 | 109 | assert guarded_eval('data["b"]', context) == {"x": 2} |
|
110 | 110 | assert guarded_eval('data["b"]["x"]', context) == 2 |
@@ -114,43 +114,43 b' def test_dict():' | |||
|
114 | 114 | |
|
115 | 115 | |
|
116 | 116 | def test_set(): |
|
117 |
context = limit |
|
|
117 | context = limited(data={"a", "b"}) | |
|
118 | 118 | assert guarded_eval("data.difference", context) |
|
119 | 119 | |
|
120 | 120 | |
|
121 | 121 | def test_list(): |
|
122 |
context = limit |
|
|
122 | context = limited(data=[1, 2, 3]) | |
|
123 | 123 | assert guarded_eval("data[1]", context) == 2 |
|
124 | 124 | assert guarded_eval("data.copy", context) |
|
125 | 125 | |
|
126 | 126 | |
|
127 | 127 | def test_dict_literal(): |
|
128 |
context = limit |
|
|
128 | context = limited() | |
|
129 | 129 | assert guarded_eval("{}", context) == {} |
|
130 | 130 | assert guarded_eval('{"a": 1}', context) == {"a": 1} |
|
131 | 131 | |
|
132 | 132 | |
|
133 | 133 | def test_list_literal(): |
|
134 |
context = limit |
|
|
134 | context = limited() | |
|
135 | 135 | assert guarded_eval("[]", context) == [] |
|
136 | 136 | assert guarded_eval('[1, "a"]', context) == [1, "a"] |
|
137 | 137 | |
|
138 | 138 | |
|
139 | 139 | def test_set_literal(): |
|
140 |
context = limit |
|
|
140 | context = limited() | |
|
141 | 141 | assert guarded_eval("set()", context) == set() |
|
142 | 142 | assert guarded_eval('{"a"}', context) == {"a"} |
|
143 | 143 | |
|
144 | 144 | |
|
145 | 145 | def test_if_expression(): |
|
146 |
context = limit |
|
|
146 | context = limited() | |
|
147 | 147 | assert guarded_eval("2 if True else 3", context) == 2 |
|
148 | 148 | assert guarded_eval("4 if False else 5", context) == 5 |
|
149 | 149 | |
|
150 | 150 | |
|
151 | 151 | def test_object(): |
|
152 | 152 | obj = object() |
|
153 |
context = limit |
|
|
153 | context = limited(obj=obj) | |
|
154 | 154 | assert guarded_eval("obj.__dir__", context) == obj.__dir__ |
|
155 | 155 | |
|
156 | 156 | |
@@ -163,11 +163,11 b' def test_object():' | |||
|
163 | 163 | ], |
|
164 | 164 | ) |
|
165 | 165 | def test_number_attributes(code, expected): |
|
166 |
assert guarded_eval(code, limit |
|
|
166 | assert guarded_eval(code, limited()) == expected | |
|
167 | 167 | |
|
168 | 168 | |
|
169 | 169 | def test_method_descriptor(): |
|
170 |
context = limit |
|
|
170 | context = limited() | |
|
171 | 171 | assert guarded_eval("list.copy.__name__", context) == "copy" |
|
172 | 172 | |
|
173 | 173 | |
@@ -179,7 +179,7 b' def test_method_descriptor():' | |||
|
179 | 179 | ], |
|
180 | 180 | ) |
|
181 | 181 | def test_calls(data, good, bad, expected): |
|
182 |
context = limit |
|
|
182 | context = limited(data=data) | |
|
183 | 183 | assert guarded_eval(good, context) == expected |
|
184 | 184 | |
|
185 | 185 | with pytest.raises(GuardRejection): |
@@ -195,13 +195,13 b' def test_calls(data, good, bad, expected):' | |||
|
195 | 195 | ], |
|
196 | 196 | ) |
|
197 | 197 | def test_literals(code, expected): |
|
198 |
context = limit |
|
|
198 | context = limited() | |
|
199 | 199 | assert guarded_eval(code, context) == expected |
|
200 | 200 | |
|
201 | 201 | |
|
202 | 202 | def test_subscript(): |
|
203 | 203 | context = EvaluationContext( |
|
204 |
locals_={}, globals_={}, evaluation="limit |
|
|
204 | locals_={}, globals_={}, evaluation="limited", in_subscript=True | |
|
205 | 205 | ) |
|
206 | 206 | empty_slice = slice(None, None, None) |
|
207 | 207 | assert guarded_eval("", context) == tuple() |
General Comments 0
You need to be logged in to leave comments.
Login now