@@ -428,6 +428,71 @@ PyImport_GetMagicTag(void)
428
428
}
429
429
430
430
431
+ /*
432
+ We support a number of kinds of single-phase init builtin/extension modules:
433
+
434
+ * "basic"
435
+ * no module state (PyModuleDef.m_size == -1)
436
+ * does not support repeated init (we use PyModuleDef.m_base.m_copy)
437
+ * may have process-global state
438
+ * the module's def is cached in _PyRuntime.imports.extensions,
439
+ by (name, filename)
440
+ * "reinit"
441
+ * no module state (PyModuleDef.m_size == 0)
442
+ * supports repeated init (m_copy is never used)
443
+ * should not have any process-global state
444
+ * its def is never cached in _PyRuntime.imports.extensions
445
+ (except, currently, under the main interpreter, for some reason)
446
+ * "with state" (almost the same as reinit)
447
+ * has module state (PyModuleDef.m_size > 0)
448
+ * supports repeated init (m_copy is never used)
449
+ * should not have any process-global state
450
+ * its def is never cached in _PyRuntime.imports.extensions
451
+ (except, currently, under the main interpreter, for some reason)
452
+
453
+ There are also variants within those classes:
454
+
455
+ * two or more modules share a PyModuleDef
456
+ * a module's init func uses another module's PyModuleDef
457
+ * a module's init func calls another's module's init func
458
+ * a module's init "func" is actually a variable statically initialized
459
+ to another module's init func
460
+ * two or modules share "methods"
461
+ * a module's init func copies another module's PyModuleDef
462
+ (with a different name)
463
+ * (basic-only) two or modules share process-global state
464
+
465
+ In the first case, where modules share a PyModuleDef, the following
466
+ notable weirdness happens:
467
+
468
+ * the module's __name__ matches the def, not the requested name
469
+ * the last module (with the same def) to be imported for the first time wins
470
+ * returned by PyState_Find_Module() (via interp->modules_by_index)
471
+ * (non-basic-only) its init func is used when re-loading any of them
472
+ (via the def's m_init)
473
+ * (basic-only) the copy of its __dict__ is used when re-loading any of them
474
+ (via the def's m_copy)
475
+
476
+ However, the following happens as expected:
477
+
478
+ * a new module object (with its own __dict__) is created for each request
479
+ * the module's __spec__ has the requested name
480
+ * the loaded module is cached in sys.modules under the requested name
481
+ * the m_index field of the shared def is not changed,
482
+ so at least PyState_FindModule() will always look in the same place
483
+
484
+ For "basic" modules there are other quirks:
485
+
486
+ * (whether sharing a def or not) when loaded the first time,
487
+ m_copy is set before _init_module_attrs() is called
488
+ in importlib._bootstrap.module_from_spec(),
489
+ so when the module is re-loaded, the previous value
490
+ for __wpec__ (and others) is reset, possibly unexpectedly.
491
+
492
+ Generally, when multiple interpreters are involved, some of the above
493
+ gets even messier.
494
+ */
495
+
431
496
/* Magic for extension modules (built-in as well as dynamically
432
497
loaded). To prevent initializing an extension module more than
433
498
once, we keep a static dictionary 'extensions' keyed by the tuple
0 commit comments