|
6 | 6 | import _imp |
7 | 7 | import dis |
8 | 8 | import gc |
| 9 | +import importlib |
9 | 10 | import io |
10 | 11 | import os |
11 | 12 | import pathlib |
|
15 | 16 | import textwrap |
16 | 17 | import unittest |
17 | 18 | from collections.abc import Callable, Generator, Sequence |
18 | | - |
19 | 19 | from contextlib import contextmanager |
20 | 20 | from importlib.abc import Loader |
21 | 21 | from importlib.machinery import SOURCE_SUFFIXES, SourceFileLoader |
|
37 | 37 | StrictSourceFileLoader, |
38 | 38 | ) |
39 | 39 | from cinderx.compiler.strict.runtime import set_freeze_enabled |
40 | | - |
| 40 | +from cinderx.static import StaticTypeError |
41 | 41 | from cinderx.test_support import passIf |
42 | 42 |
|
43 | 43 | from . import sandbox as base_sandbox |
@@ -179,9 +179,11 @@ class Sandbox(base_sandbox.Sandbox): |
179 | 179 | def begin_loader( |
180 | 180 | self, loader: tuple[Callable[[str, str], Loader], list[str]] |
181 | 181 | ) -> Generator[None, None, None]: |
182 | | - with callable_file_loader( |
183 | | - loader |
184 | | - ), restore_sys_modules(), restore_strict_modules(): |
| 182 | + with ( |
| 183 | + callable_file_loader(loader), |
| 184 | + restore_sys_modules(), |
| 185 | + restore_strict_modules(), |
| 186 | + ): |
185 | 187 | yield |
186 | 188 |
|
187 | 189 | def strict_import(self, *module_names: str) -> TModule | list[TModule]: |
@@ -222,9 +224,11 @@ def import_modules(self, *module_names: str) -> TModule | list[TModule]: |
222 | 224 |
|
223 | 225 | @contextmanager |
224 | 226 | def isolated_strict_loader(self) -> Generator[None, None, None]: |
225 | | - with callable_file_loader( |
226 | | - STRICT_LOADER |
227 | | - ), restore_sys_modules(), restore_strict_modules(): |
| 227 | + with ( |
| 228 | + callable_file_loader(STRICT_LOADER), |
| 229 | + restore_sys_modules(), |
| 230 | + restore_strict_modules(), |
| 231 | + ): |
228 | 232 | yield |
229 | 233 |
|
230 | 234 | @contextmanager |
@@ -329,6 +333,49 @@ def g() -> int: |
329 | 333 | self.assertInBytecode(a.f, "INVOKE_FUNCTION", ((("a",), "g"), 0)) |
330 | 334 | self.assertEqual(a.f(), 42) |
331 | 335 |
|
| 336 | + @passIf(not hasattr(importlib, "set_lazy_imports"), "not supported w/ lazy imports") |
| 337 | + def test_with_lazy_imports_failed_invoke(self) -> None: |
| 338 | + # pyre-ignore[16]: no such attribute |
| 339 | + enabled = importlib.is_lazy_imports_enabled() |
| 340 | + try: |
| 341 | + if not enabled: |
| 342 | + # pyre-ignore[16]: no such attribute |
| 343 | + importlib.set_lazy_imports(True) |
| 344 | + self.sbx.write_file( |
| 345 | + "a.py", |
| 346 | + """ |
| 347 | + import __static__ |
| 348 | + from b import g |
| 349 | + def f() -> int: |
| 350 | + global g |
| 351 | + res = g() |
| 352 | + del g |
| 353 | + return res |
| 354 | +
|
| 355 | + def f1() -> int: |
| 356 | + return g() |
| 357 | + """, |
| 358 | + ) |
| 359 | + self.sbx.write_file( |
| 360 | + "b.py", |
| 361 | + """ |
| 362 | + import __static__ |
| 363 | + def g() -> int: |
| 364 | + return 42 |
| 365 | + """, |
| 366 | + ) |
| 367 | + with self.sbx.in_strict_module("a") as a: |
| 368 | + self.assertInBytecode(a.f, "INVOKE_FUNCTION", ((("a",), "g"), 0)) |
| 369 | + self.assertEqual(a.f(), 42) |
| 370 | + with self.assertRaisesRegex( |
| 371 | + StaticTypeError, ".*has been deleted from container, original was.*" |
| 372 | + ): |
| 373 | + self.assertEqual(a.f1(), 42) |
| 374 | + finally: |
| 375 | + if not enabled: |
| 376 | + # pyre-ignore[16]: no such attribute |
| 377 | + importlib.set_lazy_imports(False) |
| 378 | + |
332 | 379 | def test_strict_second_import(self) -> None: |
333 | 380 | """Second import of unmodified strict module (from pyc) is still strict.""" |
334 | 381 | self.sbx.write_file("a.py", "import __strict__\nx = 2") |
@@ -364,9 +411,12 @@ def C(): |
364 | 411 | self.assertEqual(a1.g(), 42) |
365 | 412 | self.assertInBytecode(a1.g, "INVOKE_FUNCTION", ((("a",), "C"), 0)) |
366 | 413 | # ensure pycs exist and we can import from them |
367 | | - with patch.object( |
368 | | - StrictSourceFileLoader, "source_to_code", lambda *a, **kw: None |
369 | | - ), self.sbx.in_strict_module("a") as a2: |
| 414 | + with ( |
| 415 | + patch.object( |
| 416 | + StrictSourceFileLoader, "source_to_code", lambda *a, **kw: None |
| 417 | + ), |
| 418 | + self.sbx.in_strict_module("a") as a2, |
| 419 | + ): |
370 | 420 | self.assertInBytecode(a2.g, "INVOKE_FUNCTION", ((("a",), "C"), 0)) |
371 | 421 | self.assertEqual(a2.g(), 42) |
372 | 422 | # modify dependency, but not module a |
@@ -405,9 +455,12 @@ def f(): |
405 | 455 | self.assertEqual(a1.g(), 42) |
406 | 456 | self.assertInBytecode(a1.g, self.CALL, 0) |
407 | 457 | # ensure pycs exist and we can import from them |
408 | | - with patch.object( |
409 | | - StrictSourceFileLoader, "source_to_code", lambda *a, **kw: None |
410 | | - ), self.sbx.in_strict_module("a") as a2: |
| 458 | + with ( |
| 459 | + patch.object( |
| 460 | + StrictSourceFileLoader, "source_to_code", lambda *a, **kw: None |
| 461 | + ), |
| 462 | + self.sbx.in_strict_module("a") as a2, |
| 463 | + ): |
411 | 464 | self.assertInBytecode(a2.g, self.CALL, 0) |
412 | 465 | self.assertEqual(a2.g(), 42) |
413 | 466 | # modify dependency, but not module a |
@@ -446,9 +499,12 @@ def f(): |
446 | 499 | self.assertEqual(a1.g(), 42) |
447 | 500 | self.assertInBytecode(a1.g, "INVOKE_FUNCTION", ((("b",), "f"), 0)) |
448 | 501 | # ensure pycs exist and we can import from them |
449 | | - with patch.object( |
450 | | - StrictSourceFileLoader, "source_to_code", lambda *a, **kw: None |
451 | | - ), self.sbx.in_strict_module("a") as a2: |
| 502 | + with ( |
| 503 | + patch.object( |
| 504 | + StrictSourceFileLoader, "source_to_code", lambda *a, **kw: None |
| 505 | + ), |
| 506 | + self.sbx.in_strict_module("a") as a2, |
| 507 | + ): |
452 | 508 | self.assertInBytecode(a2.g, "INVOKE_FUNCTION", ((("b",), "f"), 0)) |
453 | 509 | self.assertEqual(a2.g(), 42) |
454 | 510 | b_path.unlink() |
@@ -504,9 +560,12 @@ def C(): |
504 | 560 | invalidation_mode=PycInvalidationMode.CHECKED_HASH, |
505 | 561 | ) |
506 | 562 | # ensure pycs exist and we can import from them |
507 | | - with patch.object( |
508 | | - StrictSourceFileLoader, "source_to_code", lambda *a, **kw: None |
509 | | - ), self.sbx.in_strict_module("a") as a2: |
| 563 | + with ( |
| 564 | + patch.object( |
| 565 | + StrictSourceFileLoader, "source_to_code", lambda *a, **kw: None |
| 566 | + ), |
| 567 | + self.sbx.in_strict_module("a") as a2, |
| 568 | + ): |
510 | 569 | self.assertInBytecode(a2.g, "INVOKE_FUNCTION", ((("a",), "C"), 0)) |
511 | 570 | self.assertEqual(a2.g(), 42) |
512 | 571 | # modify dependency, but not module a |
@@ -555,9 +614,12 @@ def C(): |
555 | 614 | invalidation_mode=PycInvalidationMode.UNCHECKED_HASH, |
556 | 615 | ) |
557 | 616 | # ensure pycs exist and we can import from them |
558 | | - with patch.object( |
559 | | - StrictSourceFileLoader, "source_to_code", lambda *a, **kw: None |
560 | | - ), self.sbx.in_strict_module("a") as a2: |
| 617 | + with ( |
| 618 | + patch.object( |
| 619 | + StrictSourceFileLoader, "source_to_code", lambda *a, **kw: None |
| 620 | + ), |
| 621 | + self.sbx.in_strict_module("a") as a2, |
| 622 | + ): |
561 | 623 | self.assertInBytecode(a2.g, "INVOKE_FUNCTION", ((("a",), "C"), 0)) |
562 | 624 | self.assertEqual(a2.g(), 42) |
563 | 625 | self.sbx.write_file( |
@@ -1926,14 +1988,20 @@ def fn(): |
1926 | 1988 | assert isinstance(a, StrictModule) |
1927 | 1989 | self.assertEqual(a.__final_constants__, ("a",)) |
1928 | 1990 |
|
1929 | | - with StrictModuleTestingPatchProxy(a) as proxy, self.assertRaisesRegex( |
1930 | | - AttributeError, "Cannot patch Final attribute `a` of module `a`" |
| 1991 | + with ( |
| 1992 | + StrictModuleTestingPatchProxy(a) as proxy, |
| 1993 | + self.assertRaisesRegex( |
| 1994 | + AttributeError, "Cannot patch Final attribute `a` of module `a`" |
| 1995 | + ), |
1931 | 1996 | ): |
1932 | 1997 | # pyre-ignore [16]: `proxy` has no attribute `a` |
1933 | 1998 | proxy.a = 0xDEADBEEF |
1934 | 1999 |
|
1935 | | - with StrictModuleTestingPatchProxy(a) as proxy, self.assertRaisesRegex( |
1936 | | - AttributeError, "Cannot patch Final attribute `a` of module `a`" |
| 2000 | + with ( |
| 2001 | + StrictModuleTestingPatchProxy(a) as proxy, |
| 2002 | + self.assertRaisesRegex( |
| 2003 | + AttributeError, "Cannot patch Final attribute `a` of module `a`" |
| 2004 | + ), |
1937 | 2005 | ): |
1938 | 2006 | del proxy.a |
1939 | 2007 |
|
@@ -2287,7 +2355,10 @@ def f(c: mod.C): |
2287 | 2355 |
|
2288 | 2356 | flag.val = False |
2289 | 2357 | # pyre-ignore[21]: Loaded dynamically. |
2290 | | - import mod, other # noqa: E401, F811 |
| 2358 | + import mod # noqa: E401, F811 |
| 2359 | + |
| 2360 | + # pyre-ignore[21]: Loaded dynamically. |
| 2361 | + import other # noqa: E401, F811 |
2291 | 2362 |
|
2292 | 2363 | c = mod.C() |
2293 | 2364 |
|
|
0 commit comments