Skip to content

Commit ff52c1d

Browse files
Kronuzfacebook-github-bot
authored andcommitted
Fixes NameError bug
Summary: We had been seeing bogus `NameError` exceptions and uninformative `ImportError: Unable to resolve all lazy imports` when import cycles got in the way of resolving a deferred objects. The reason was `_PyImport_LoadLazyImportTstate()` returned `NULL` without error to signal a cycle and this wasn't handled properly in all cases. We now return an explicit `ImportCycleError` instead, and ignore that particular exception during `LookupAttr()` to make the import system resolve cases like `from . import version`, where `version` is a submodule. Reviewed By: cxxxs Differential Revision: D49166179 fbshipit-source-id: 0423e72865cdebbb4f7b8142527306aae06756c8
1 parent b3d2841 commit ff52c1d

File tree

13 files changed

+171
-29
lines changed

13 files changed

+171
-29
lines changed

Cinder/Include/cinder/exports.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ CiAPI_FUNC(PyObject *) Ci_dict_subscript(PyObject *mp, PyObject *key);
2424
CiAPI_FUNC(PyObject *) Ci_list_subscript(PyObject *list, PyObject *item);
2525
CiAPI_FUNC(PyObject *) Ci_tuple_subscript(PyObject *self, PyObject *item);
2626
CiAPI_FUNC(PyObject *) Ci_module_lookupattro(PyObject *self, PyObject *name, int suppress);
27+
CiAPI_FUNC(PyObject *) Ci_strictmodule_lookupattro(PyObject *self, PyObject *name, int suppress);
2728

2829
CiAPI_FUNC(Py_hash_t) Ci_TupleHashItems(PyObject *const *items, Py_ssize_t len);
2930

CinderX/known-core-python-exported-symbols

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ Ci_PyWaitHandle_Type
4646
Ci_RuntimeState_SetProfileInterpPeriod
4747
Ci_set_attribute_error_context
4848
Ci_StaticFunction_Vectorcall
49+
Ci_strictmodule_lookupattro
4950
Ci_SuperLookupMethodOrAttr
5051
Ci_ThreadState_SetProfileInterp
5152
Ci_ThreadState_SetProfileInterpAll
@@ -756,6 +757,7 @@ PyExc_FileNotFoundError
756757
PyExc_FloatingPointError
757758
PyExc_FutureWarning
758759
PyExc_GeneratorExit
760+
PyExc_ImportCycleError
759761
PyExc_ImportError
760762
PyExc_ImportWarning
761763
PyExc_IndentationError

Include/pyerrors.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ PyAPI_DATA(PyObject *) PyExc_BufferError;
7979
PyAPI_DATA(PyObject *) PyExc_EOFError;
8080
PyAPI_DATA(PyObject *) PyExc_FloatingPointError;
8181
PyAPI_DATA(PyObject *) PyExc_OSError;
82+
PyAPI_DATA(PyObject *) PyExc_ImportCycleError;
8283
PyAPI_DATA(PyObject *) PyExc_ImportError;
8384
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03060000
8485
PyAPI_DATA(PyObject *) PyExc_ModuleNotFoundError;

Lib/_compat_pickle.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@
244244
REVERSE_NAME_MAPPING[('builtins', excname)] = ('exceptions', 'OSError')
245245

246246
PYTHON3_IMPORTERROR_EXCEPTIONS = (
247+
'ImportCycleError',
247248
'ModuleNotFoundError',
248249
)
249250

Lib/test/exception_hierarchy.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ BaseException
1414
+-- BufferError
1515
+-- EOFError
1616
+-- ImportError
17+
| +-- ImportCycleError
1718
| +-- ModuleNotFoundError
1819
+-- LookupError
1920
| +-- IndexError
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
try:
2+
from . import a
3+
a
4+
# we expect a circular import ImportError; anything else is wrong
5+
except ImportError as e:
6+
if "circular import" not in str(e):
7+
raise
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from .b import B
2+
3+
def get_B():
4+
return B
5+
6+
B
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
B = "B"
2+
3+
from .a import get_B
4+
5+
get_B()

Lib/test/test_compiler/test_strict/test_loader.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import _imp
34
import dis
45
import gc
56
import io
@@ -2568,3 +2569,34 @@ def f(c: mod.C):
25682569
# version of the C class, but the argument check will be against the
25692570
# old version of the C class from the failed import
25702571
self.assertIs(other.f(c), c)
2572+
2573+
def test_strict_lazy_import_cycle(self):
2574+
self.sbx.write_file(
2575+
"mod/__init__.py",
2576+
"""
2577+
import __strict__
2578+
from . import version
2579+
""",
2580+
)
2581+
self.sbx.write_file(
2582+
"mod/version.py",
2583+
"""
2584+
import __strict__
2585+
VERSION = 1
2586+
""",
2587+
)
2588+
self.sbx.write_file(
2589+
"entry.py",
2590+
"""
2591+
from mod import version
2592+
v = version.VERSION
2593+
""",
2594+
)
2595+
for lazy in [True, False]:
2596+
with self.subTest(lazy=lazy):
2597+
orig = _imp._set_lazy_imports(lazy)
2598+
try:
2599+
mod = self.sbx.strict_import("entry")
2600+
finally:
2601+
_imp._set_lazy_imports(*orig)
2602+
self.assertEqual(mod.v, 1)

Objects/exceptions.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,14 @@ ComplexExtendsException(PyExc_Exception, ImportError,
779779
"Import can't find module, or can't find name in "
780780
"module.");
781781

782+
/*
783+
* ImportCycleError extends ImportError
784+
*/
785+
786+
787+
MiddlingExtendsException(PyExc_ImportError, ImportCycleError, ImportError,
788+
"Import produces a cycle.");
789+
782790
/*
783791
* ModuleNotFoundError extends ImportError
784792
*/
@@ -2686,6 +2694,7 @@ _PyExc_Init(PyInterpreterState *interp)
26862694
PRE_INIT(SystemExit);
26872695
PRE_INIT(KeyboardInterrupt);
26882696
PRE_INIT(ImportError);
2697+
PRE_INIT(ImportCycleError);
26892698
PRE_INIT(ModuleNotFoundError);
26902699
PRE_INIT(OSError);
26912700
PRE_INIT(EOFError);
@@ -2821,6 +2830,7 @@ _PyBuiltins_AddExceptions(PyObject *bltinmod)
28212830
POST_INIT(SystemExit);
28222831
POST_INIT(KeyboardInterrupt);
28232832
POST_INIT(ImportError);
2833+
POST_INIT(ImportCycleError);
28242834
POST_INIT(ModuleNotFoundError);
28252835
POST_INIT(OSError);
28262836
INIT_ALIAS(EnvironmentError, OSError);

0 commit comments

Comments
 (0)