Skip to content

Conversation

@VanshAgarwal24036
Copy link
Contributor

@VanshAgarwal24036 VanshAgarwal24036 commented Jan 18, 2026

This PR splits the datetime tests into two groups:

  • Pure-Python tests that do not require _datetime
  • Fast C-accelerated tests that require _datetime

The _Fast tests are only loaded when _datetime is available, allowing
the test suite to run correctly on builds where _datetime is not present.

No test behavior is changed.

@VanshAgarwal24036
Copy link
Contributor Author

Test-only change; no user-visible impact, so no NEWS entry is needed.

@serhiy-storchaka serhiy-storchaka added skip news needs backport to 3.13 bugs and security fixes needs backport to 3.14 bugs and security fixes labels Jan 20, 2026
fast_tests = import_fresh_module(TESTS,
fresh=['datetime', '_strptime'],
blocked=['_pydatetime'])
pure_tests = import_fresh_module(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can now restore the old formatting.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — I’ve restored the original formatting.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not restored yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ve now restored the original structure by keeping the two import_fresh_module() calls adjacent and moved the _datetime availability check outside that block.

@vstinner
Copy link
Member

It's not easy to test this change on CPython since _datetime is now a built-in module.

I tried to build _datetime as a shared extension:

diff --git a/Modules/Setup b/Modules/Setup
index 7d816ead843..828368cf7c2 100644
--- a/Modules/Setup
+++ b/Modules/Setup
@@ -126,14 +126,14 @@ PYTHONPATH=$(COREPYTHONPATH)
 # modules are to be built as shared libraries (see above for more
 # detail; also note that *static* or *disabled* cancels this effect):
 
-#*shared*
+*shared*
 
 # Modules that should always be present (POSIX and Windows):
 
 #_asyncio _asynciomodule.c
 #_bisect _bisectmodule.c
 #_csv _csv.c
-#_datetime _datetimemodule.c
+_datetime _datetimemodule.c
 #_decimal _decimal/_decimal.c
 #_heapq _heapqmodule.c
 #_interpchannels _interpchannelsmodule.c
diff --git a/Modules/Setup.bootstrap.in b/Modules/Setup.bootstrap.in
index 65a1fefe72e..3e7344382da 100644
--- a/Modules/Setup.bootstrap.in
+++ b/Modules/Setup.bootstrap.in
@@ -13,7 +13,7 @@ _signal signalmodule.c
 _tracemalloc _tracemalloc.c
 _suggestions _suggestions.c
 # needs libm and on some platforms librt
-_datetime _datetimemodule.c
+#_datetime _datetimemodule.c
 
 # modules used by importlib, deepfreeze, freeze, runpy, and sysconfig
 _codecs _codecsmodule.c

But the build fails with:

/usr/bin/ld: Python/pylifecycle.o: in function `pycore_init_types':
/home/vstinner/python/main/Python/pylifecycle.c:779:(.text+0x9db): undefined reference to `_PyDateTime_InitTypes'

I tested the change with:

diff --git a/Lib/test/test_datetime.py b/Lib/test/test_datetime.py
index 32556bcb184..5b6d52859e2 100644
--- a/Lib/test/test_datetime.py
+++ b/Lib/test/test_datetime.py
@@ -9,6 +9,7 @@
 def load_tests(loader, tests, pattern):
     try:
         try:
+            raise ImportError
             import _datetime
         except ImportError:
             has_datetime_ext = False

Output:

...
test_tricky (test.datetimetester.TestTimezoneConversions_Pure.test_tricky) ... ok
test_folds (test.datetimetester.ZoneInfoTest_Pure.test_folds) ... ok
test_gaps (test.datetimetester.ZoneInfoTest_Pure.test_gaps) ... ok
test_system_transitions (test.datetimetester.ZoneInfoTest_Pure.test_system_transitions) ... ok
setUpClass (test.datetimetester.CapiTest_Fast) ... skipped 'requires _datetime module'
setUpClass (test.datetimetester.ExtensionModuleTests_Fast) ... skipped 'requires _datetime module'
setUpClass (test.datetimetester.IranTest_Fast) ... skipped 'requires _datetime module'
setUpClass (test.datetimetester.Oddballs_Fast) ... skipped 'requires _datetime module'
setUpClass (test.datetimetester.TestDate_Fast) ... skipped 'requires _datetime module'
setUpClass (test.datetimetester.TestDateOnly_Fast) ... skipped 'requires _datetime module'
setUpClass (test.datetimetester.TestDateTime_Fast) ... skipped 'requires _datetime module'
setUpClass (test.datetimetester.TestDateTimeTZ_Fast) ... skipped 'requires _datetime module'
setUpClass (test.datetimetester.TestLocalTimeDisambiguation_Fast) ... skipped 'requires _datetime module'
setUpClass (test.datetimetester.TestModule_Fast) ... skipped 'requires _datetime module'
setUpClass (test.datetimetester.TestSubclassDateTime_Fast) ... skipped 'requires _datetime module'
setUpClass (test.datetimetester.TestTZInfo_Fast) ... skipped 'requires _datetime module'
setUpClass (test.datetimetester.TestTime_Fast) ... skipped 'requires _datetime module'
setUpClass (test.datetimetester.TestTimeDelta_Fast) ... skipped 'requires _datetime module'
setUpClass (test.datetimetester.TestTimeTZ_Fast) ... skipped 'requires _datetime module'
setUpClass (test.datetimetester.TestTimeZone_Fast) ... skipped 'requires _datetime module'
setUpClass (test.datetimetester.TestTimezoneConversions_Fast) ... skipped 'requires _datetime module'
setUpClass (test.datetimetester.ZoneInfoTest_Fast) ... skipped 'requires _datetime module'
...

@VanshAgarwal24036
Copy link
Contributor Author

Thanks for testing this, as well as the explanation.

Yes, the intention is just this behavior: when _datetime is notimportable, all Fast tests are skipped once at the class level, while Pure tests are imported. To identify tests are still run. I agree that the simulation of the IMPORTERROR, or at least what would raise an IMPORTERROR practical way to exercise this right.

@vstinner
Copy link
Member

I hacked Python to build again _datetime as an extension module and I removed ./build/lib.linux-x86_64-3.15/_datetime.cpython-315d-x86_64-linux-gnu.so.

Result: test_datetime fails.

0:00:00 load avg: 1.15 [1/1] test_datetime
Failed to call load_tests:
Traceback (most recent call last):
  File "/home/vstinner/python/main/Lib/unittest/loader.py", line 113, in loadTestsFromModule
    return load_tests(self, tests, pattern)
  File "/home/vstinner/python/main/Lib/test/test_datetime.py", line 38, in load_tests
    for name, cls in module.__dict__.items():
                     ^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute '__dict__'. Did you mean: '__dir__'?

test test_datetime crashed -- Traceback (most recent call last):
  File "/home/vstinner/python/main/Lib/test/libregrtest/single.py", line 210, in _runtest_env_changed_exc
    _load_run_test(result, runtests)
    ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
  File "/home/vstinner/python/main/Lib/test/libregrtest/single.py", line 165, in _load_run_test
    regrtest_runner(result, test_func, runtests)
    ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/vstinner/python/main/Lib/test/libregrtest/single.py", line 118, in regrtest_runner
    test_result = test_func()
  File "/home/vstinner/python/main/Lib/test/libregrtest/single.py", line 162, in test_func
    return run_unittest(test_mod, runtests)
  File "/home/vstinner/python/main/Lib/test/libregrtest/single.py", line 38, in run_unittest
    raise Exception("errors while loading tests")
Exception: errors while loading tests

0:00:00 load avg: 1.15 [1/1/1] test_datetime failed (uncaught exception)

I'm not convinced that this change works as expected.

@vstinner
Copy link
Member

My hack to build _datetime as a shared extension:

Details
diff --git a/Modules/Setup b/Modules/Setup
index 7d816ead843..828368cf7c2 100644
--- a/Modules/Setup
+++ b/Modules/Setup
@@ -126,14 +126,14 @@ PYTHONPATH=$(COREPYTHONPATH)
 # modules are to be built as shared libraries (see above for more
 # detail; also note that *static* or *disabled* cancels this effect):
 
-#*shared*
+*shared*
 
 # Modules that should always be present (POSIX and Windows):
 
 #_asyncio _asynciomodule.c
 #_bisect _bisectmodule.c
 #_csv _csv.c
-#_datetime _datetimemodule.c
+_datetime _datetimemodule.c
 #_decimal _decimal/_decimal.c
 #_heapq _heapqmodule.c
 #_interpchannels _interpchannelsmodule.c
diff --git a/Modules/Setup.bootstrap.in b/Modules/Setup.bootstrap.in
index 65a1fefe72e..3e7344382da 100644
--- a/Modules/Setup.bootstrap.in
+++ b/Modules/Setup.bootstrap.in
@@ -13,7 +13,7 @@ _signal signalmodule.c
 _tracemalloc _tracemalloc.c
 _suggestions _suggestions.c
 # needs libm and on some platforms librt
-_datetime _datetimemodule.c
+#_datetime _datetimemodule.c
 
 # modules used by importlib, deepfreeze, freeze, runpy, and sysconfig
 _codecs _codecsmodule.c
diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c
index 8f64e572bd6..754e7c7da22 100644
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -7690,6 +7690,12 @@ static PyModuleDef datetimemodule = {
 PyMODINIT_FUNC
 PyInit__datetime(void)
 {
+    PyInterpreterState *interp = PyInterpreterState_Get();
+    PyStatus status = _PyDateTime_InitTypes(interp);
+    if (_PyStatus_EXCEPTION(status)) {
+        Py_ExitStatusException(status);
+    }
+
     return PyModuleDef_Init(&datetimemodule);
 }
 
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 88dbdb6d139..c7fdd3dfd27 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -776,11 +776,6 @@ pycore_init_types(PyInterpreterState *interp)
         return status;
     }
 
-    status = _PyDateTime_InitTypes(interp);
-    if (_PyStatus_EXCEPTION(status)) {
-        return status;
-    }
-
     return _PyStatus_OK();
 }
 

@serhiy-storchaka
Copy link
Member

It's not easy to test this change on CPython since _datetime is now a built-in module.

This is why I have not merged this PR yet.

I consider also the idea of adding '_datetime' to the fresh list. Then the result is None and this can be easily tested. But I am not sure that it is safe to reload the _datetime module, especially in older Python versions.

@VanshAgarwal24036
Copy link
Contributor Author

Thanks for investigating this and for conducting such an in-depth experiment.

My plan would be to decide whether there should be Fast tests in the function load_tests().
time, not later during execution. If _datetime is not importable, the Fast test module is simply not created or iterated over at all, and only the Pure tests are loaded.

This prevents us from having to use the skip() within setUpClass(). It load_tests() never sees a partially or incompletely initialized module, Which is what resulted in the NoneType error you encountered. Other
words, presence of _datetime determines whether test suite of Fast is executed otherwise is generated in the first place.

Does this approach look OK to you?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting review needs backport to 3.13 bugs and security fixes needs backport to 3.14 bugs and security fixes skip news tests Tests in the Lib/test dir

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants