Skip to content

Commit dae95ca

Browse files
authored
Adjusting _Py_FREEZABLE_PROXY (#92)
* This should be safe * Try changeing the default freezability * Restrict module-proxies to module objects * Change default freezability of _random * Better re-import for modules in proxy mode
1 parent d5b515f commit dae95ca

5 files changed

Lines changed: 64 additions & 9 deletions

File tree

Lib/test/test_freeze/test_module_proxy.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,20 @@ def coin():
7070
elif hasattr(mut_random, attr):
7171
delattr(mut_random, attr)
7272

73+
def test_proxy_reimport(self):
74+
import random
75+
self.assertFalse(is_frozen(random))
76+
77+
freeze(random)
78+
79+
# Unimport "random"
80+
old_mut_random = sys.mut_modules["random"]
81+
del sys.mut_modules["random"]
82+
83+
# This should reimport the module
84+
random.random()
85+
86+
7387

7488
if __name__ == '__main__':
7589
unittest.main()

Modules/_randommodule.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,13 @@ _random_exec(PyObject *module)
639639
if (state->Long___abs__ == NULL) {
640640
return -1;
641641
}
642+
643+
if (_PyImmutability_SetFreezable(module, _Py_FREEZABLE_NO) < 0
644+
|| _PyImmutability_SetFreezable(state->Random_Type, _Py_FREEZABLE_NO) < 0
645+
) {
646+
return -1;
647+
}
648+
642649
return 0;
643650
}
644651

Objects/moduleobject.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1501,10 +1501,14 @@ static PyGetSetDef module_getsets[] = {
15011501
static int
15021502
module_reachable(PyObject *self, visitproc visit, void *arg)
15031503
{
1504+
// FIXME(immutability): Allow modules to define their own custom
1505+
// `md_reachable` function. Currently, we're falling back on
1506+
// `md_traverse`
15041507
Py_VISIT(_PyObject_CAST(Py_TYPE(self)));
15051508
return module_traverse(self, visit, arg);
15061509
}
15071510

1511+
// Artifact[Implementation]: This turns an existing ModuleObject into a proxy object:
15081512
static int
15091513
module_make_immutable_proxy(PyObject *self) {
15101514
// Use cast, since we want this exact object
@@ -1554,13 +1558,19 @@ module_make_immutable_proxy(PyObject *self) {
15541558
return 0;
15551559
}
15561560

1557-
// Artifact[Implementation]: The pre-freeze hook of module objects
15581561
static int
15591562
module_prefreeze(PyObject *self) {
1560-
// TODO(immutable): Check if the module defines a custom pre-freeze hook:
1563+
// FIXME(immutability): Modules can define their own pre-freeze hook
1564+
// and then delegate to this function to turn themself into a
1565+
// proxy object. While this works, it's a bit cumbersome. There should
1566+
// be an easier and more direct way
15611567

1562-
// TODO(immutable): Check if the module wants to be a proxy first:
1563-
return module_make_immutable_proxy(self);
1568+
_Py_freezable_status status = _PyImmutability_GetFreezable(self);
1569+
if (status == _Py_FREEZABLE_PROXY) {
1570+
return module_make_immutable_proxy(self);
1571+
}
1572+
1573+
return 0;
15641574
}
15651575

15661576
PyTypeObject PyModule_Type = {

Python/immutability.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,10 @@ int init_state(struct _Py_immutability_state *state)
174174
return -1;
175175
}
176176
}
177+
178+
if (_PyImmutability_SetFreezable((PyObject*)&PyModule_Type, _Py_FREEZABLE_PROXY)) {
179+
return -1;
180+
}
177181
return 0;
178182
}
179183

@@ -1498,7 +1502,9 @@ int _PyImmutability_SetFreezable(PyObject *obj, _Py_freezable_status status)
14981502
return -1;
14991503
}
15001504

1501-
if (status == _Py_FREEZABLE_PROXY && !PyModule_Check(obj)) {
1505+
if (status == _Py_FREEZABLE_PROXY
1506+
&& !(PyModule_Check(obj) || obj == _PyObject_CAST(&PyModule_Type))
1507+
) {
15021508
PyErr_SetString(PyExc_TypeError,
15031509
"FREEZABLE_PROXY can only be set on module objects");
15041510
return -1;

Python/pystate.c

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,6 +1373,28 @@ PyModuleObject* _PyInterpreterState_GetModuleState(PyObject *mod) {
13731373
PyModuleObject *self = (PyModuleObject*) mod;
13741374

13751375
if (!PyDict_Contains(is->mutable_modules, self->md_name)) {
1376+
// Get the `sys.modules` reference
1377+
PyObject* modules = _PyImport_GetModulesRef(is);
1378+
if (modules == Py_None) {
1379+
return NULL;
1380+
}
1381+
1382+
PyObject *modules_mod = NULL;
1383+
if (PyDict_GetItemRef(modules, self->md_name, &modules_mod) < 0) {
1384+
return 0;
1385+
}
1386+
if (modules_mod == mod) {
1387+
// The modules in `sys.modules` is this frozen mod but there is
1388+
// no mutable state in `sys.mut_modules`, probably because it was
1389+
// unimported? Either way:
1390+
// Remove `mod` from `sys.modules` and trigger a reimport.
1391+
if (PyDict_DelItem(modules, self->md_name) < 0) {
1392+
Py_DECREF(modules_mod);
1393+
return NULL;
1394+
}
1395+
}
1396+
Py_XDECREF(modules_mod);
1397+
13761398
// Importing the module will import the module or return the already
13771399
// imported instance in `sys.modules`.
13781400
PyObject *local_mod = PyImport_Import(self->md_name);
@@ -1392,10 +1414,6 @@ PyModuleObject* _PyInterpreterState_GetModuleState(PyObject *mod) {
13921414
}
13931415

13941416
// Place immutable proxy in `sys.modules[dict]`
1395-
PyObject* modules = _PyImport_GetModulesRef(is);
1396-
if (modules == Py_None) {
1397-
return NULL;
1398-
}
13991417
res = PyDict_SetItem(modules, self->md_name, _PyObject_CAST(self));
14001418
if (res != 0) {
14011419
return NULL;

0 commit comments

Comments
 (0)