Skip to content

Commit e9d9e90

Browse files
DinoVmeta-codesync[bot]
authored andcommitted
Add extra logging when we can't find a target to invoke
Summary: https://fb.workplace.com/groups/fbpython/permalink/33362824583332156/ There are transient errors popping up. This adds some extra logging to see if it can help determine what's happening and a test case that hits the code path. Reviewed By: alexmalyshev Differential Revision: D90198692 fbshipit-source-id: b4e6576c32fdec14fcd737b3fc5eece461ec1308
1 parent 7b26dc6 commit e9d9e90

File tree

2 files changed

+123
-30
lines changed

2 files changed

+123
-30
lines changed

cinderx/PythonLib/test_cinderx/test_compiler/test_strict/test_loader.py

Lines changed: 99 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import _imp
77
import dis
88
import gc
9+
import importlib
910
import io
1011
import os
1112
import pathlib
@@ -15,7 +16,6 @@
1516
import textwrap
1617
import unittest
1718
from collections.abc import Callable, Generator, Sequence
18-
1919
from contextlib import contextmanager
2020
from importlib.abc import Loader
2121
from importlib.machinery import SOURCE_SUFFIXES, SourceFileLoader
@@ -37,7 +37,7 @@
3737
StrictSourceFileLoader,
3838
)
3939
from cinderx.compiler.strict.runtime import set_freeze_enabled
40-
40+
from cinderx.static import StaticTypeError
4141
from cinderx.test_support import passIf
4242

4343
from . import sandbox as base_sandbox
@@ -179,9 +179,11 @@ class Sandbox(base_sandbox.Sandbox):
179179
def begin_loader(
180180
self, loader: tuple[Callable[[str, str], Loader], list[str]]
181181
) -> 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+
):
185187
yield
186188

187189
def strict_import(self, *module_names: str) -> TModule | list[TModule]:
@@ -222,9 +224,11 @@ def import_modules(self, *module_names: str) -> TModule | list[TModule]:
222224

223225
@contextmanager
224226
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+
):
228232
yield
229233

230234
@contextmanager
@@ -329,6 +333,49 @@ def g() -> int:
329333
self.assertInBytecode(a.f, "INVOKE_FUNCTION", ((("a",), "g"), 0))
330334
self.assertEqual(a.f(), 42)
331335

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+
332379
def test_strict_second_import(self) -> None:
333380
"""Second import of unmodified strict module (from pyc) is still strict."""
334381
self.sbx.write_file("a.py", "import __strict__\nx = 2")
@@ -364,9 +411,12 @@ def C():
364411
self.assertEqual(a1.g(), 42)
365412
self.assertInBytecode(a1.g, "INVOKE_FUNCTION", ((("a",), "C"), 0))
366413
# 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+
):
370420
self.assertInBytecode(a2.g, "INVOKE_FUNCTION", ((("a",), "C"), 0))
371421
self.assertEqual(a2.g(), 42)
372422
# modify dependency, but not module a
@@ -405,9 +455,12 @@ def f():
405455
self.assertEqual(a1.g(), 42)
406456
self.assertInBytecode(a1.g, self.CALL, 0)
407457
# 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+
):
411464
self.assertInBytecode(a2.g, self.CALL, 0)
412465
self.assertEqual(a2.g(), 42)
413466
# modify dependency, but not module a
@@ -446,9 +499,12 @@ def f():
446499
self.assertEqual(a1.g(), 42)
447500
self.assertInBytecode(a1.g, "INVOKE_FUNCTION", ((("b",), "f"), 0))
448501
# 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+
):
452508
self.assertInBytecode(a2.g, "INVOKE_FUNCTION", ((("b",), "f"), 0))
453509
self.assertEqual(a2.g(), 42)
454510
b_path.unlink()
@@ -504,9 +560,12 @@ def C():
504560
invalidation_mode=PycInvalidationMode.CHECKED_HASH,
505561
)
506562
# 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+
):
510569
self.assertInBytecode(a2.g, "INVOKE_FUNCTION", ((("a",), "C"), 0))
511570
self.assertEqual(a2.g(), 42)
512571
# modify dependency, but not module a
@@ -555,9 +614,12 @@ def C():
555614
invalidation_mode=PycInvalidationMode.UNCHECKED_HASH,
556615
)
557616
# 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+
):
561623
self.assertInBytecode(a2.g, "INVOKE_FUNCTION", ((("a",), "C"), 0))
562624
self.assertEqual(a2.g(), 42)
563625
self.sbx.write_file(
@@ -1926,14 +1988,20 @@ def fn():
19261988
assert isinstance(a, StrictModule)
19271989
self.assertEqual(a.__final_constants__, ("a",))
19281990

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+
),
19311996
):
19321997
# pyre-ignore [16]: `proxy` has no attribute `a`
19331998
proxy.a = 0xDEADBEEF
19341999

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+
),
19372005
):
19382006
del proxy.a
19392007

@@ -2287,7 +2355,10 @@ def f(c: mod.C):
22872355

22882356
flag.val = False
22892357
# 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
22912362

22922363
c = mod.C()
22932364

cinderx/StaticPython/classloader.c

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -319,11 +319,33 @@ PyObject* _PyClassLoader_ResolveFunction(PyObject* path, PyObject** container) {
319319
((PyTypeObject*)container_obj)->tp_name,
320320
attr_name);
321321
} else {
322+
PyErr_Clear();
323+
PyObject* dir = PyObject_Dir(container_obj);
324+
if (dir != NULL) {
325+
PyObject* comma = PyUnicode_FromString(", ");
326+
if (comma != NULL) {
327+
PyObject* dir_str = PyUnicode_Join(comma, dir);
328+
Py_DECREF(comma);
329+
Py_DECREF(dir);
330+
dir = dir_str;
331+
} else {
332+
Py_CLEAR(dir);
333+
}
334+
}
335+
if (dir == NULL) {
336+
PyErr_Clear();
337+
dir = Py_None;
338+
}
322339
PyErr_Format(
323340
CiExc_StaticTypeError,
324-
"%R.%U has been deleted from container",
341+
"%R.%U (%s) has been deleted from container, original was %R, "
342+
"contents are %R",
325343
container_obj,
326-
attr_name);
344+
attr_name,
345+
Py_TYPE(container_obj)->tp_name,
346+
original,
347+
dir);
348+
Py_DECREF(dir);
327349
}
328350
}
329351
Py_DECREF(container_obj);

0 commit comments

Comments
 (0)