Coverage Report

Created: 2026-02-26 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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_frame.h"
6
#include "pycore_import.h"
7
#include "pycore_interpframe.h"
8
#include "pycore_lazyimportobject.h"
9
#include "pycore_modsupport.h"
10
11
3.08k
#define PyLazyImportObject_CAST(op) ((PyLazyImportObject *)(op))
12
13
PyObject *
14
_PyLazyImport_New(_PyInterpreterFrame *frame, PyObject *builtins, PyObject *name, PyObject *fromlist)
15
112
{
16
112
    PyLazyImportObject *m;
17
112
    if (!name || !PyUnicode_Check(name)) {
18
0
        PyErr_SetString(PyExc_TypeError, "expected str for name");
19
0
        return NULL;
20
0
    }
21
112
    if (fromlist == Py_None || fromlist == NULL) {
22
0
        fromlist = NULL;
23
0
    }
24
112
    else if (!PyUnicode_Check(fromlist) && !PyTuple_Check(fromlist)) {
25
0
        PyErr_SetString(PyExc_TypeError,
26
0
            "lazy_import: fromlist must be None, a string, or a tuple");
27
0
        return NULL;
28
0
    }
29
112
    m = PyObject_GC_New(PyLazyImportObject, &PyLazyImport_Type);
30
112
    if (m == NULL) {
31
0
        return NULL;
32
0
    }
33
112
    m->lz_builtins = Py_XNewRef(builtins);
34
112
    m->lz_from = Py_NewRef(name);
35
112
    m->lz_attr = Py_XNewRef(fromlist);
36
37
    // Capture frame information for the original import location.
38
112
    m->lz_code = NULL;
39
112
    m->lz_instr_offset = -1;
40
41
112
    if (frame != NULL) {
42
112
        PyCodeObject *code = _PyFrame_GetCode(frame);
43
112
        if (code != NULL) {
44
112
            m->lz_code = (PyCodeObject *)Py_NewRef(code);
45
            // Calculate the instruction offset from the current frame.
46
112
            m->lz_instr_offset = _PyInterpreterFrame_LASTI(frame);
47
112
        }
48
112
    }
49
50
112
    _PyObject_GC_TRACK(m);
51
112
    return (PyObject *)m;
52
112
}
53
54
static int
55
lazy_import_traverse(PyObject *op, visitproc visit, void *arg)
56
3.02k
{
57
3.02k
    PyLazyImportObject *m = PyLazyImportObject_CAST(op);
58
3.02k
    Py_VISIT(m->lz_builtins);
59
3.02k
    Py_VISIT(m->lz_from);
60
3.02k
    Py_VISIT(m->lz_attr);
61
3.02k
    Py_VISIT(m->lz_code);
62
3.02k
    return 0;
63
3.02k
}
64
65
static int
66
lazy_import_clear(PyObject *op)
67
56
{
68
56
    PyLazyImportObject *m = PyLazyImportObject_CAST(op);
69
56
    Py_CLEAR(m->lz_builtins);
70
56
    Py_CLEAR(m->lz_from);
71
56
    Py_CLEAR(m->lz_attr);
72
56
    Py_CLEAR(m->lz_code);
73
56
    return 0;
74
56
}
75
76
static void
77
lazy_import_dealloc(PyObject *op)
78
56
{
79
56
    _PyObject_GC_UNTRACK(op);
80
56
    (void)lazy_import_clear(op);
81
56
    Py_TYPE(op)->tp_free(op);
82
56
}
83
84
static PyObject *
85
lazy_import_name(PyLazyImportObject *m)
86
0
{
87
0
    if (m->lz_attr != NULL) {
88
0
        if (PyUnicode_Check(m->lz_attr)) {
89
0
            return PyUnicode_FromFormat("%U.%U", m->lz_from, m->lz_attr);
90
0
        }
91
0
        else {
92
0
            return PyUnicode_FromFormat("%U...", m->lz_from);
93
0
        }
94
0
    }
95
0
    return Py_NewRef(m->lz_from);
96
0
}
97
98
static PyObject *
99
lazy_import_repr(PyObject *op)
100
0
{
101
0
    PyLazyImportObject *m = PyLazyImportObject_CAST(op);
102
0
    PyObject *name = lazy_import_name(m);
103
0
    if (name == NULL) {
104
0
        return NULL;
105
0
    }
106
0
    PyObject *res = PyUnicode_FromFormat("<%T '%U'>", op, name);
107
0
    Py_DECREF(name);
108
0
    return res;
109
0
}
110
111
PyObject *
112
_PyLazyImport_GetName(PyObject *op)
113
0
{
114
0
    PyLazyImportObject *lazy_import = PyLazyImportObject_CAST(op);
115
0
    assert(PyLazyImport_CheckExact(lazy_import));
116
0
    return lazy_import_name(lazy_import);
117
0
}
118
119
static PyObject *
120
lazy_import_resolve(PyObject *self, PyObject *args)
121
0
{
122
0
    return _PyImport_LoadLazyImportTstate(PyThreadState_GET(), self);
123
0
}
124
125
static PyMethodDef lazy_import_methods[] = {
126
    {
127
        "resolve", lazy_import_resolve, METH_NOARGS,
128
        PyDoc_STR("resolves the lazy import and returns the actual object")
129
    },
130
    {NULL, NULL}
131
};
132
133
134
PyDoc_STRVAR(lazy_import_doc,
135
"lazy_import(builtins, name, fromlist=None, /)\n"
136
"--\n"
137
"\n"
138
"Represents a deferred import that will be resolved on first use.\n"
139
"\n"
140
"Instances of this object accessed from the global scope will be\n"
141
"automatically imported based upon their name and then replaced with\n"
142
"the imported value.");
143
144
PyTypeObject PyLazyImport_Type = {
145
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
146
    .tp_name = "lazy_import",
147
    .tp_basicsize = sizeof(PyLazyImportObject),
148
    .tp_dealloc = lazy_import_dealloc,
149
    .tp_repr = lazy_import_repr,
150
    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
151
    .tp_doc = lazy_import_doc,
152
    .tp_traverse = lazy_import_traverse,
153
    .tp_clear = lazy_import_clear,
154
    .tp_methods = lazy_import_methods,
155
    .tp_alloc = PyType_GenericAlloc,
156
    .tp_free = PyObject_GC_Del,
157
};