/src/cpython/Objects/lazyimportobject.c
Line | Count | Source |
1 | | // Lazy object implementation. |
2 | | |
3 | | #include "Python.h" |
4 | | #include "pycore_ceval.h" |
5 | | #include "pycore_dict.h" |
6 | | #include "pycore_gc.h" |
7 | | #include "pycore_import.h" |
8 | | #include "pycore_interpframe.h" |
9 | | #include "pycore_lazyimportobject.h" |
10 | | #include "pycore_long.h" |
11 | | #include "pycore_moduleobject.h" |
12 | | #include "pycore_pyatomic_ft_wrappers.h" |
13 | | #include "pycore_pyerrors.h" |
14 | | #include "pycore_traceback.h" |
15 | | #include "pycore_tstate.h" |
16 | | |
17 | | typedef struct { |
18 | | PyObject_HEAD |
19 | | PyObject *lz_builtins; // Roots own the mapping; projections retain the root. |
20 | | // A root stores its absolute name (PyUnicode) in lz_from, and original |
21 | | // fromlist in lz_attr. |
22 | | // A projection stores its source placeholder (a PyLazyImportObject) |
23 | | // in lz_from, and the attribute to import from (PyUnicode) it in lz_attr. |
24 | | PyObject *lz_from; |
25 | | PyObject *lz_attr; |
26 | | // Declaration location. |
27 | | PyCodeObject *lz_code; |
28 | | int lz_instr_offset; |
29 | | // The root was accessed, so its declared path requires import semantics. |
30 | | int lz_active; |
31 | | } PyLazyImportObject; |
32 | | |
33 | 16.1k | #define PyLazyImportObject_CAST(op) ((PyLazyImportObject *)(op)) |
34 | | |
35 | | static PyObject *lazy_import_name(PyLazyImportObject *m); |
36 | | |
37 | | PyObject * |
38 | | _PyLazyImport_New(_PyInterpreterFrame *frame, PyObject *builtins, |
39 | | PyObject *name, PyObject *fromlist) |
40 | 490 | { |
41 | 490 | if (!name || !(PyUnicode_Check(name) || PyLazyImport_CheckExact(name))) { |
42 | 0 | PyErr_SetString(PyExc_TypeError, "expected str or lazy_import for name"); |
43 | 0 | return NULL; |
44 | 0 | } |
45 | 490 | if (fromlist == Py_None || fromlist == NULL) { |
46 | 255 | fromlist = NULL; |
47 | 255 | } |
48 | 235 | else if (!PyUnicode_Check(fromlist) && !PyTuple_Check(fromlist)) { |
49 | 0 | PyErr_SetString(PyExc_TypeError, |
50 | 0 | "lazy_import: fromlist must be None, a string, or a tuple"); |
51 | 0 | return NULL; |
52 | 0 | } |
53 | | #ifndef NDEBUG |
54 | | if (PyLazyImport_CheckExact(name)) { |
55 | | // projection |
56 | | assert(builtins == NULL); |
57 | | assert(fromlist != NULL); |
58 | | assert(PyUnicode_Check(fromlist)); |
59 | | } |
60 | | else { |
61 | | // root |
62 | | assert(builtins != NULL); |
63 | | } |
64 | | #endif |
65 | 490 | PyLazyImportObject *m = PyObject_GC_New( |
66 | 490 | PyLazyImportObject, &PyLazyImport_Type); |
67 | 490 | if (m == NULL) { |
68 | 0 | return NULL; |
69 | 0 | } |
70 | 490 | m->lz_builtins = Py_XNewRef(builtins); |
71 | 490 | m->lz_from = Py_NewRef(name); |
72 | 490 | m->lz_attr = Py_XNewRef(fromlist); |
73 | | |
74 | 490 | m->lz_code = NULL; |
75 | 490 | m->lz_instr_offset = -1; |
76 | 490 | m->lz_active = 0; |
77 | | |
78 | 490 | if (frame != NULL) { |
79 | 490 | m->lz_code = (PyCodeObject *)Py_NewRef(_PyFrame_GetCode(frame)); |
80 | 490 | m->lz_instr_offset = _PyInterpreterFrame_LASTI(frame); |
81 | 490 | } |
82 | | |
83 | 490 | _PyObject_GC_TRACK(m); |
84 | 490 | return (PyObject *)m; |
85 | 490 | } |
86 | | |
87 | | // Reuse concrete attributes of initialized modules without waiting for imports |
88 | | // or resolving lazy attributes. Failed cache lookups are retried at resolution. |
89 | | // May return NULL with or without an exception set. |
90 | | static PyObject * |
91 | | lazy_import_get_loaded_attr(PyThreadState *tstate, PyObject *name, |
92 | | PyObject *attr_name) |
93 | 141 | { |
94 | 141 | PyObject *mod = NULL, *spec = NULL, *current = NULL, *attr = NULL; |
95 | 141 | PyObject *modules = Py_XNewRef(_PyImport_GetModules(tstate->interp)); |
96 | 141 | if (modules == NULL) { |
97 | 0 | return NULL; |
98 | 0 | } |
99 | 141 | int rc = PyMapping_GetOptionalItem(modules, name, &mod); |
100 | 141 | if (rc <= 0 || !PyModule_Check(mod)) { |
101 | 139 | goto done; |
102 | 139 | } |
103 | 2 | PyObject *dict = _PyModule_GetDict(mod); |
104 | 2 | if (PyObject_GetOptionalAttr(mod, &_Py_ID(__spec__), &spec) < 0 || |
105 | 2 | _PyModuleSpec_IsInitializing(spec) != 0) { |
106 | 0 | goto done; |
107 | 0 | } |
108 | | // An initialization check can replace the module in sys.modules. |
109 | 2 | if (modules != _PyImport_GetModules(tstate->interp) || |
110 | 2 | PyMapping_GetOptionalItem(modules, name, ¤t) <= 0 || |
111 | 2 | current != mod) { |
112 | 0 | goto done; |
113 | 0 | } |
114 | 2 | if (PyDict_GetItemRef(dict, attr_name, &attr) < 0) { |
115 | 0 | goto done; |
116 | 0 | } |
117 | 2 | if (attr != NULL && PyLazyImport_CheckExact(attr)) { |
118 | 0 | Py_CLEAR(attr); |
119 | 0 | } |
120 | | |
121 | 141 | done: |
122 | 141 | Py_XDECREF(current); |
123 | 141 | Py_XDECREF(spec); |
124 | 141 | Py_XDECREF(mod); |
125 | 141 | Py_DECREF(modules); |
126 | 141 | if (PyErr_ExceptionMatches(PyExc_Exception)) { |
127 | 0 | PyErr_Clear(); |
128 | 0 | } |
129 | 141 | return attr; |
130 | 2 | } |
131 | | |
132 | | PyObject * |
133 | | _PyEval_LazyImportFrom(PyThreadState *tstate, _PyInterpreterFrame *frame, |
134 | | PyObject *v, PyObject *name) |
135 | 141 | { |
136 | 141 | assert(PyLazyImport_CheckExact(v)); |
137 | 141 | assert(name); |
138 | 141 | assert(PyUnicode_Check(name)); |
139 | 141 | PyLazyImportObject *lz = PyLazyImportObject_CAST(v); |
140 | | // Only `from a import b` can take b off an already imported a; |
141 | | // `import a.b as c` has to import a.b first. |
142 | 141 | if (lz->lz_attr != NULL && PyTuple_Check(lz->lz_attr) && |
143 | 141 | PyTuple_GET_SIZE(lz->lz_attr) > 0) { |
144 | 141 | PyObject *attr = lazy_import_get_loaded_attr(tstate, lz->lz_from, name); |
145 | 141 | if (attr != NULL || PyErr_Occurred()) { |
146 | 2 | return attr; |
147 | 2 | } |
148 | 141 | } |
149 | 139 | return _PyLazyImport_New(frame, NULL, v, name); |
150 | 141 | } |
151 | | |
152 | | static int |
153 | | lazy_import_traverse(PyObject *op, visitproc visit, void *arg) |
154 | 15.9k | { |
155 | 15.9k | PyLazyImportObject *m = PyLazyImportObject_CAST(op); |
156 | 15.9k | Py_VISIT(m->lz_builtins); |
157 | 15.9k | Py_VISIT(m->lz_from); |
158 | 15.9k | Py_VISIT(m->lz_attr); |
159 | 15.9k | Py_VISIT(m->lz_code); |
160 | 15.9k | return 0; |
161 | 15.9k | } |
162 | | |
163 | | static int |
164 | | lazy_import_clear(PyObject *op) |
165 | 24 | { |
166 | 24 | PyLazyImportObject *m = PyLazyImportObject_CAST(op); |
167 | 24 | Py_CLEAR(m->lz_builtins); |
168 | 24 | Py_CLEAR(m->lz_from); |
169 | 24 | Py_CLEAR(m->lz_attr); |
170 | 24 | Py_CLEAR(m->lz_code); |
171 | 24 | return 0; |
172 | 24 | } |
173 | | |
174 | | static void |
175 | | lazy_import_dealloc(PyObject *op) |
176 | 24 | { |
177 | 24 | _PyObject_GC_UNTRACK(op); |
178 | 24 | (void)lazy_import_clear(op); |
179 | 24 | Py_TYPE(op)->tp_free(op); |
180 | 24 | } |
181 | | |
182 | | /* Specialize the error message for failed attribute lookups. */ |
183 | | static PyObject * |
184 | | lazy_import_getattro(PyObject *op, PyObject *name) |
185 | 0 | { |
186 | 0 | PyObject *value = _PyObject_GenericGetAttrWithDict( |
187 | 0 | op, name, NULL, /* suppress */ 1); |
188 | 0 | if (value != NULL || PyErr_Occurred()) { |
189 | 0 | return value; |
190 | 0 | } |
191 | 0 | PyObject *lz_name = lazy_import_name(PyLazyImportObject_CAST(op)); |
192 | 0 | if (lz_name == NULL) { |
193 | 0 | return NULL; |
194 | 0 | } |
195 | 0 | PyErr_Format(PyExc_AttributeError, |
196 | 0 | "cannot access attribute %R on unresolved lazy import %R", |
197 | 0 | name, lz_name); |
198 | 0 | Py_DECREF(lz_name); |
199 | 0 | return NULL; |
200 | 0 | } |
201 | | |
202 | | // The dotted name of the object that resolving the placeholder returns. |
203 | | static PyObject * |
204 | | lazy_import_path(PyLazyImportObject *m) |
205 | 28 | { |
206 | 28 | if (PyLazyImport_CheckExact(m->lz_from)) { |
207 | 7 | if (_Py_EnterRecursiveCall(" while getting the name of a lazy import")) { |
208 | 0 | return NULL; |
209 | 0 | } |
210 | 7 | PyObject *base = lazy_import_path((PyLazyImportObject *)m->lz_from); |
211 | 7 | _Py_LeaveRecursiveCall(); |
212 | 7 | if (base == NULL) { |
213 | 0 | return NULL; |
214 | 0 | } |
215 | 7 | PyObject *res = PyUnicode_FromFormat("%U.%U", base, m->lz_attr); |
216 | 7 | Py_DECREF(base); |
217 | 7 | return res; |
218 | 7 | } |
219 | 21 | if (m->lz_attr != NULL && |
220 | 7 | (!PyTuple_Check(m->lz_attr) || PyTuple_GET_SIZE(m->lz_attr) > 0)) { |
221 | 7 | return Py_NewRef(m->lz_from); |
222 | 7 | } |
223 | | // __import__("a.b") returns the top-level package `a`. |
224 | 14 | Py_ssize_t dot = PyUnicode_FindChar( |
225 | 14 | m->lz_from, '.', 0, PyUnicode_GET_LENGTH(m->lz_from), 1 |
226 | 14 | ); |
227 | 14 | if (dot == -2) { |
228 | 0 | return NULL; |
229 | 0 | } |
230 | 14 | if (dot < 0) { |
231 | 14 | return Py_NewRef(m->lz_from); |
232 | 14 | } |
233 | 0 | return PyUnicode_Substring(m->lz_from, 0, dot); |
234 | 14 | } |
235 | | |
236 | | static PyObject * |
237 | | lazy_import_name(PyLazyImportObject *m) |
238 | 0 | { |
239 | 0 | if (PyLazyImport_CheckExact(m->lz_from)) { |
240 | 0 | return lazy_import_path(m); |
241 | 0 | } |
242 | 0 | if (m->lz_attr != NULL && |
243 | 0 | (!PyTuple_Check(m->lz_attr) || PyTuple_GET_SIZE(m->lz_attr) > 0)) { |
244 | 0 | return PyUnicode_FromFormat("%U...", m->lz_from); |
245 | 0 | } |
246 | 0 | return Py_NewRef(m->lz_from); |
247 | 0 | } |
248 | | |
249 | | static PyObject * |
250 | | lazy_import_repr(PyObject *op) |
251 | 0 | { |
252 | 0 | PyLazyImportObject *m = PyLazyImportObject_CAST(op); |
253 | 0 | PyObject *name = lazy_import_name(m); |
254 | 0 | if (name == NULL) { |
255 | 0 | return NULL; |
256 | 0 | } |
257 | 0 | PyObject *res = PyUnicode_FromFormat("<%T '%U'>", op, name); |
258 | 0 | Py_DECREF(name); |
259 | 0 | return res; |
260 | 0 | } |
261 | | |
262 | | // Consume a result, resolving a placeholder returned by an import hook or |
263 | | // an attribute lookup under the same cycle and recursion checks. |
264 | | static PyObject * |
265 | | lazy_import_resolve_result(PyThreadState *tstate, PyObject *obj) |
266 | 28 | { |
267 | 28 | if (obj == NULL || !PyLazyImport_CheckExact(obj)) { |
268 | 28 | return obj; |
269 | 28 | } |
270 | 0 | PyObject *result = _PyImport_LoadLazyImportTstate(tstate, obj); |
271 | 0 | Py_DECREF(obj); |
272 | 0 | return result; |
273 | 28 | } |
274 | | |
275 | | // Look up, in order, the attributes recorded from the root placeholder to lz |
276 | | // on the module the root's import returned. |
277 | | static PyObject * |
278 | | lazy_import_replay_from(PyThreadState *tstate, PyObject *mod, |
279 | | PyLazyImportObject *lz) |
280 | 14 | { |
281 | 14 | if (!PyLazyImport_CheckExact(lz->lz_from)) { |
282 | 7 | return Py_NewRef(mod); |
283 | 7 | } |
284 | 7 | if (_Py_EnterRecursiveCallTstate(tstate, " while resolving a lazy import")) { |
285 | 0 | return NULL; |
286 | 0 | } |
287 | 7 | PyObject *from = lazy_import_replay_from( |
288 | 7 | tstate, mod, (PyLazyImportObject *)lz->lz_from); |
289 | 7 | _Py_LeaveRecursiveCallTstate(tstate); |
290 | 7 | if (from == NULL) { |
291 | 0 | return NULL; |
292 | 0 | } |
293 | 7 | PyObject *obj = _PyEval_ImportFrom(tstate, from, lz->lz_attr); |
294 | 7 | Py_DECREF(from); |
295 | 7 | return lazy_import_resolve_result(tstate, obj); |
296 | 7 | } |
297 | | |
298 | | // Preserve the resolution error and attach the import's declaration location. |
299 | | static void |
300 | | lazy_import_add_exception_cause(PyThreadState *tstate, PyLazyImportObject *lz) |
301 | 0 | { |
302 | 0 | if (!PyErr_Occurred() || lz->lz_code == NULL || lz->lz_instr_offset < 0) { |
303 | 0 | return; |
304 | 0 | } |
305 | 0 | PyObject *exc = _PyErr_GetRaisedException(tstate); |
306 | 0 | PyObject *name = lazy_import_name(lz); |
307 | 0 | if (name == NULL) { |
308 | 0 | goto done; |
309 | 0 | } |
310 | 0 | int lineno = PyCode_Addr2Line(lz->lz_code, lz->lz_instr_offset * 2); |
311 | 0 | const char *filename = PyUnicode_AsUTF8(lz->lz_code->co_filename); |
312 | 0 | if (filename == NULL) { |
313 | 0 | goto done; |
314 | 0 | } |
315 | 0 | const char *funcname = PyUnicode_AsUTF8(lz->lz_code->co_name); |
316 | 0 | if (funcname == NULL) { |
317 | 0 | goto done; |
318 | 0 | } |
319 | 0 | PyBaseExceptionObject *base_exc = (PyBaseExceptionObject *)exc; |
320 | 0 | if (base_exc->cause != NULL || base_exc->context != NULL || |
321 | 0 | base_exc->suppress_context) { |
322 | | // Preserve the original chain, including `raise ... from None`. |
323 | 0 | PyObject *note = PyUnicode_FromFormat( |
324 | 0 | "lazy import of '%U' declared in %s at %s:%d", |
325 | 0 | name, funcname, filename, lineno); |
326 | 0 | if (note != NULL) { |
327 | 0 | PyObject *notes; |
328 | 0 | if (PyObject_GetOptionalAttr(exc, &_Py_ID(__notes__), ¬es) >= 0) { |
329 | 0 | if (notes == NULL || PySequence_Contains(notes, note) == 0) { |
330 | 0 | (void)_PyException_AddNote(exc, note); |
331 | 0 | } |
332 | 0 | Py_XDECREF(notes); |
333 | 0 | } |
334 | 0 | Py_DECREF(note); |
335 | 0 | } |
336 | 0 | goto done; |
337 | 0 | } |
338 | 0 | PyObject *msg = PyUnicode_FromFormat( |
339 | 0 | "lazy import of '%U' raised an exception during resolution", name); |
340 | 0 | if (msg == NULL) { |
341 | 0 | goto done; |
342 | 0 | } |
343 | 0 | PyObject *cause = PyObject_CallOneArg(PyExc_ImportError, msg); |
344 | 0 | Py_DECREF(msg); |
345 | 0 | if (cause == NULL) { |
346 | 0 | goto done; |
347 | 0 | } |
348 | 0 | _PyErr_SetRaisedException(tstate, cause); |
349 | 0 | _PyTraceback_Add(funcname, filename, lineno); |
350 | 0 | PyException_SetCause(exc, _PyErr_GetRaisedException(tstate)); |
351 | |
|
352 | 0 | done: |
353 | 0 | Py_XDECREF(name); |
354 | 0 | _PyErr_SetRaisedException(tstate, exc); |
355 | 0 | } |
356 | | |
357 | | int |
358 | | _PyLazyImport_IsResolving(PyThreadState *tstate, PyObject *op) |
359 | 21 | { |
360 | 21 | _PyThreadStateImpl *ts = (_PyThreadStateImpl *)tstate; |
361 | 21 | assert(PyLazyImport_CheckExact(op)); |
362 | 21 | int active = ts->lazy_imports == NULL ? 0 : PySet_Contains(ts->lazy_imports, op); |
363 | 21 | assert(active >= 0); // Exact placeholders use identity hashing and equality. |
364 | 21 | return active; |
365 | 21 | } |
366 | | |
367 | | static PyObject * |
368 | | lazy_import_resolve_impl(PyThreadState *tstate, PyObject *lazy_import, |
369 | | PyObject **imported_module) |
370 | 21 | { |
371 | 21 | PyObject *obj = NULL; |
372 | 21 | PyObject *fromlist = NULL; |
373 | 21 | PyObject *import_func = NULL; |
374 | 21 | PyObject *resolving = NULL; |
375 | 21 | assert(lazy_import != NULL); |
376 | 21 | assert(PyLazyImport_CheckExact(lazy_import)); |
377 | | |
378 | 21 | PyLazyImportObject *lz = (PyLazyImportObject *)lazy_import; |
379 | | |
380 | | // Walk back to the placeholder IMPORT_NAME left, and the first lookup on it. |
381 | 21 | PyLazyImportObject *root = lz, *first = NULL; |
382 | 21 | while (PyLazyImport_CheckExact(root->lz_from)) { |
383 | 7 | first = root; |
384 | 7 | root = (PyLazyImportObject *)root->lz_from; |
385 | 7 | } |
386 | | |
387 | 21 | if (_PyLazyImport_IsResolving(tstate, lazy_import)) { |
388 | 0 | PyObject *name = lazy_import_name(lz); |
389 | 0 | if (name == NULL) { |
390 | 0 | return NULL; |
391 | 0 | } |
392 | 0 | PyObject *errmsg = PyUnicode_FromFormat( |
393 | 0 | "cannot import name %R (most likely due to a circular import)", |
394 | 0 | name); |
395 | 0 | Py_DECREF(name); |
396 | 0 | if (errmsg != NULL) { |
397 | 0 | PyErr_SetImportErrorSubclass(PyExc_ImportCycleError, errmsg, |
398 | 0 | root->lz_from, NULL); |
399 | 0 | Py_DECREF(errmsg); |
400 | 0 | } |
401 | 0 | return NULL; |
402 | 0 | } |
403 | 21 | if (_Py_EnterRecursiveCallTstate(tstate, " while resolving a lazy import")) { |
404 | 0 | return NULL; |
405 | 0 | } |
406 | 21 | _PyThreadStateImpl *ts = (_PyThreadStateImpl *)tstate; |
407 | 21 | if (ts->lazy_imports == NULL) { |
408 | 7 | ts->lazy_imports = PySet_New(NULL); |
409 | 7 | if (ts->lazy_imports == NULL) { |
410 | 0 | goto done; |
411 | 0 | } |
412 | 7 | } |
413 | 21 | resolving = ts->lazy_imports; |
414 | 21 | if (PySet_Add(resolving, lazy_import) < 0) { |
415 | 0 | goto done; |
416 | 0 | } |
417 | | |
418 | | // `from a import b, c`: import only the name being resolved. |
419 | | // Keep an empty tuple intact for custom __import__ hooks. |
420 | 21 | if (first != NULL && root->lz_attr != NULL && |
421 | 21 | PyTuple_Check(root->lz_attr) && PyTuple_GET_SIZE(root->lz_attr) > 0) { |
422 | 7 | fromlist = PyTuple_Pack(1, first->lz_attr); |
423 | 7 | } |
424 | 14 | else { |
425 | 14 | fromlist = Py_NewRef(root->lz_attr != NULL ? root->lz_attr : Py_None); |
426 | 14 | } |
427 | 21 | if (fromlist == NULL) { |
428 | 0 | goto done; |
429 | 0 | } |
430 | | |
431 | 21 | PyObject *globals = PyEval_GetGlobals(); |
432 | 21 | if (globals == NULL) { |
433 | 0 | globals = Py_None; |
434 | 0 | } |
435 | | |
436 | 21 | if (PyMapping_GetOptionalItem(root->lz_builtins, &_Py_ID(__import__), |
437 | 21 | &import_func) < 0) { |
438 | 0 | goto done; |
439 | 0 | } |
440 | 21 | if (import_func == NULL) { |
441 | 0 | PyErr_SetString(PyExc_ImportError, "__import__ not found"); |
442 | 0 | goto done; |
443 | 0 | } |
444 | 21 | PyObject *name = Py_NewRef(root->lz_from); |
445 | 21 | if (first == NULL && |
446 | 14 | (fromlist == Py_None || |
447 | 0 | (PyTuple_Check(fromlist) && PyTuple_GET_SIZE(fromlist) == 0)) && |
448 | 14 | _PyImport_IsDefaultImportFunc(tstate->interp, import_func) && |
449 | 14 | root->lz_builtins == tstate->interp->builtins) { |
450 | | // A plain import binds the root package. Its pending children are |
451 | | // imported separately when their attributes are accessed. |
452 | 14 | Py_ssize_t dot = PyUnicode_FindChar( |
453 | 14 | name, '.', 0, PyUnicode_GET_LENGTH(name), 1); |
454 | 14 | if (dot >= 0) { |
455 | | // Preserve eager traversal through aliased or non-package parents. |
456 | 0 | int regular = 1; |
457 | 0 | int complete = 1; |
458 | 0 | Py_ssize_t end = PyUnicode_GET_LENGTH(name); |
459 | 0 | for (Py_ssize_t i = dot; i >= 0 && regular; |
460 | 0 | i = PyUnicode_FindChar(name, '.', i + 1, end, 1)) { |
461 | 0 | PyObject *prefix = PyUnicode_Substring(name, 0, i); |
462 | 0 | PyObject *cached = prefix == NULL ? NULL : |
463 | 0 | lazy_import_get_loaded_attr(tstate, prefix, &_Py_ID(__name__)); |
464 | 0 | if (cached == NULL) { |
465 | 0 | complete = 0; |
466 | 0 | Py_XDECREF(prefix); |
467 | 0 | break; |
468 | 0 | } |
469 | 0 | PyObject *path = lazy_import_get_loaded_attr( |
470 | 0 | tstate, prefix, &_Py_ID(__path__)); |
471 | 0 | regular = path != NULL && PyUnicode_Check(cached) && |
472 | 0 | PyUnicode_Compare(cached, prefix) == 0; |
473 | 0 | Py_XDECREF(path); |
474 | 0 | Py_DECREF(cached); |
475 | 0 | Py_DECREF(prefix); |
476 | 0 | } |
477 | 0 | PyObject *loaded = lazy_import_get_loaded_attr( |
478 | 0 | tstate, name, &_Py_ID(__name__)); |
479 | 0 | if (regular && (!complete || loaded == NULL) && !PyErr_Occurred()) { |
480 | 0 | Py_SETREF(name, PyUnicode_Substring(name, 0, dot)); |
481 | 0 | } |
482 | 0 | Py_XDECREF(loaded); |
483 | 0 | if (PyErr_Occurred()) { |
484 | 0 | Py_CLEAR(name); |
485 | 0 | } |
486 | 0 | } |
487 | 14 | else if (dot == -2) { |
488 | 0 | Py_CLEAR(name); |
489 | 0 | } |
490 | 14 | if (name == NULL) { |
491 | 0 | goto done; |
492 | 0 | } |
493 | 14 | } |
494 | 21 | obj = _PyEval_ImportNameWithImport( |
495 | 21 | tstate, import_func, globals, globals, |
496 | 21 | name, fromlist, _PyLong_GetZero() |
497 | 21 | ); |
498 | 21 | if (obj != NULL && name != root->lz_from) { |
499 | 0 | FT_ATOMIC_STORE_INT_RELAXED(root->lz_active, 1); |
500 | 0 | FT_ATOMIC_STORE_INT_RELAXED(tstate->interp->imports.has_lazy_submodules, 1); |
501 | 0 | int package = PyModule_CheckExact(obj) ? |
502 | 0 | PyDict_Contains(_PyModule_GetDict(obj), &_Py_ID(__path__)) : 0; |
503 | 0 | if (package < 0) { |
504 | 0 | Py_CLEAR(obj); |
505 | 0 | } |
506 | 0 | else if (!package) { |
507 | 0 | Py_DECREF(obj); |
508 | 0 | obj = _PyEval_ImportNameWithImport( |
509 | 0 | tstate, import_func, globals, globals, |
510 | 0 | root->lz_from, fromlist, _PyLong_GetZero()); |
511 | 0 | } |
512 | 0 | } |
513 | 21 | if (obj != NULL && name == root->lz_from && |
514 | 21 | _PyImport_IsDefaultImportFunc(tstate->interp, import_func) && |
515 | 21 | _PyImport_ClearLazySubmodule(tstate, name, 0) < 0) { |
516 | 0 | Py_CLEAR(obj); |
517 | 0 | } |
518 | 21 | Py_DECREF(name); |
519 | 21 | obj = lazy_import_resolve_result(tstate, obj); |
520 | | // The normal importer may publish this module on its parent. Custom |
521 | | // hooks retain control of their own assignments to the parent. |
522 | 21 | if (imported_module != NULL && obj != NULL && PyModule_Check(obj) && |
523 | 21 | _PyImport_IsDefaultImportFunc(tstate->interp, import_func)) { |
524 | 21 | *imported_module = Py_NewRef(obj); |
525 | 21 | } |
526 | 21 | if (obj != NULL && first != NULL) { |
527 | | // Keep the hook and root result alive until all attribute lookups finish. |
528 | 7 | PyObject *from = obj; |
529 | 7 | obj = lazy_import_replay_from(tstate, from, lz); |
530 | 7 | Py_DECREF(from); |
531 | 7 | } |
532 | | |
533 | 21 | done: |
534 | 21 | if (obj == NULL) { |
535 | 0 | lazy_import_add_exception_cause(tstate, lz); |
536 | 0 | } |
537 | 21 | assert(obj == NULL || !PyLazyImport_CheckExact(obj)); |
538 | 21 | if (obj != NULL) { |
539 | 21 | PyObject *name = lazy_import_path(lz); |
540 | 21 | if (name == NULL || |
541 | 21 | _PyImport_DiscardLazyModule(tstate->interp, name) < 0) { |
542 | 0 | Py_CLEAR(obj); |
543 | 0 | } |
544 | 21 | Py_XDECREF(name); |
545 | 21 | } |
546 | 21 | if (resolving != NULL) { |
547 | | // A failed set resize can leave the placeholder inserted. Removing by |
548 | | // identity also permits greenlets to finish in a different order. |
549 | 21 | if (PySet_Discard(resolving, lazy_import) < 0) { |
550 | 0 | Py_CLEAR(obj); |
551 | 0 | } |
552 | 21 | if (PySet_GET_SIZE(resolving) == 0) { |
553 | | // Keep the set, but release the capacity used by deep resolutions. |
554 | 21 | (void)PySet_Clear(resolving); |
555 | 21 | } |
556 | 21 | } |
557 | | |
558 | 21 | Py_XDECREF(fromlist); |
559 | 21 | Py_XDECREF(import_func); |
560 | 21 | _Py_LeaveRecursiveCallTstate(tstate); |
561 | 21 | return obj; |
562 | 21 | } |
563 | | |
564 | | PyObject * |
565 | | _PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import) |
566 | 0 | { |
567 | 0 | return lazy_import_resolve_impl(tstate, lazy_import, NULL); |
568 | 0 | } |
569 | | |
570 | | // Resolve a pending child as an aliased import, preserving its declaration. |
571 | | PyObject * |
572 | | _PyLazyImport_LoadChild(PyThreadState *tstate, PyObject *declaration, |
573 | | PyObject *name) |
574 | 0 | { |
575 | 0 | assert(PyLazyImport_CheckExact(declaration)); |
576 | 0 | PyLazyImportObject *source = PyLazyImportObject_CAST(declaration); |
577 | 0 | PyObject *lz = _PyLazyImport_New(NULL, source->lz_builtins, name, NULL); |
578 | 0 | Py_ssize_t end = PyUnicode_GET_LENGTH(name); |
579 | 0 | Py_ssize_t dot = PyUnicode_FindChar(name, '.', 0, end, 1); |
580 | 0 | if (dot == -2) { |
581 | 0 | Py_CLEAR(lz); |
582 | 0 | } |
583 | 0 | while (lz != NULL && dot >= 0) { |
584 | 0 | Py_ssize_t start = dot + 1; |
585 | 0 | dot = PyUnicode_FindChar(name, '.', start, end, 1); |
586 | 0 | if (dot == -2) { |
587 | 0 | Py_CLEAR(lz); |
588 | 0 | break; |
589 | 0 | } |
590 | 0 | PyObject *attr = PyUnicode_Substring(name, start, dot < 0 ? end : dot); |
591 | 0 | PyObject *next = attr == NULL ? NULL : |
592 | 0 | _PyLazyImport_New(NULL, NULL, lz, attr); |
593 | 0 | Py_XDECREF(attr); |
594 | 0 | Py_SETREF(lz, next); |
595 | 0 | } |
596 | 0 | if (lz == NULL) { |
597 | 0 | return NULL; |
598 | 0 | } |
599 | 0 | PyLazyImportObject *child = PyLazyImportObject_CAST(lz); |
600 | 0 | child->lz_code = (PyCodeObject *)Py_XNewRef(source->lz_code); |
601 | 0 | child->lz_instr_offset = source->lz_instr_offset; |
602 | 0 | PyObject *result = _PyImport_LoadLazyImportTstate(tstate, lz); |
603 | 0 | Py_DECREF(lz); |
604 | 0 | if (result != NULL && PyModule_Check(result) && |
605 | 0 | PyUnicode_Compare(source->lz_from, name) != 0 && |
606 | 0 | !PyDict_Contains(_PyModule_GetDict(result), &_Py_ID(__path__))) { |
607 | | // Reaching a non-package must still reject an invalid dotted import. |
608 | 0 | Py_DECREF(result); |
609 | 0 | return _PyLazyImport_LoadChild(tstate, declaration, source->lz_from); |
610 | 0 | } |
611 | 0 | return result; |
612 | 0 | } |
613 | | |
614 | | int |
615 | | _PyLazyImport_IsActive(PyObject *declaration) |
616 | 0 | { |
617 | 0 | assert(PyLazyImport_CheckExact(declaration)); |
618 | 0 | return FT_ATOMIC_LOAD_INT_RELAXED( |
619 | 0 | PyLazyImportObject_CAST(declaration)->lz_active); |
620 | 0 | } |
621 | | |
622 | | // Loading pkg.child can replace a placeholder in pkg.child with the module |
623 | | // before a from-import retrieves the value that belongs in that binding. |
624 | | // This is an optimization that can be safely skipped. |
625 | | static int |
626 | | lazy_import_replace_child(PyThreadState *tstate, PyObject *placeholder, |
627 | | PyObject *name, PyObject *namespace, |
628 | | PyObject *child, PyObject *value) |
629 | 0 | { |
630 | 0 | PyLazyImportObject *root = (PyLazyImportObject *)placeholder; |
631 | 0 | if (!PyLazyImport_CheckExact(root->lz_from)) { |
632 | 0 | return 0; |
633 | 0 | } |
634 | 0 | while (PyLazyImport_CheckExact(root->lz_from)) { |
635 | 0 | root = (PyLazyImportObject *)root->lz_from; |
636 | 0 | } |
637 | 0 | Py_ssize_t end = PyUnicode_GET_LENGTH(root->lz_from); |
638 | 0 | Py_ssize_t dot = PyUnicode_FindChar(root->lz_from, '.', 0, end, -1); |
639 | 0 | if (dot < 0) { |
640 | 0 | return dot == -1 ? 0 : -1; |
641 | 0 | } |
642 | 0 | if (end - dot - 1 != PyUnicode_GET_LENGTH(name)) { |
643 | 0 | return 0; |
644 | 0 | } |
645 | 0 | Py_ssize_t matches = PyUnicode_Tailmatch(root->lz_from, name, dot + 1, end, 1); |
646 | 0 | if (matches <= 0) { |
647 | 0 | return matches ? -1 : 0; |
648 | 0 | } |
649 | 0 | PyObject *parent_name = PyUnicode_Substring(root->lz_from, 0, dot); |
650 | 0 | if (parent_name == NULL) { |
651 | 0 | return -1; |
652 | 0 | } |
653 | 0 | PyObject *modules = Py_XNewRef(_PyImport_GetModules(tstate->interp)); |
654 | 0 | PyObject *parent = NULL; |
655 | 0 | int rc = 0; |
656 | 0 | if (modules != NULL) { |
657 | 0 | rc = PyMapping_GetOptionalItem(modules, parent_name, &parent); |
658 | 0 | if (rc > 0 && PyModule_Check(parent) && |
659 | 0 | _PyModule_GetDict(parent) == namespace) { |
660 | 0 | rc = _PyDict_ReplaceItemIf(namespace, name, child, value); |
661 | 0 | } |
662 | 0 | } |
663 | 0 | Py_XDECREF(parent); |
664 | 0 | Py_XDECREF(modules); |
665 | 0 | Py_DECREF(parent_name); |
666 | 0 | return rc; |
667 | 0 | } |
668 | | |
669 | | PyObject * |
670 | | _PyLazyImport_Reify(PyThreadState *tstate, PyObject *placeholder, |
671 | | PyObject *name, PyObject *namespace) |
672 | 21 | { |
673 | 21 | PyObject *imported_module = NULL; |
674 | 21 | PyObject *value = lazy_import_resolve_impl( |
675 | 21 | tstate, placeholder, &imported_module); |
676 | 21 | if (value == NULL) { |
677 | 0 | Py_XDECREF(imported_module); |
678 | 0 | return NULL; |
679 | 0 | } |
680 | 21 | int rc; |
681 | 21 | if (PyDict_CheckExact(namespace)) { |
682 | 21 | rc = _PyDict_ReplaceItemIf(namespace, name, placeholder, value); |
683 | 21 | if (rc == 0 && imported_module != NULL) { |
684 | 0 | rc = lazy_import_replace_child( |
685 | 0 | tstate, placeholder, name, namespace, imported_module, value); |
686 | 0 | } |
687 | 21 | } |
688 | 0 | else if (Py_TYPE(namespace)->tp_as_mapping == NULL || |
689 | 0 | Py_TYPE(namespace)->tp_as_mapping->mp_ass_subscript == NULL) { |
690 | | // Read-only namespaces can resolve a value without caching it. |
691 | 0 | Py_XDECREF(imported_module); |
692 | 0 | return value; |
693 | 0 | } |
694 | 0 | else { |
695 | | // Custom namespaces retain their mapping protocol. Atomic replacement |
696 | | // is only available for exact dictionaries. |
697 | 0 | PyObject *current; |
698 | 0 | rc = PyMapping_GetOptionalItem(namespace, name, ¤t); |
699 | 0 | if (rc > 0) { |
700 | 0 | if (current == placeholder) { |
701 | 0 | rc = PyObject_SetItem(namespace, name, value); |
702 | 0 | } |
703 | 0 | Py_DECREF(current); |
704 | 0 | } |
705 | 0 | } |
706 | 21 | if (rc < 0) { |
707 | 0 | Py_CLEAR(value); |
708 | 0 | } |
709 | 21 | Py_XDECREF(imported_module); |
710 | 21 | return value; |
711 | 21 | } |
712 | | |
713 | | static PyObject * |
714 | | lazy_import_resolve(PyObject *self, PyObject *args) |
715 | 0 | { |
716 | 0 | return _PyImport_LoadLazyImportTstate(PyThreadState_GET(), self); |
717 | 0 | } |
718 | | |
719 | | static PyMethodDef lazy_import_methods[] = { |
720 | | { |
721 | | "resolve", lazy_import_resolve, METH_NOARGS, |
722 | | PyDoc_STR("Resolve the lazy import and return the imported object.") |
723 | | }, |
724 | | {NULL, NULL} |
725 | | }; |
726 | | |
727 | | |
728 | | PyDoc_STRVAR(lazy_import_doc, |
729 | | "lazy_import(builtins, name, fromlist=None, /)\n" |
730 | | "--\n" |
731 | | "\n" |
732 | | "Represents a lazy import that will be resolved on first use.\n" |
733 | | "\n" |
734 | | "Instances of this object accessed from the global scope will be\n" |
735 | | "automatically imported based upon their name and then replaced with\n" |
736 | | "the imported value."); |
737 | | |
738 | | PyTypeObject PyLazyImport_Type = { |
739 | | PyVarObject_HEAD_INIT(&PyType_Type, 0) |
740 | | .tp_name = "lazy_import", |
741 | | .tp_basicsize = sizeof(PyLazyImportObject), |
742 | | .tp_dealloc = lazy_import_dealloc, |
743 | | .tp_repr = lazy_import_repr, |
744 | | .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, |
745 | | .tp_doc = lazy_import_doc, |
746 | | .tp_getattro = lazy_import_getattro, |
747 | | .tp_traverse = lazy_import_traverse, |
748 | | .tp_clear = lazy_import_clear, |
749 | | .tp_methods = lazy_import_methods, |
750 | | .tp_alloc = PyType_GenericAlloc, |
751 | | .tp_free = PyObject_GC_Del, |
752 | | }; |