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