@@ -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