Skip to content

Commit 43d4990

Browse files
authored
Add time-machine support for faster time freezing in tests (#2555)
1 parent 99727ce commit 43d4990

8 files changed

Lines changed: 144 additions & 15 deletions

File tree

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ test = [
8888
"pytest-cov>=5",
8989
"pytest-mock>=3.14",
9090
"pytest-xdist>=3.3.1",
91-
"setuptools; python_version<'3.10'", # PyCharm debugger needs it
91+
"setuptools; python_version<'3.10'", # PyCharm debugger needs it
92+
"time-machine>=3.1; python_version>='3.10'",
9293
{ include-group = "coverage" },
9394
]
9495
type = [

tests/conftest.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
from __future__ import annotations
44

55
import inspect
6-
from typing import TYPE_CHECKING, Protocol
6+
import sys
7+
from typing import TYPE_CHECKING, Any, Protocol
78

89
import pytest
910
from inline_snapshot import external_file, register_format_alias
@@ -14,6 +15,37 @@
1415
from collections.abc import Callable
1516
from pathlib import Path
1617

18+
if sys.version_info >= (3, 10):
19+
from datetime import datetime, timezone
20+
21+
import time_machine
22+
23+
def _parse_time_string(time_str: str) -> datetime:
24+
"""Parse time string to datetime with UTC timezone."""
25+
for fmt in (
26+
"%Y-%m-%dT%H:%M:%S%z",
27+
"%Y-%m-%d %H:%M:%S%z",
28+
"%Y-%m-%dT%H:%M:%S",
29+
"%Y-%m-%d %H:%M:%S",
30+
"%Y-%m-%d",
31+
):
32+
try:
33+
dt = datetime.strptime(time_str, fmt) # noqa: DTZ007
34+
if dt.tzinfo is None:
35+
dt = dt.replace(tzinfo=timezone.utc)
36+
return dt # noqa: TRY300
37+
except ValueError: # noqa: PERF203
38+
continue
39+
return datetime.fromisoformat(time_str.replace("Z", "+00:00")) # pragma: no cover
40+
41+
def freeze_time(time_to_freeze: str, **kwargs: Any) -> time_machine.travel: # noqa: ARG001
42+
"""Freeze time using time-machine (100-200x faster than freezegun)."""
43+
dt = _parse_time_string(time_to_freeze)
44+
return time_machine.travel(dt, tick=False)
45+
46+
else:
47+
from freezegun import freeze_time as freeze_time # noqa: PLC0414
48+
1749

1850
def _normalize_line_endings(text: str) -> str:
1951
"""Normalize line endings to LF for cross-platform comparison."""
@@ -200,3 +232,13 @@ def _inline_snapshot_file_formats() -> None:
200232
def min_version() -> str:
201233
"""Return minimum Python version as string."""
202234
return f"3.{MIN_VERSION}"
235+
236+
237+
@pytest.fixture(scope="session", autouse=True)
238+
def _preload_heavy_modules() -> None:
239+
"""Pre-import heavy modules once per session to warm up the import cache.
240+
241+
This reduces per-test overhead when running with pytest-xdist,
242+
as each worker only pays the import cost once at session start.
243+
"""
244+
import datamodel_code_generator # noqa: PLC0415, F401

tests/main/conftest.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@
1010
from typing import Literal
1111

1212
import pytest
13-
from freezegun import freeze_time
1413

1514
from datamodel_code_generator.__main__ import Exit, main
16-
from tests.conftest import AssertFileContent, assert_directory_content, assert_output
15+
from tests.conftest import AssertFileContent, assert_directory_content, assert_output, freeze_time
1716

1817
InputFileTypeLiteral = Literal["auto", "openapi", "jsonschema", "json", "yaml", "dict", "csv", "graphql"]
1918
CopyFilesMapping = Sequence[tuple[Path, Path]]

tests/main/jsonschema/test_main_jsonschema.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
import black
1212
import pytest
13-
from freezegun import freeze_time
1413
from packaging import version
1514

1615
from datamodel_code_generator import (
@@ -22,7 +21,7 @@
2221
generate,
2322
)
2423
from datamodel_code_generator.__main__ import Exit, main
25-
from tests.conftest import assert_directory_content
24+
from tests.conftest import assert_directory_content, freeze_time
2625
from tests.main.conftest import (
2726
DATA_PATH,
2827
JSON_SCHEMA_DATA_PATH,

tests/main/openapi/test_main_openapi.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import black
1313
import pydantic
1414
import pytest
15-
from freezegun import freeze_time
1615
from packaging import version
1716

1817
from datamodel_code_generator import (
@@ -27,7 +26,7 @@
2726
inferred_message,
2827
)
2928
from datamodel_code_generator.__main__ import Exit
30-
from tests.conftest import assert_directory_content
29+
from tests.conftest import assert_directory_content, freeze_time
3130
from tests.main.conftest import (
3231
DATA_PATH,
3332
OPEN_API_DATA_PATH,

tests/main/test_main_general.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from typing import TYPE_CHECKING
77

88
import pytest
9-
from freezegun import freeze_time
109

1110
from datamodel_code_generator import (
1211
DataModelType,
@@ -16,7 +15,7 @@
1615
)
1716
from datamodel_code_generator.__main__ import Config, Exit
1817
from datamodel_code_generator.format import PythonVersion
19-
from tests.conftest import create_assert_file_content
18+
from tests.conftest import create_assert_file_content, freeze_time
2019
from tests.main.conftest import (
2120
DATA_PATH,
2221
EXPECTED_MAIN_PATH,

tests/test_main_kr.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@
77

88
import black
99
import pytest
10-
from freezegun import freeze_time
1110

1211
from datamodel_code_generator import MIN_VERSION, chdir, inferred_message
1312
from datamodel_code_generator.__main__ import Exit
14-
from tests.conftest import create_assert_file_content
13+
from tests.conftest import create_assert_file_content, freeze_time
1514
from tests.main.conftest import run_main_and_assert, run_main_with_args
1615

1716
DATA_PATH: Path = Path(__file__).parent / "data"

0 commit comments

Comments
 (0)