@@ -540,6 +540,50 @@ sync_module_clear(struct sync_module *data)
540540}
541541
542542
543+ static PyObject *
544+ get_cached_module_ns (PyThreadState * tstate ,
545+ const char * modname , const char * filename )
546+ {
547+ // Load the module from the original file.
548+ assert (filename != NULL );
549+ PyObject * loaded = NULL ;
550+
551+ const char * run_modname = modname ;
552+ if (strcmp (modname , "__main__" ) == 0 ) {
553+ // We don't want to trigger "if __name__ == '__main__':".
554+ run_modname = "<fake __main__>" ;
555+ }
556+
557+ // First try the per-interpreter cache.
558+ PyObject * interpns = PyInterpreterState_GetDict (tstate -> interp );
559+ assert (interpns != NULL );
560+ PyObject * key = PyUnicode_FromFormat ("CACHED_MODULE_NS_%s" , modname );
561+ if (key == NULL ) {
562+ return NULL ;
563+ }
564+ if (PyDict_GetItemRef (interpns , key , & loaded ) < 0 ) {
565+ goto finally ;
566+ }
567+ if (loaded != NULL ) {
568+ goto finally ;
569+ }
570+
571+ // It wasn't already loaded from file.
572+ loaded = runpy_run_path (filename , run_modname );
573+ if (loaded == NULL ) {
574+ goto finally ;
575+ }
576+ if (PyDict_SetItem (interpns , key , loaded ) < 0 ) {
577+ Py_CLEAR (loaded );
578+ goto finally ;
579+ }
580+
581+ finally :
582+ Py_DECREF (key );
583+ return loaded ;
584+ }
585+
586+
543587struct _unpickle_context {
544588 PyThreadState * tstate ;
545589 // We only special-case the __main__ module,
@@ -574,37 +618,40 @@ _unpickle_context_set_module(struct _unpickle_context *ctx,
574618 struct sync_module_result res = {0 };
575619 struct sync_module_result * cached = NULL ;
576620 const char * filename = NULL ;
577- const char * run_modname = modname ;
578621 if (strcmp (modname , "__main__" ) == 0 ) {
579622 cached = & ctx -> main .cached ;
580623 filename = ctx -> main .filename ;
581- // We don't want to trigger "if __name__ == '__main__':".
582- run_modname = "<fake __main__>" ;
583624 }
584625 else {
585626 res .failed = PyExc_NotImplementedError ;
586- goto finally ;
627+ goto error ;
587628 }
588629
589630 res .module = import_get_module (ctx -> tstate , modname );
590631 if (res .module == NULL ) {
591- res .failed = _PyErr_GetRaisedException (ctx -> tstate );
592- assert (res .failed != NULL );
593- goto finally ;
632+ goto error ;
594633 }
595634
635+ // Load the module ns from the original file and cache it.
636+ // Note that functions will use the cached ns for __globals__,
637+ // not res.module.
596638 if (filename == NULL ) {
597- Py_CLEAR (res .module );
598639 res .failed = PyExc_NotImplementedError ;
599- goto finally ;
640+ goto error ;
600641 }
601- res .loaded = runpy_run_path ( filename , run_modname );
642+ res .loaded = get_cached_module_ns ( ctx -> tstate , modname , filename );
602643 if (res .loaded == NULL ) {
603- Py_CLEAR (res .module );
644+ goto error ;
645+ }
646+ goto finally ;
647+
648+ error :
649+ Py_CLEAR (res .module );
650+ if (res .failed == NULL ) {
604651 res .failed = _PyErr_GetRaisedException (ctx -> tstate );
605652 assert (res .failed != NULL );
606- goto finally ;
607653 }
654+ assert (!_PyErr_Occurred (ctx -> tstate ));
608655
609656finally :
610657 if (cached != NULL ) {
@@ -629,7 +676,8 @@ _handle_unpickle_missing_attr(struct _unpickle_context *ctx, PyObject *exc)
629676 }
630677
631678 // Get the module.
632- struct sync_module_result mod = _unpickle_context_get_module (ctx , info .modname );
679+ struct sync_module_result mod =
680+ _unpickle_context_get_module (ctx , info .modname );
633681 if (mod .failed != NULL ) {
634682 // It must have failed previously.
635683 return -1 ;
0 commit comments