Skip to content

Commit 6b86f50

Browse files
committed
Don't eagerly load the Locale, nor pytest until it's needed.
The main problem here was that importing pendulum.testing.traveller was importing time_machine, which was also importing pytest. And creating the DifferenceFormatter at import time was also loading the locale. Both of these are "optional" in the sense that there is lots you can do in Pendulum without ever using them, so we can relatively straightforwardly delay things until they are requested. Tested by running `uv run --with time_machine -p 3.12 python -Ximporttime -c 'import pendulum'`: - Before: ~50-64ms (a lot of noise) - After: 20-29ms Not a huge slow down, but I figured it was worth it anyway, especially not not load pytest.
1 parent 2982f25 commit 6b86f50

File tree

3 files changed

+46
-12
lines changed

3 files changed

+46
-12
lines changed

src/pendulum/__init__.py

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import datetime as _datetime
44

5+
from typing import TYPE_CHECKING
56
from typing import Any
67
from typing import Union
78
from typing import cast
@@ -30,7 +31,6 @@
3031
from pendulum.helpers import week_starts_at
3132
from pendulum.interval import Interval
3233
from pendulum.parser import parse as parse
33-
from pendulum.testing.traveller import Traveller
3434
from pendulum.time import Time
3535
from pendulum.tz import UTC
3636
from pendulum.tz import fixed_timezone
@@ -42,6 +42,16 @@
4242
from pendulum.tz.timezone import Timezone
4343

4444

45+
if TYPE_CHECKING:
46+
from pendulum.testing.traveller import Traveller
47+
48+
_traveller = Traveller(DateTime)
49+
freeze = _traveller.freeze
50+
travel = _traveller.travel
51+
travel_to = _traveller.travel_to
52+
travel_back = _traveller.travel_back
53+
54+
4555
MONDAY = WeekDay.MONDAY
4656
TUESDAY = WeekDay.TUESDAY
4757
WEDNESDAY = WeekDay.WEDNESDAY
@@ -334,17 +344,26 @@ def interval(
334344
return Interval(start, end, absolute=absolute)
335345

336346

337-
# Testing
347+
def __getattr__(name: str) -> Any:
348+
# Lazy load this, so we don't eagerly load Pytest if we don't need to
349+
if name in {"freeze", "travel", "travel_to", "travel_back"}:
350+
fn = getattr(__getattr__("_traveller"), name)
351+
globals()[name] = fn
352+
return fn
338353

339-
_traveller = Traveller(DateTime)
354+
if name == "_traveller":
355+
from pendulum.testing.traveller import Traveller
340356

341-
freeze = _traveller.freeze
342-
travel = _traveller.travel
343-
travel_to = _traveller.travel_to
344-
travel_back = _traveller.travel_back
357+
_traveller = Traveller(DateTime)
358+
globals()[name] = _traveller
359+
return _traveller
360+
if name == "Traveller":
361+
# This wasn't in `__all__`, but it was defined before, so keep it for back compat
362+
from pendulum.testing.traveller import Traveller
345363

364+
globals()[name] = Traveller
365+
return Traveller
346366

347-
def __getattr__(name: str) -> Any:
348367
if name == "__version__":
349368
import importlib.metadata
350369
import warnings

src/pendulum/helpers.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,24 @@
88
from datetime import timedelta
99
from math import copysign
1010
from typing import TYPE_CHECKING
11+
from typing import Any
1112
from typing import TypeVar
1213
from typing import overload
1314

1415
import pendulum
1516

1617
from pendulum.constants import DAYS_PER_MONTHS
1718
from pendulum.day import WeekDay
18-
from pendulum.formatting.difference_formatter import DifferenceFormatter
1919
from pendulum.locales.locale import Locale
2020

2121

2222
if TYPE_CHECKING:
2323
# Prevent import cycles
2424
from pendulum.duration import Duration
2525

26+
# LazyLoad
27+
from pendulum.formatting.difference_formatter import DifferenceFormatter
28+
2629
with_extensions = os.getenv("PENDULUM_EXTENSIONS", "1") == "1"
2730

2831
_DT = TypeVar("_DT", bound=datetime)
@@ -48,7 +51,7 @@
4851
from pendulum._helpers import precise_diff # type: ignore[assignment]
4952
from pendulum._helpers import week_day
5053

51-
difference_formatter = DifferenceFormatter()
54+
difference_formatter: DifferenceFormatter
5255

5356

5457
@overload
@@ -164,7 +167,7 @@ def format_diff(
164167
if locale is None:
165168
locale = get_locale()
166169

167-
return difference_formatter.format(diff, is_now, absolute, locale)
170+
return __getattr__("difference_formatter").format(diff, is_now, absolute, locale)
168171

169172

170173
def _sign(x: float) -> int:
@@ -202,6 +205,17 @@ def week_ends_at(wday: WeekDay) -> None:
202205
pendulum._WEEK_ENDS_AT = wday
203206

204207

208+
def __getattr__(name: str) -> Any:
209+
if name == "difference_formatter":
210+
# Lazily create
211+
from pendulum.formatting.difference_formatter import DifferenceFormatter
212+
213+
global difference_formatter
214+
difference_formatter = DifferenceFormatter()
215+
return difference_formatter
216+
raise AttributeError(name)
217+
218+
205219
__all__ = [
206220
"PreciseDiff",
207221
"add_duration",

src/pendulum/locales/locale.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import re
44

5-
from importlib import import_module, resources
65
from pathlib import Path
76
from typing import Any
87
from typing import ClassVar
@@ -24,6 +23,8 @@ def __init__(self, locale: str, data: Any) -> None:
2423

2524
@classmethod
2625
def load(cls, locale: str | Locale) -> Locale:
26+
from importlib import import_module, resources
27+
2728
if isinstance(locale, Locale):
2829
return locale
2930

0 commit comments

Comments
 (0)