Skip to content

Commit 9879c07

Browse files
committed
Working on global freezing
Signed-off-by: Matthew A Johnson <matjoh@microsoft.com>
1 parent 0428f05 commit 9879c07

9 files changed

Lines changed: 77 additions & 31 deletions

Include/internal/pycore_global_objects_fini_generated.h

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_global_strings.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,12 @@ struct _Py_global_strings {
4848
STRUCT_FOR_STR(json_decoder, "json.decoder")
4949
STRUCT_FOR_STR(kwdefaults, ".kwdefaults")
5050
STRUCT_FOR_STR(list_err, "list index out of range")
51+
STRUCT_FOR_STR(namedtuple, "namedtuple")
5152
STRUCT_FOR_STR(newline, "\n")
5253
STRUCT_FOR_STR(open_br, "{")
5354
STRUCT_FOR_STR(percent, "%")
5455
STRUCT_FOR_STR(shim_name, "<shim>")
56+
STRUCT_FOR_STR(startswith, "startswith")
5557
STRUCT_FOR_STR(type_params, ".type_params")
5658
STRUCT_FOR_STR(utf_8, "utf-8")
5759
} literals;

Include/internal/pycore_runtime_init_generated.h

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_freezeglobals.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import unittest
2+
3+
4+
class TestFreezeGlobals(unittest.TestCase):
5+
def test_freeze_globals(self):
6+
freezeglobals()
7+
8+
if __name__ == "__main__":
9+
unittest.main()
10+
# Test the freezeglobals function

Objects/longobject.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1775,6 +1775,7 @@ long_to_decimal_string_internal(PyObject *aa,
17751775
}
17761776
size_a = _PyLong_DigitCount(a);
17771777
negative = _PyLong_IsNegative(a);
1778+
kind = 0;
17781779

17791780
/* quick and dirty pre-check for overflowing the decimal digit limit,
17801781
based on the inequality 10/3 >= log2(10)
@@ -2006,6 +2007,7 @@ long_format_binary(PyObject *aa, int base, int alternate,
20062007
}
20072008
size_a = _PyLong_DigitCount(a);
20082009
negative = _PyLong_IsNegative(a);
2010+
kind = 0;
20092011

20102012
/* Compute a rough upper bound for the length of the string */
20112013
switch (base) {

Objects/moduleobject.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,8 +601,10 @@ void
601601
_PyModule_Clear(PyObject *m)
602602
{
603603
PyObject *d = ((PyModuleObject *)m)->md_dict;
604-
if (d != NULL)
604+
if (d != NULL){
605+
_Py_ClearImmutable(d);
605606
_PyModule_ClearDict(d);
607+
}
606608
}
607609

608610
void

Python/immutability.c

Lines changed: 52 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -111,20 +111,27 @@ static PyObject* walk_function(PyObject* op, stack* frontier)
111111

112112
_Py_SetImmutable(op);
113113

114-
115114
f = (PyFunctionObject*)op;
116115

116+
117117
globals = f->func_globals;
118118
builtins = f->func_builtins;
119119

120-
if(PyUnicode_CompareWithASCIIString(f->func_module, "_frozen_importlib") == 0){
121-
// we don't want to freeze the importlib module
122-
Py_RETURN_NONE;
123-
}
124-
125120
module = PyImport_Import(f->func_module);
126121
if(module == NULL){
127-
return module;
122+
// clear the exception so we can check if the module is a namedtuple
123+
PyObject* exc = PyErr_GetRaisedException();
124+
_Py_DECLARE_STR(namedtuple, "namedtuple");
125+
_Py_DECLARE_STR(startswith, "startswith");
126+
PyObject* res = PyObject_CallMethodOneArg(f->func_module, &_Py_STR(startswith), &_Py_STR(namedtuple));
127+
if(Py_IsTrue(res)){
128+
// namedtuple creates a fake module, which cannot be imported
129+
Py_RETURN_NONE;
130+
}
131+
132+
// not a namedtuple, so we need to set the exception
133+
PyErr_SetRaisedException(exc);
134+
return NULL;
128135
}
129136

130137
if(PyModule_Check(module)){
@@ -294,18 +301,6 @@ static PyObject* walk_function(PyObject* op, stack* frontier)
294301

295302
static int _freeze_visit(PyObject* obj, void* frontier)
296303
{
297-
if(PyModule_Check(obj)){
298-
const char* name = PyModule_GetName(obj);
299-
300-
if(strcmp(name, "_frozen_importlib_external") == 0){
301-
return 0;
302-
}
303-
304-
if(strcmp(name, "_frozen_importlib") == 0){
305-
return 0;
306-
}
307-
}
308-
309304
if(!_Py_IsImmutable(obj)){
310305
if(stack_push((stack*)frontier, obj)){
311306
PyErr_NoMemory();
@@ -332,9 +327,39 @@ PyObject* _Py_Freeze(PyObject* obj)
332327
return PyErr_NoMemory();
333328
}
334329

330+
PyObject* frozen_importlib = PyImport_ImportModule("_frozen_importlib");
331+
if(frozen_importlib == NULL){
332+
stack_free(frontier);
333+
return NULL;
334+
}
335+
336+
PyObject* blocking_on = PyObject_GetAttrString(frozen_importlib, "_blocking_on");
337+
if(blocking_on == NULL){
338+
Py_DECREF(frozen_importlib);
339+
stack_free(frontier);
340+
return NULL;
341+
}
342+
343+
PyObject* module_locks = PyObject_GetAttrString(frozen_importlib, "_module_locks");
344+
if(module_locks == NULL){
345+
Py_DECREF(blocking_on);
346+
Py_DECREF(frozen_importlib);
347+
stack_free(frontier);
348+
return NULL;
349+
}
350+
351+
Py_DECREF(frozen_importlib);
352+
335353
while(!stack_empty(frontier)){
336354
PyObject* item = stack_pop(frontier); // item.rc = x + 1
337355

356+
if(item == blocking_on ||
357+
item == module_locks){
358+
// the module lock and blocking on dictionaries must remain mutable or else
359+
// we will not be able to import modules
360+
continue;
361+
}
362+
338363
PyTypeObject* type = Py_TYPE(item);
339364
traverseproc traverse;
340365
PyObject* type_op = NULL;
@@ -354,6 +379,8 @@ PyObject* _Py_Freeze(PyObject* obj)
354379
if(PyFunction_Check(item)){
355380
PyObject* err = walk_function(item, frontier);
356381
if(!Py_IsNone(err)){
382+
Py_DECREF(blocking_on);
383+
Py_DECREF(module_locks);
357384
stack_free(frontier);
358385
return err;
359386
}
@@ -363,6 +390,8 @@ PyObject* _Py_Freeze(PyObject* obj)
363390
traverse = type->tp_traverse;
364391
if(traverse != NULL){
365392
if(traverse(item, (visitproc)_freeze_visit, frontier)){
393+
Py_DECREF(blocking_on);
394+
Py_DECREF(module_locks);
366395
stack_free(frontier);
367396
return NULL;
368397
}
@@ -373,12 +402,16 @@ PyObject* _Py_Freeze(PyObject* obj)
373402
if (!_Py_IsImmutable(type_op)){
374403
if (stack_push(frontier, type_op))
375404
{
405+
Py_DECREF(blocking_on);
406+
Py_DECREF(module_locks);
376407
stack_free(frontier);
377408
return PyErr_NoMemory();
378409
}
379410
}
380411
}
381412

413+
Py_DECREF(blocking_on);
414+
Py_DECREF(module_locks);
382415
stack_free(frontier);
383416

384417
Py_RETURN_NONE;
@@ -390,22 +423,12 @@ PyObject* _Py_FreezeGlobals()
390423
PyObject* main_dict;
391424
PyInterpreterState* main;
392425

393-
// we will need to exclude importlib from this process
394-
PyObject* importlib = PyUnicode_FromString("importlib");
395-
if(importlib == NULL){
396-
return NULL;
397-
}
398-
399426
// go module by module and freeze their global dictionaries
400427
PyObject* modules = PyImport_GetModuleDict();
401428
Py_ssize_t size = PyDict_Size(modules);
402429
PyObject* keys = PyDict_Keys(modules);
403430
for(Py_ssize_t i = 0; i < size; i++){
404431
PyObject* key = PyList_GetItem(keys, i);
405-
if (PyUnicode_Contains(key, importlib)){
406-
continue;
407-
}
408-
409432
PyObject* module = PyDict_GetItem(modules, key);
410433
PyObject* globals = PyModule_GetDict(module);
411434
ret = _Py_Freeze(globals);
@@ -416,7 +439,6 @@ PyObject* _Py_FreezeGlobals()
416439
}
417440

418441
Py_DECREF(keys);
419-
Py_DECREF(importlib);
420442

421443
main = PyInterpreterState_Main();
422444
if(main == NULL){

Python/pylifecycle.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,6 +1340,7 @@ finalize_modules_delete_special(PyThreadState *tstate, int verbose)
13401340
};
13411341

13421342
PyInterpreterState *interp = tstate->interp;
1343+
_Py_ClearImmutable(interp->builtins);
13431344
if (verbose) {
13441345
PySys_WriteStderr("# clear builtins._\n");
13451346
}

Python/pythonrun.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,9 @@ void
842842
PyErr_PrintEx(int set_sys_last_vars)
843843
{
844844
PyThreadState *tstate = _PyThreadState_GET();
845+
if(set_sys_last_vars){
846+
_Py_ClearImmutable(tstate->interp->sysdict);
847+
}
845848
_PyErr_PrintEx(tstate, set_sys_last_vars);
846849
}
847850

0 commit comments

Comments
 (0)