mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
```
import types
types.LazyImportType({}, "3", 0)
```
```
def f():
exec("lazy import json")
f()
```
180 lines
4.8 KiB
C
180 lines
4.8 KiB
C
/* Lazy object implementation */
|
|
|
|
#include "Python.h"
|
|
#include "pycore_ceval.h"
|
|
#include "pycore_frame.h"
|
|
#include "pycore_import.h"
|
|
#include "pycore_interpframe.h"
|
|
#include "pycore_lazyimportobject.h"
|
|
#include "pycore_modsupport.h"
|
|
|
|
#define PyLazyImportObject_CAST(op) ((PyLazyImportObject *)(op))
|
|
|
|
PyObject *
|
|
_PyLazyImport_New(PyObject *builtins, PyObject *from, PyObject *attr)
|
|
{
|
|
PyLazyImportObject *m;
|
|
if (!from || !PyUnicode_Check(from)) {
|
|
PyErr_BadArgument();
|
|
return NULL;
|
|
}
|
|
if (attr == Py_None || attr == NULL) {
|
|
attr = NULL;
|
|
}
|
|
else if (!PyUnicode_Check(attr) && !PyTuple_Check(attr)) {
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"lazy_import: fromlist must be None, a string, or a tuple");
|
|
return NULL;
|
|
}
|
|
m = PyObject_GC_New(PyLazyImportObject, &PyLazyImport_Type);
|
|
if (m == NULL) {
|
|
return NULL;
|
|
}
|
|
m->lz_builtins = Py_XNewRef(builtins);
|
|
m->lz_from = Py_NewRef(from);
|
|
m->lz_attr = Py_XNewRef(attr);
|
|
|
|
/* Capture frame information for the original import location */
|
|
m->lz_code = NULL;
|
|
m->lz_instr_offset = -1;
|
|
|
|
_PyInterpreterFrame *frame = _PyEval_GetFrame();
|
|
if (frame != NULL) {
|
|
PyCodeObject *code = _PyFrame_GetCode(frame);
|
|
if (code != NULL) {
|
|
m->lz_code = (PyCodeObject *)Py_NewRef(code);
|
|
/* Calculate the instruction offset from the current frame */
|
|
m->lz_instr_offset = _PyInterpreterFrame_LASTI(frame);
|
|
}
|
|
}
|
|
|
|
_PyObject_GC_TRACK(m);
|
|
return (PyObject *)m;
|
|
}
|
|
|
|
static int
|
|
lazy_import_traverse(PyObject *op, visitproc visit, void *arg)
|
|
{
|
|
PyLazyImportObject *m = PyLazyImportObject_CAST(op);
|
|
Py_VISIT(m->lz_builtins);
|
|
Py_VISIT(m->lz_from);
|
|
Py_VISIT(m->lz_attr);
|
|
Py_VISIT(m->lz_code);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
lazy_import_clear(PyObject *op)
|
|
{
|
|
PyLazyImportObject *m = PyLazyImportObject_CAST(op);
|
|
Py_CLEAR(m->lz_builtins);
|
|
Py_CLEAR(m->lz_from);
|
|
Py_CLEAR(m->lz_attr);
|
|
Py_CLEAR(m->lz_code);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
lazy_import_dealloc(PyObject *op)
|
|
{
|
|
_PyObject_GC_UNTRACK(op);
|
|
(void)lazy_import_clear(op);
|
|
Py_TYPE(op)->tp_free(op);
|
|
}
|
|
|
|
static PyObject *
|
|
lazy_import_name(PyLazyImportObject *m)
|
|
{
|
|
if (m->lz_attr != NULL) {
|
|
if (PyUnicode_Check(m->lz_attr)) {
|
|
return PyUnicode_FromFormat("%U.%U", m->lz_from, m->lz_attr);
|
|
}
|
|
else {
|
|
return PyUnicode_FromFormat("%U...", m->lz_from);
|
|
}
|
|
}
|
|
return Py_NewRef(m->lz_from);
|
|
}
|
|
|
|
static PyObject *
|
|
lazy_import_repr(PyObject *op)
|
|
{
|
|
PyLazyImportObject *m = PyLazyImportObject_CAST(op);
|
|
PyObject *name = lazy_import_name(m);
|
|
if (name == NULL) {
|
|
return NULL;
|
|
}
|
|
PyObject *res = PyUnicode_FromFormat("<%T '%U'>", op, name);
|
|
Py_DECREF(name);
|
|
return res;
|
|
}
|
|
|
|
static PyObject *
|
|
lazy_import_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|
{
|
|
PyTypeObject *base_tp = &PyLazyImport_Type;
|
|
if (
|
|
(type == base_tp || type->tp_init == base_tp->tp_init)
|
|
&& !_PyArg_NoKeywords("lazy_import", kwds)
|
|
) {
|
|
return NULL;
|
|
}
|
|
|
|
Py_ssize_t nargs = PyTuple_GET_SIZE(args);
|
|
if (!_PyArg_CheckPositional("lazy_import", nargs, 2, 3)) {
|
|
return NULL;
|
|
}
|
|
|
|
PyObject *builtins = PyTuple_GET_ITEM(args, 0);
|
|
PyObject *from = PyTuple_GET_ITEM(args, 1);
|
|
PyObject *attr = nargs == 3 ? PyTuple_GET_ITEM(args, 2) : NULL;
|
|
return _PyLazyImport_New(builtins, from, attr);
|
|
}
|
|
|
|
PyObject *
|
|
_PyLazyImport_GetName(PyObject *op)
|
|
{
|
|
PyLazyImportObject *lazy_import = PyLazyImportObject_CAST(op);
|
|
assert(PyLazyImport_CheckExact(lazy_import));
|
|
return lazy_import_name(lazy_import);
|
|
}
|
|
|
|
static PyObject *
|
|
lazy_import_resolve(PyObject *self, PyObject *args)
|
|
{
|
|
return _PyImport_LoadLazyImportTstate(PyThreadState_GET(), self);
|
|
}
|
|
|
|
static PyMethodDef lazy_import_methods[] = {
|
|
{
|
|
"resolve", lazy_import_resolve, METH_NOARGS,
|
|
PyDoc_STR("resolves the lazy import and returns the actual object")
|
|
},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
|
|
PyDoc_STRVAR(lazy_import_doc,
|
|
"lazy_import(builtins, name, fromlist=None, /)\n"
|
|
"--\n"
|
|
"\n"
|
|
"Represents a deferred import that will be resolved on first use.\n"
|
|
"\n"
|
|
"This type is used internally by the 'lazy import' statement.\n"
|
|
"Users should not typically create instances directly.");
|
|
|
|
PyTypeObject PyLazyImport_Type = {
|
|
PyVarObject_HEAD_INIT(&PyType_Type, 0)
|
|
.tp_name = "lazy_import",
|
|
.tp_basicsize = sizeof(PyLazyImportObject),
|
|
.tp_dealloc = lazy_import_dealloc,
|
|
.tp_repr = lazy_import_repr,
|
|
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE,
|
|
.tp_doc = lazy_import_doc,
|
|
.tp_traverse = lazy_import_traverse,
|
|
.tp_clear = lazy_import_clear,
|
|
.tp_methods = lazy_import_methods,
|
|
.tp_alloc = PyType_GenericAlloc,
|
|
.tp_new = lazy_import_new,
|
|
.tp_free = PyObject_GC_Del,
|
|
};
|