Skip to content

Commit fe526b4

Browse files
authored
Merge pull request python#9 from LazyImportsCabal/cleanup
More PEP-810 compatibility, fix memory leaks and LOAD_ATTR specialization
2 parents 46b3b75 + d9ad012 commit fe526b4

File tree

15 files changed

+130
-89
lines changed

15 files changed

+130
-89
lines changed

Include/internal/pycore_dict.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ extern PyObject* _PyDict_GetItemIdWithError(PyObject *dp,
3838
extern int _PyDict_ContainsId(PyObject *, _Py_Identifier *);
3939
extern int _PyDict_SetItemId(PyObject *dp, _Py_Identifier *key, PyObject *item);
4040
extern int _PyDict_DelItemId(PyObject *mp, _Py_Identifier *key);
41+
extern void _PyDict_ClearKeysVersion(PyObject *mp);
4142

4243
extern int _PyDict_Next(
4344
PyObject *mp, Py_ssize_t *pos, PyObject **key, PyObject **value, Py_hash_t *hash);

Include/internal/pycore_lazyimportobject.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ PyAPI_DATA(PyTypeObject) PyLazyImport_Type;
1313

1414
typedef struct {
1515
PyObject_HEAD
16-
PyObject *lz_import_func;
16+
PyObject *lz_builtins;
1717
PyObject *lz_from;
1818
PyObject *lz_attr;
1919
/* Frame information for the original import location */

Include/internal/pycore_moduleobject.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ extern Py_ssize_t _PyModule_GetFilenameUTF8(
5858
PyObject* _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress);
5959
PyObject* _Py_module_getattro(PyObject *m, PyObject *name);
6060

61+
PyAPI_FUNC(int) _PyModule_ReplaceLazyValue(PyObject *dict, PyObject *name, PyObject *value);
62+
6163
#ifdef __cplusplus
6264
}
6365
#endif

Lib/importlib/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959

6060
from ._bootstrap import __import__
6161

62-
from _imp import lazy_import, set_lazy_imports
62+
from _imp import set_lazy_imports
6363

6464

6565
def invalidate_caches():

Lib/test/test_import/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2790,7 +2790,7 @@ def test_lazy_import_pkg_cross_import(self):
27902790

27912791
g = test.test_import.data.lazy_imports.pkg.c.get_globals()
27922792
self.assertEqual(type(g["x"]), int)
2793-
self.assertEqual(type(g["b"]), importlib.lazy_import)
2793+
self.assertEqual(type(g["b"]), types.LazyImportType)
27942794

27952795
class TestSinglePhaseSnapshot(ModuleSnapshot):
27962796
"""A representation of a single-phase init module for testing.

Modules/_typesmodule.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "Python.h"
44
#include "pycore_descrobject.h" // _PyMethodWrapper_Type
5+
#include "pycore_lazyimportobject.h" // PyLazyImport_Type
56
#include "pycore_namespace.h" // _PyNamespace_Type
67
#include "pycore_object.h" // _PyNone_Type, _PyNotImplemented_Type
78
#include "pycore_unionobject.h" // _PyUnion_Type
@@ -35,6 +36,7 @@ _types_exec(PyObject *m)
3536
EXPORT_STATIC_TYPE("GetSetDescriptorType", PyGetSetDescr_Type);
3637
// LambdaType is the same as FunctionType
3738
EXPORT_STATIC_TYPE("LambdaType", PyFunction_Type);
39+
EXPORT_STATIC_TYPE("LazyImportType", PyLazyImport_Type);
3840
EXPORT_STATIC_TYPE("MappingProxyType", PyDictProxy_Type);
3941
EXPORT_STATIC_TYPE("MemberDescriptorType", PyMemberDescr_Type);
4042
EXPORT_STATIC_TYPE("MethodDescriptorType", PyMethodDescr_Type);

Objects/dictobject.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4719,6 +4719,14 @@ sizeof_lock_held(PyDictObject *mp)
47194719
return (Py_ssize_t)res;
47204720
}
47214721

4722+
void
4723+
_PyDict_ClearKeysVersion(PyObject *mp)
4724+
{
4725+
ASSERT_DICT_LOCKED(mp);
4726+
4727+
FT_ATOMIC_STORE_UINT32_RELAXED(((PyDictObject *)mp)->ma_keys->dk_version, 0);
4728+
}
4729+
47224730
Py_ssize_t
47234731
_PyDict_SizeOf(PyDictObject *mp)
47244732
{

Objects/lazyimportobject.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include "pycore_interpframe.h"
99

1010
PyObject *
11-
_PyLazyImport_New(PyObject *import_func, PyObject *from, PyObject *attr)
11+
_PyLazyImport_New(PyObject *builtins, PyObject *from, PyObject *attr)
1212
{
1313
PyLazyImportObject *m;
1414
if (!from || !PyUnicode_Check(from)) {
@@ -23,8 +23,8 @@ _PyLazyImport_New(PyObject *import_func, PyObject *from, PyObject *attr)
2323
if (m == NULL) {
2424
return NULL;
2525
}
26-
Py_XINCREF(import_func);
27-
m->lz_import_func = import_func;
26+
Py_XINCREF(builtins);
27+
m->lz_builtins = builtins;
2828
Py_INCREF(from);
2929
m->lz_from = from;
3030
Py_XINCREF(attr);
@@ -52,7 +52,7 @@ static void
5252
lazy_import_dealloc(PyLazyImportObject *m)
5353
{
5454
PyObject_GC_UnTrack(m);
55-
Py_XDECREF(m->lz_import_func);
55+
Py_XDECREF(m->lz_builtins);
5656
Py_XDECREF(m->lz_from);
5757
Py_XDECREF(m->lz_attr);
5858
Py_XDECREF(m->lz_code);
@@ -88,7 +88,7 @@ lazy_import_repr(PyLazyImportObject *m)
8888
static int
8989
lazy_import_traverse(PyLazyImportObject *m, visitproc visit, void *arg)
9090
{
91-
Py_VISIT(m->lz_import_func);
91+
Py_VISIT(m->lz_builtins);
9292
Py_VISIT(m->lz_from);
9393
Py_VISIT(m->lz_attr);
9494
Py_VISIT(m->lz_code);
@@ -98,7 +98,7 @@ lazy_import_traverse(PyLazyImportObject *m, visitproc visit, void *arg)
9898
static int
9999
lazy_import_clear(PyLazyImportObject *m)
100100
{
101-
Py_CLEAR(m->lz_import_func);
101+
Py_CLEAR(m->lz_builtins);
102102
Py_CLEAR(m->lz_from);
103103
Py_CLEAR(m->lz_attr);
104104
Py_CLEAR(m->lz_code);
@@ -144,8 +144,8 @@ static PyMethodDef lazy_methods[] = {
144144

145145
PyTypeObject PyLazyImport_Type = {
146146
PyVarObject_HEAD_INIT(&PyType_Type, 0)
147-
"lazy_import", /* tp_name */
148-
sizeof(PyLazyImportObject), /* tp_basicsize */
147+
"LazyImport", /* tp_name */
148+
sizeof(PyLazyImportObject), /* tp_basicsize */
149149
0, /* tp_itemsize */
150150
(destructor)lazy_import_dealloc, /* tp_dealloc */
151151
0, /* tp_print */

Objects/moduleobject.c

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,6 +1036,23 @@ _PyModule_IsPossiblyShadowing(PyObject *origin)
10361036
return result;
10371037
}
10381038

1039+
int
1040+
_PyModule_ReplaceLazyValue(PyObject *dict, PyObject *name, PyObject *value)
1041+
{
1042+
// The adaptive interpreter uses the dictionary version to return the slot at
1043+
// a given index from the module. When replacing a value the version number doesn't
1044+
// change, so we need to atomically clear the version before replacing so that it
1045+
// doesn't return a lazy value.
1046+
int err;
1047+
Py_BEGIN_CRITICAL_SECTION(dict);
1048+
1049+
_PyDict_ClearKeysVersion(dict);
1050+
err = _PyDict_SetItem_LockHeld((PyDictObject *)dict, name, value);
1051+
1052+
Py_END_CRITICAL_SECTION();
1053+
return err;
1054+
}
1055+
10391056
PyObject*
10401057
_Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress)
10411058
{
@@ -1052,13 +1069,13 @@ _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress)
10521069
// instead treated as if the attribute doesn't exist.
10531070
PyErr_Clear();
10541071
}
1055-
1056-
return NULL;
1057-
} else if (PyDict_SetItem(m->md_dict, name, new_value) < 0) {
1058-
Py_DECREF(new_value);
10591072
Py_DECREF(attr);
10601073
return NULL;
10611074
}
1075+
1076+
if (_PyModule_ReplaceLazyValue(m->md_dict, name, new_value) < 0) {
1077+
Py_CLEAR(new_value);
1078+
}
10621079
Py_DECREF(attr);
10631080
return new_value;
10641081
}
@@ -1490,7 +1507,7 @@ module_get_dict(PyObject *mod, void *Py_UNUSED(ignored))
14901507
if (new_value == NULL) {
14911508
return NULL;
14921509
}
1493-
if (PyDict_SetItem(self->md_dict, key, new_value) < 0) {
1510+
if (_PyModule_ReplaceLazyValue(self->md_dict, key, new_value) < 0) {
14941511
Py_DECREF(new_value);
14951512
return NULL;
14961513
}

Python/bytecodes.c

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1799,12 +1799,13 @@ dummy_func(
17991799
ERROR_IF(v_o == NULL);
18001800
if (PyLazyImport_CheckExact(v_o)) {
18011801
PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, v_o);
1802-
if (l_v != NULL && PyDict_SetItem(GLOBALS(), name, l_v) < 0) {
1802+
Py_DECREF(v_o);
1803+
ERROR_IF(l_v == NULL);
1804+
int err = _PyModule_ReplaceLazyValue(GLOBALS(), name, l_v);
1805+
if (err < 0) {
18031806
JUMP_TO_LABEL(error);
18041807
}
1805-
Py_DECREF(v_o);
18061808
v_o = l_v;
1807-
ERROR_IF(v_o == NULL);
18081809
}
18091810

18101811
v = PyStackRef_FromPyObjectSteal(v_o);
@@ -1838,13 +1839,14 @@ dummy_func(
18381839
PyObject *res_o = PyStackRef_AsPyObjectBorrow(*res);
18391840
if (PyLazyImport_CheckExact(res_o)) {
18401841
PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, res_o);
1841-
if (l_v != NULL && PyDict_SetItem(GLOBALS(), name, l_v) < 0) {
1842+
PyStackRef_CLOSE(res[0]);
1843+
ERROR_IF(l_v == NULL);
1844+
int err = _PyModule_ReplaceLazyValue(GLOBALS(), name, l_v);
1845+
if (err < 0) {
1846+
Py_DECREF(l_v);
18421847
JUMP_TO_LABEL(error);
18431848
}
1844-
res_o = l_v;
1845-
PyStackRef_CLOSE(res[0]);
1846-
ERROR_IF(res_o == NULL);
1847-
*res = PyStackRef_FromPyObjectSteal(res_o);
1849+
*res = PyStackRef_FromPyObjectSteal(l_v);
18481850
}
18491851
}
18501852

0 commit comments

Comments
 (0)