this repo has no description
1#!/usr/bin/env python3
2# Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
3import sys
4import unittest
5from types import MethodType, ModuleType, SimpleNamespace
6from unittest.mock import Mock
7
8
9class IntepreterTest(unittest.TestCase):
10 def test_binary_mul_smallint_returns_large_int(self):
11 def mul(a, b):
12 return a * b
13
14 # Rewrite mul() to use BINARY_MUL_SMALLINT.
15 self.assertEqual(mul(5, 6), 30)
16
17 small_int = 1 << 62
18 self.assertEqual(mul(small_int, small_int), small_int * small_int)
19
20 def test_binary_mul_smallint_with_operand_zero_returns_zero(self):
21 def mul(a, b):
22 return a * b
23
24 # Rewrite mul() to use BINARY_MUL_SMALLINT.
25 self.assertEqual(mul(5, 6), 30)
26
27 small_int = 1 << 62
28 self.assertEqual(mul(small_int, 0), 0)
29 self.assertEqual(mul(0, small_int), 0)
30
31 def test_binary_subscr_calls_type_dunder_class_getitem(self):
32 class C:
33 def __class_getitem__(cls, item):
34 return f"C:{cls.__name__}[{item.__name__}]"
35
36 self.assertEqual(C[int], "C:C[int]")
37
38 def test_binary_subscr_ignores_instance_dunder_class_getitem(self):
39 class C:
40 def __class_getitem__(cls, item):
41 return f"C:{cls.__name__}[{item.__name__}]"
42
43 with self.assertRaises(TypeError) as context:
44 C()[int]
45 self.assertEqual(str(context.exception), "'C' object is not subscriptable")
46
47 def test_binary_subscr_prioritizes_metaclass_dunder_getitem(self):
48 class M(type):
49 def __getitem__(cls, item):
50 return f"M:{cls.__name__}[{item.__name__}]"
51
52 class C(metaclass=M):
53 def __class_getitem__(cls, item):
54 return f"C:{cls.__name__}[{item.__name__}]"
55
56 self.assertEqual(C[int], "M:C[int]")
57
58 def test_build_map_returns_dict(self):
59 def foo(k0, k1, k2):
60 return {k0: 0, k1: 42.4, k2: "test"}
61
62 d = foo("key0", "aaa", "zzz")
63 self.assertIs(type(d), dict)
64 self.assertEqual(list(d.keys()), ["key0", "aaa", "zzz"])
65 self.assertEqual(list(d.values()), [0, 42.4, "test"])
66
67 def test_compare_op_in_propagetes_exception(self):
68 class C:
69 def __contains__(self, value):
70 raise UserWarning("C.__contains__")
71
72 c = C()
73 with self.assertRaises(UserWarning) as context:
74 1 in c
75 self.assertEqual(str(context.exception), "C.__contains__")
76
77 def test_compare_op_not_in_propagetes_exception(self):
78 class C:
79 def __contains__(self, value):
80 raise UserWarning("C.__contains__")
81
82 c = C()
83 with self.assertRaises(UserWarning) as context:
84 1 not in c
85 self.assertEqual(str(context.exception), "C.__contains__")
86
87 def test_for_iter_iterates_dict_by_insertion_order(self):
88 d = {}
89 d["a"] = 1
90 d["c"] = 3
91 d["b"] = 2
92 result = []
93 for key in d:
94 result.append(key)
95 self.assertEqual(result, ["a", "c", "b"])
96
97 def test_store_name_calls_dunder_setitem(self):
98 class C(dict):
99 def __setitem__(self, key, value):
100 self.result = (key, value)
101
102 locals = C()
103 exec("x = 44", None, locals)
104 self.assertEqual(locals.result[0], "x")
105 self.assertEqual(locals.result[1], 44)
106
107 def test_store_name_setitem_propagates_exception(self):
108 class C:
109 def __setitem__(self, key, value):
110 raise UserWarning("bar")
111
112 def __getitem__(self, key):
113 ... # just here so this type is considered a mapping.
114
115 locals = C()
116 with self.assertRaises(UserWarning) as context:
117 exec("x = 1", None, locals)
118 self.assertEqual(str(context.exception), "bar")
119
120 def test_store_name_setitem_propagates_descriptor_exception(self):
121 class D:
122 def __get__(self, instance, owner):
123 self.result = (instance, owner)
124 raise UserWarning("baz")
125
126 d = D()
127
128 class C:
129 __setitem__ = d
130
131 def __getitem__(self, key):
132 ... # just here so this type is considered a mapping.
133
134 locals = C()
135 with self.assertRaises(UserWarning) as context:
136 exec("x = 1", None, locals)
137 self.assertEqual(str(context.exception), "baz")
138 self.assertEqual(d.result[0], locals)
139 self.assertEqual(d.result[1], C)
140
141 def test_load_attr_calls_dunder_get(self):
142 class Descriptor:
143 def __get__(self, *args):
144 self.args = args
145 return 42
146
147 descriptor = Descriptor()
148
149 class C:
150 foo = descriptor
151
152 self.assertEqual(C.foo, 42)
153 self.assertEqual(descriptor.args, (None, C))
154 c = C()
155 self.assertEqual(c.foo, 42)
156 self.assertEqual(descriptor.args, (c, C))
157
158 def test_load_attr_calls_callable_dunder_get(self):
159 class Callable:
160 def __call__(self, *args):
161 self.args = args
162 return 42
163
164 callable = Callable()
165
166 class Descriptor:
167 __get__ = callable
168
169 descriptor = Descriptor()
170
171 class C:
172 foo = descriptor
173
174 self.assertEqual(C.foo, 42)
175 self.assertEqual(callable.args, (descriptor, None, C))
176 c = C()
177 self.assertEqual(c.foo, 42)
178 self.assertEqual(callable.args, (descriptor, c, C))
179
180 def test_load_attr_does_not_recursively_call_dunder_get(self):
181 class Descriptor0:
182 def __get__(self, *args):
183 return 13
184
185 def __call__(self, *args):
186 return 42
187
188 descriptor0 = Descriptor0()
189
190 class Descriptor1:
191 __get__ = descriptor0
192
193 descriptor1 = Descriptor1()
194
195 class C:
196 foo = descriptor1
197
198 self.assertEqual(C.foo, 42)
199 self.assertEqual(C().foo, 42)
200 self.assertIs(C.__dict__["foo"].__get__, 13)
201
202 def test_load_attr_with_module_prioritizes_data_descr_than_instance_attr(self):
203 from types import ModuleType
204
205 class MyModule(ModuleType):
206 @property
207 def foo(self):
208 return "data descriptor"
209
210 m = MyModule("m")
211 m.__dict__["foo"] = "instance attribute"
212
213 self.assertEqual(m.foo, "data descriptor")
214
215 def test_load_attr_with_module_prioritizes_instance_attr_than_non_data_descr(self):
216 from types import ModuleType
217
218 class NonDataDescriptor:
219 def __get__(self, obj, obj_type):
220 return "non-data descriptor"
221
222 class MyModule(ModuleType):
223 foo = NonDataDescriptor()
224
225 m = MyModule("m")
226 m.foo = "instance attribute"
227
228 self.assertEqual(m.foo, "instance attribute")
229
230 def test_load_attr_with_module_prioritizes_non_data_descr_than_instance_getattr(
231 self,
232 ):
233 from types import ModuleType
234
235 class NonDataDescriptor:
236 def __get__(self, obj, obj_type):
237 return "non-data descriptor"
238
239 class MyModule(ModuleType):
240 foo = NonDataDescriptor()
241
242 m = MyModule("m")
243 exec('def __getattr__(name): return name + " instance.__getattr__"', m.__dict__)
244 self.assertEqual(m.foo, "non-data descriptor")
245
246 def test_load_attr_with_module_prioritizes_instance_getattr_than_type_getattr(self):
247 from types import ModuleType
248
249 class MyModule(ModuleType):
250 def __getattr__(self, name):
251 return name + " type.__getattr__"
252
253 m = MyModule("m")
254 exec('def __getattr__(name): return name + " instance.__getattr__"', m.__dict__)
255 self.assertEqual(m.foo, "foo instance.__getattr__")
256
257 def test_load_attr_with_module_returns_type_getattr(self):
258 from types import ModuleType
259
260 class MyModule(ModuleType):
261 def __getattr__(self, name):
262 return name + " type.__getattr__"
263
264 m = MyModule("m")
265 self.assertEqual(m.foo, "foo type.__getattr__")
266
267 def test_load_attr_with_module_returns_type_getattr(self):
268 from types import ModuleType
269
270 class MyModule(ModuleType):
271 pass
272
273 m = MyModule("m")
274 with self.assertRaises(AttributeError):
275 m.foo
276
277 def test_load_deref_with_assigned_cell_returns_assigned_value(self):
278 def foo(x):
279 def bar():
280 return x
281
282 return bar
283
284 bar = foo(10)
285 self.assertEqual(bar(), 10)
286
287 def test_load_deref_with_delete_cell_raises_name_error(self):
288 def foo(x):
289 def bar():
290 return x # noqa: F821
291
292 del x
293 return bar
294
295 bar = foo(10)
296 with self.assertRaises(NameError):
297 bar()
298
299 def test_load_name_calls_dunder_getitem(self):
300 class C:
301 def __getitem__(self, key):
302 return (key, 17)
303
304 globals = {"foo": None}
305 locals = C()
306 exec('assert(foo == ("foo", 17))', globals, locals)
307
308 def test_load_name_continues_on_keyerror(self):
309 class C:
310 def __getitem__(self, key):
311 self.result = key
312 raise KeyError()
313
314 globals = {}
315 exec("foo = 99", globals)
316 self.assertIn("foo", globals)
317
318 locals = C()
319 exec("assert(foo == 99)", globals, locals) # should not raise
320 self.assertEqual(locals.result, "foo")
321
322 def test_load_name_getitem_propagates_exception(self):
323 class C:
324 def __getitem__(self, key):
325 raise UserWarning("bar")
326
327 globals = None
328 locals = C()
329 with self.assertRaises(UserWarning) as context:
330 exec("foo", globals, locals)
331 self.assertEqual(str(context.exception), "bar")
332
333 def test_load_name_getitem_propagates_descriptor_exception(self):
334 class D:
335 def __get__(self, instance, owner):
336 self.result = (instance, owner)
337 raise UserWarning("baz")
338
339 d = D()
340
341 class C:
342 __getitem__ = d
343
344 globals = None
345 locals = C()
346 with self.assertRaises(UserWarning) as context:
347 exec("foo", globals, locals)
348 self.assertEqual(str(context.exception), "baz")
349 self.assertEqual(d.result[0], locals)
350 self.assertEqual(d.result[1], C)
351
352 def test_load_global_returns_global(self):
353 globals = {
354 "foo": 42,
355 "__builtins__": {"foo": 8},
356 }
357 locals = {"foo": 3}
358 self.assertIs(
359 exec("def get_foo(): global foo; return foo", globals, locals), None
360 )
361 self.assertEqual(locals["get_foo"](), 42)
362
363 def test_load_global_returns_builtin(self):
364 globals = {
365 "foo": 42,
366 "__builtins__": {"bar": 8},
367 }
368 locals = {"bar": 3}
369 self.assertIs(
370 exec("def get_bar(): global bar; return bar", globals, locals), None
371 )
372 self.assertEqual(locals["get_bar"](), 8)
373
374 def test_load_global_does_not_cache_builtin(self):
375 builtins = {"bar": -8}
376 globals = {"foo": 42, "__builtins__": builtins}
377 locals = {"bar": 3}
378 self.assertIs(
379 exec("def get_bar(): global bar; return bar", globals, locals), None
380 )
381 self.assertEqual(locals["get_bar"](), -8)
382 builtins["bar"] = 9
383 self.assertEqual(locals["get_bar"](), 9)
384
385 def test_load_global_calls_builtins_getitem(self):
386 class C:
387 def __getitem__(self, key):
388 return (self, key)
389
390 builtins = C()
391 globals = {"foo": 42, "__builtins__": builtins}
392 locals = {"bar": 3}
393 self.assertIs(
394 exec("def get_bar(): global bar; return bar", globals, locals), None
395 )
396 self.assertEqual(locals["get_bar"](), (builtins, "bar"))
397
398 def test_delete_attr_calls_dunder_delete(self):
399 class Descriptor:
400 def __delete__(self, *args):
401 self.args = args
402 return "return value ignored"
403
404 descriptor = Descriptor()
405
406 class C:
407 foo = descriptor
408
409 c = C()
410 del c.foo
411 self.assertEqual(descriptor.args, (c,))
412 # Deletion on class should just delete descriptor
413 del C.foo
414 self.assertFalse(hasattr(C, "foo"))
415 self.assertNotIn("foo", C.__dict__)
416
417 def test_delete_attr_calls_callable_dunder_delete(self):
418 class Callable:
419 def __call__(self, *args):
420 self.args = args
421 return "return value ignored"
422
423 callable = Callable()
424
425 class Descriptor:
426 __delete__ = callable
427
428 descriptor = Descriptor()
429
430 class C:
431 foo = descriptor
432
433 c = C()
434 del c.foo
435 self.assertEqual(callable.args, (c,))
436 # Deletion on class should just delete descriptor
437 del C.foo
438 self.assertFalse(hasattr(C, "foo"))
439 self.assertNotIn("foo", C.__dict__)
440
441 def test_delete_attr_call_dunder_delete_descriptor(self):
442 class Callable:
443 def __call__(self, *args):
444 self.args = args
445 return "return value ignored"
446
447 callable = Callable()
448
449 class Descriptor0:
450 def __get__(self, *args):
451 self.args = args
452 return callable
453
454 descriptor0 = Descriptor0()
455
456 class Descriptor1:
457 __delete__ = descriptor0
458
459 descriptor1 = Descriptor1()
460
461 class C:
462 foo = descriptor1
463
464 c = C()
465 del c.foo
466 self.assertEqual(descriptor0.args, (descriptor1, Descriptor1))
467 self.assertEqual(callable.args, (c,))
468
469 def test_delete_fast_with_assigned_name_succeeds(self):
470 def foo():
471 a = 4
472 del a
473
474 foo()
475
476 def test_delete_fast_with_deleted_name_raises_unbound_local_error(self):
477 def foo():
478 a = 4
479 del a
480 del a # noqa: F821
481
482 with self.assertRaises(UnboundLocalError) as context:
483 foo()
484 self.assertEqual(
485 str(context.exception), "local variable 'a' referenced before assignment"
486 )
487
488 def test_delete_name_calls_dunder_delitem(self):
489 class C(dict):
490 def __delitem__(self, key):
491 self.result = key
492 return None
493
494 locals = C()
495 exec("del foo", None, locals)
496 self.assertEqual(locals.result, "foo")
497
498 def test_delete_name_raises_name_error_on_error(self):
499 class C(dict):
500 def __delitem__(self, key):
501 self.result = key
502 raise UserWarning("bar")
503
504 globals = {}
505 locals = C()
506 with self.assertRaises(NameError) as context:
507 exec("del foo", globals, locals)
508 self.assertEqual(str(context.exception), "name 'foo' is not defined")
509 self.assertEqual(locals.result, "foo")
510
511 def test_delete_name_raises_name_error_on_descriptor_error(self):
512 class D:
513 def __get__(self, instance, owner):
514 self.result = (instance, owner)
515 raise UserWarning("baz")
516
517 d = D()
518
519 class C(dict):
520 __delitem__ = d
521
522 globals = {}
523 locals = C()
524 with self.assertRaises(NameError) as context:
525 exec("del foo", globals, locals)
526 self.assertEqual(str(context.exception), "name 'foo' is not defined")
527 self.assertEqual(d.result[0], locals)
528 self.assertEqual(d.result[1], C)
529
530 def test_get_iter_with_range_with_bool(self):
531 result = None
532 for i in range(True):
533 if result is None:
534 result = i
535 else:
536 result += i
537 self.assertEqual(result, 0)
538
539 result = 0
540 for i in range(True, 4):
541 result += i
542 self.assertEqual(result, 6)
543
544 result = 0
545 for i in range(0, 4, True):
546 result += i
547 self.assertEqual(result, 6)
548
549 def test_import_performs_secondary_lookup(self):
550 import sys
551
552 class FakeModule:
553 def __getattribute__(self, name):
554 if name == "__name__":
555 return "test_import_performs_secondary_lookup_fake_too"
556 raise AttributeError(name)
557
558 sys.modules["test_import_performs_secondary_lookup_fake"] = FakeModule()
559 sys.modules["test_import_performs_secondary_lookup_fake_too.foo"] = 42
560 try:
561 from test_import_performs_secondary_lookup_fake import foo
562 finally:
563 del sys.modules["test_import_performs_secondary_lookup_fake"]
564 del sys.modules["test_import_performs_secondary_lookup_fake_too.foo"]
565 self.assertEqual(foo, 42)
566
567 def test_call_ex_kw_with_class_with_iterable_raises_type_error(self):
568 def foo(a):
569 return a
570
571 mapping = [("hello", "world")]
572 with self.assertRaisesRegex(TypeError, "must be a mapping, not list"):
573 foo(**mapping)
574
575 def test_call_ex_kw_with_class_without_keys_method_raises_type_error(self):
576 class C:
577 __getitem__ = Mock(name="__getitem__", return_value="mock")
578
579 def foo(a):
580 return a
581
582 mapping = C()
583 with self.assertRaisesRegex(TypeError, "must be a mapping, not C"):
584 foo(**mapping)
585
586 def test_call_ex_kw_with_dict_subclass_does_not_call_keys_or_dunder_getitem(self):
587 class C(dict):
588 __getitem__ = Mock(name="__getitem__", return_value="mock")
589 keys = Mock(name="keys", return_value=("a",))
590
591 def foo(a):
592 return a
593
594 mapping = C({"a": "foo"})
595 result = foo(**mapping)
596 self.assertEqual(result, "foo")
597 C.keys.assert_not_called()
598 C.__getitem__.assert_not_called()
599
600 def test_call_ex_kw_with_non_dict_calls_keys_and_dunder_getitem(self):
601 class C:
602 __getitem__ = Mock(name="__getitem__", return_value="mock")
603 keys = Mock(name="keys", return_value=("a",))
604
605 def foo(a):
606 return a
607
608 mapping = C()
609 result = foo(**mapping)
610 self.assertEqual(result, "mock")
611 C.keys.assert_called_once()
612 C.__getitem__.assert_called_once()
613
614 def test_call_ex_kw_with_non_dict_passes_dict_as_kwargs(self):
615 class C:
616 __getitem__ = Mock(name="__getitem__", return_value="mock")
617 keys = Mock(name="keys", return_value=("hello", "world"))
618
619 def foo(**kwargs):
620 self.assertIs(type(kwargs), dict)
621 self.assertIn("hello", kwargs)
622 self.assertIn("world", kwargs)
623
624 mapping = C()
625 foo(**mapping)
626 C.keys.assert_called_once()
627 self.assertEqual(C.__getitem__.call_count, 2)
628
629 def test_callfunction_kw_does_not_modify_actual_names(self):
630 def foo(a, b):
631 return (a, b)
632
633 def bar():
634 return foo(b="b", a="a")
635
636 # Call the same function twice to see the actual names are preserved.
637 self.assertEqual(bar(), ("a", "b"))
638 self.assertEqual(bar(), ("a", "b"))
639
640 def test_call_raises_recursion_error(self):
641 def foo(x):
642 return foo(x)
643
644 with self.assertRaisesRegex(RecursionError, "maximum recursion depth exceeded"):
645 foo(1)
646
647 def test_cache_misses_after_dunder_class_update(self):
648 class C:
649 def foo(self):
650 return 100
651
652 class D:
653 def foo(self):
654 return 200
655
656 def cache_attribute(c):
657 return c.foo()
658
659 c = C()
660 # Load the cache
661 result = cache_attribute(c)
662 self.assertIs(result, 100)
663
664 c.__class__ = D
665 # The loaded cache doesn't match `c` since its layout id has changed.
666 result = cache_attribute(c)
667 self.assertIs(result, 200)
668
669 def test_unpack_sequence_raises_type_error(self):
670 class Foo:
671 pass
672
673 with self.assertRaisesRegex(TypeError, "cannot unpack non-iterable Foo object"):
674 a, b = Foo()
675
676
677class InlineCacheTests(unittest.TestCase):
678 def test_load_slot_descriptor(self):
679 class C:
680 __slots__ = "x"
681
682 def read_x(c):
683 return c.x
684
685 c = C()
686 c.x = 50
687
688 # Cache C.x.
689 self.assertEqual(read_x(c), 50)
690 # Use the cached descriptor.
691 self.assertEqual(read_x(c), 50)
692
693 # The cached descriptor raises AttributeError after its corresponding
694 # attribute gets deleted.
695 del c.x
696 with self.assertRaises(AttributeError):
697 read_x(c)
698
699 def test_load_slot_descriptor_invalidated_by_updating_type_attr(self):
700 class C:
701 __slots__ = "x"
702
703 def read_x(c):
704 return c.x
705
706 c = C()
707 c.x = 50
708
709 # Cache C.x.
710 self.assertEqual(read_x(c), 50)
711
712 # Invalidate the cache.
713 C.x = property(lambda self: 100)
714
715 # Verify that the updated type attribute is used for `c.x`.
716 self.assertEqual(read_x(c), 100)
717
718 def test_store_attr_calls_dunder_set(self):
719 class Descriptor:
720 def __set__(self, *args):
721 self.args = args
722 return "return value ignored"
723
724 descriptor = Descriptor()
725
726 class C:
727 foo = descriptor
728
729 c = C()
730 c.foo = 42
731 self.assertEqual(descriptor.args, (c, 42))
732 # Assignment on the class should replace descriptor.
733 C.foo = 13
734 self.assertEqual(C.foo, 13)
735 self.assertEqual(C.__dict__["foo"], 13)
736
737 def test_store_attr_calls_callable_dunder_set(self):
738 class Callable:
739 def __call__(self, *args):
740 self.args = args
741 return "return value ignored"
742
743 callable = Callable()
744
745 class Descriptor:
746 __set__ = callable
747
748 descriptor = Descriptor()
749
750 class C:
751 foo = descriptor
752
753 c = C()
754 c.foo = 42
755 self.assertEqual(callable.args, (c, 42))
756 # Assignment on the class should replace descriptor.
757 C.foo = 13
758 self.assertEqual(C.foo, 13)
759 self.assertEqual(C.__dict__["foo"], 13)
760
761 def test_store_attr_call_dunder_set_descriptor(self):
762 class Callable:
763 def __call__(self, *args):
764 self.args = args
765 return "return value ignored"
766
767 callable = Callable()
768
769 class Descriptor0:
770 def __get__(self, *args):
771 self.args = args
772 return callable
773
774 descriptor0 = Descriptor0()
775
776 class Descriptor1:
777 __set__ = descriptor0
778
779 descriptor1 = Descriptor1()
780
781 class C:
782 foo = descriptor1
783
784 c = C()
785 c.foo = 42
786 self.assertEqual(descriptor0.args, (descriptor1, Descriptor1))
787 self.assertEqual(callable.args, (c, 42))
788
789 def test_store_slot_descriptor(self):
790 class C:
791 __slots__ = "x"
792
793 def write_x(c, v):
794 c.x = v
795
796 c = C()
797
798 # Cache C.x.
799 write_x(c, 1)
800 self.assertEqual(c.x, 1)
801
802 # Use the cached descriptor.
803 write_x(c, 2)
804 self.assertEqual(c.x, 2)
805
806 def test_store_slot_descriptor_invalidated_by_updating_type_attr(self):
807 class C:
808 __slots__ = "x"
809
810 def write_x(c, v):
811 c.x = v
812
813 c = C()
814
815 # Cache C.x.
816 write_x(c, 1)
817 self.assertEqual(c.x, 1)
818
819 # Invalidate the cache.
820 def setter(self, value):
821 value["setter_executed"] = True
822
823 C.x = property(lambda self: 100, setter)
824
825 # Verify that the updated type attribute is used for `c.x`.
826 v = {"setter_executed": False}
827 write_x(c, v)
828 self.assertTrue(v["setter_executed"], True)
829
830 def test_raise_oserror_with_errno_and_strerror(self):
831 with self.assertRaises(FileNotFoundError) as context:
832 open("/tmp/nonexisting_file")
833 exc = context.exception
834 self.assertEqual(exc.errno, 2)
835 self.assertEqual(exc.strerror, "No such file or directory")
836 # TODO(T67323177): Check filename.
837
838 def test_call_with_bound_method_storing_arbitrary_callable_resolves_callable(self):
839 class C:
840 def __call__(self, arg):
841 return self, arg
842
843 func = C()
844 instance = object()
845 method = MethodType(func, instance)
846 self.assertEqual((func, instance), method())
847
848
849class ImportNameTests(unittest.TestCase):
850 def test_import_name_calls_dunder_builtins_dunder_import(self):
851 def my_import(name, globals, locals, fromlist, level):
852 return SimpleNamespace(baz=42, bam=(name, fromlist, level))
853
854 builtins = ModuleType("builtins")
855 builtins.__import__ = my_import
856 my_globals = {"__builtins__": builtins}
857
858 exec("from ..foo.bar import baz, bam", my_globals)
859 self.assertEqual(my_globals["baz"], 42)
860 self.assertEqual(my_globals["bam"], ("foo.bar", ("baz", "bam"), 2))
861
862 def test_import_name_without_dunder_import_raises_import_error(self):
863 my_builtins = ModuleType("my_builtins")
864 my_globals = {"__builtins__": my_builtins}
865 with self.assertRaisesRegex(ImportError, "__import__ not found"):
866 exec("import foo", my_globals)
867
868 def test_import_from_module_with_raising_dunder_getattr_propagates_exception(self):
869 import collections
870
871 self.assertIn("__getattr__", collections.__dict__)
872 with self.assertRaises(ImportError):
873 from collections import definitely_does_not_exist
874
875
876class ImportStarTests(unittest.TestCase):
877 def test_import_star_imports_symbols(self):
878 def my_import(name, globals, locals, fromlist, level):
879 result = ModuleType("m")
880 result.foo = 1
881 result._bar = 2
882 result.baz_ = 3
883 result.__bam__ = 4
884 return result
885
886 builtins = ModuleType("builtins")
887 builtins.__import__ = my_import
888 my_globals = {"__builtins__": builtins}
889
890 exec("from m import *", my_globals)
891 self.assertEqual(my_globals["foo"], 1)
892 self.assertEqual(my_globals["baz_"], 3)
893 self.assertNotIn("_bar", my_globals)
894 self.assertNotIn("__bam__", my_globals)
895
896 def test_import_star_with_dunder_all_tuple_imports_symbols(self):
897 def my_import(name, globals, locals, fromlist, level):
898 result = ModuleType("m")
899 result.foo = 1
900 result._bar = 2
901 result.baz = 99
902 result.__all__ = ("foo", "_bar", "__repr__")
903 return result
904
905 builtins = ModuleType("builtins")
906 builtins.__import__ = my_import
907 my_globals = {"__builtins__": builtins}
908
909 exec("from m import *", my_globals)
910 self.assertEqual(my_globals["foo"], 1)
911 self.assertEqual(my_globals["_bar"], 2)
912 self.assertEqual(my_globals["__repr__"](), "<module 'm'>")
913 self.assertNotIn("baz", my_globals)
914 self.assertNotIn("__all__", my_globals)
915
916 def test_import_star_with_dunder_all_list_imports_symbols(self):
917 def my_import(name, globals, locals, fromlist, level):
918 result = ModuleType("m")
919 result.foo = 1
920 result._bar = 2
921 result.baz = 99
922 result.__all__ = ["foo", "_bar", "__all__"]
923 return result
924
925 builtins = ModuleType("builtins")
926 builtins.__import__ = my_import
927 my_globals = {"__builtins__": builtins}
928
929 exec("from m import *", my_globals)
930 self.assertEqual(my_globals["foo"], 1)
931 self.assertEqual(my_globals["_bar"], 2)
932 self.assertEqual(my_globals["__all__"], ["foo", "_bar", "__all__"])
933 self.assertNotIn("baz", my_globals)
934
935 def test_import_star_with_dunder_all_sequence_imports_symbols(self):
936 def my_import(name, globals, locals, fromlist, level):
937 class C:
938 def __getitem__(self, key):
939 if key == 0:
940 return "foo"
941 if key == 1:
942 return "_bar"
943 raise IndexError
944
945 result = ModuleType("m")
946 result.foo = 1
947 result._bar = 2
948 result.baz = 99
949 result.__all__ = C()
950 return result
951
952 builtins = ModuleType("builtins")
953 builtins.__import__ = my_import
954 my_globals = {"__builtins__": builtins}
955
956 exec("from m import *", my_globals)
957 self.assertEqual(my_globals["foo"], 1)
958 self.assertEqual(my_globals["_bar"], 2)
959 self.assertNotIn("baz", my_globals)
960
961 def test_import_star_calls_object_dunder_dict_keys(self):
962 def my_import(name, globals, locals, fromlist, level):
963 class C:
964 def keys(self):
965 return ["foo", "_bar", "baz_"]
966
967 class M:
968 __dict__ = C()
969
970 def __getattr__(self, key):
971 if key == "__all__":
972 raise AttributeError
973 return f"value for {key}"
974
975 return M()
976
977 builtins = ModuleType("builtins")
978 builtins.__import__ = my_import
979 my_globals = {"__builtins__": builtins}
980
981 exec("from m import *", my_globals)
982 self.assertEqual(my_globals["foo"], "value for foo")
983 self.assertEqual(my_globals["baz_"], "value for baz_")
984 self.assertNotIn("_bar", my_globals)
985
986 def test_import_star_calls_implicit_globals_dunder_setitem(self):
987 def my_import(name, globals, locals, fromlist, level):
988 result = ModuleType("m")
989 result.foo = 88
990 result.bar = 77
991 return result
992
993 class G(dict):
994 def __init__(self):
995 self.setitem_keys = []
996
997 def __setitem__(self, key, value):
998 dict.__setitem__(self, key, value)
999 self.setitem_keys.append(key)
1000
1001 builtins = ModuleType("builtins")
1002 builtins.__import__ = my_import
1003 my_globals = {"__builtins__": builtins}
1004 my_locals = G()
1005
1006 exec("from m import *", my_globals, my_locals)
1007 self.assertEqual(my_locals["foo"], 88)
1008 self.assertEqual(my_locals["bar"], 77)
1009 self.assertEqual(sorted(my_locals.setitem_keys), ["bar", "foo"])
1010 self.assertNotIn("foo", my_globals)
1011 self.assertNotIn("bar", my_globals)
1012
1013 def test_import_star_with_object_without_dunder_dict_raises_import_errro(self):
1014 def my_import(name, globals, locals, fromlist, level):
1015 class C:
1016 def __getattribute__(self, key):
1017 if key == "__dict__":
1018 raise AttributeError
1019 return object.__getattribute__(self, key)
1020
1021 return C()
1022
1023 builtins = ModuleType("builtins")
1024 builtins.__import__ = my_import
1025 my_globals = {"__builtins__": builtins}
1026
1027 with self.assertRaisesRegex(
1028 ImportError, r"from-import-\* object has no __dict__ and no __all__"
1029 ):
1030 exec("from m import *", my_globals)
1031
1032
1033if __name__ == "__main__":
1034 unittest.main()