/* 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, };