Skip to content

Commit 3d604ad

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 3d604ad

File tree

3 files changed

+45
-12
lines changed

3 files changed

+45
-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: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,16 @@
1515

1616
from pendulum.constants import DAYS_PER_MONTHS
1717
from pendulum.day import WeekDay
18-
from pendulum.formatting.difference_formatter import DifferenceFormatter
1918
from pendulum.locales.locale import Locale
2019

2120

2221
if TYPE_CHECKING:
2322
# Prevent import cycles
2423
from pendulum.duration import Duration
2524

25+
# LazyLoad
26+
from pendulum.formatting.difference_formatter import DifferenceFormatter
27+
2628
with_extensions = os.getenv("PENDULUM_EXTENSIONS", "1") == "1"
2729

2830
_DT = TypeVar("_DT", bound=datetime)
@@ -48,7 +50,7 @@
4850
from pendulum._helpers import precise_diff # type: ignore[assignment]
4951
from pendulum._helpers import week_day
5052

51-
difference_formatter = DifferenceFormatter()
53+
difference_formatter: DifferenceFormatter
5254

5355

5456
@overload
@@ -164,7 +166,7 @@ def format_diff(
164166
if locale is None:
165167
locale = get_locale()
166168

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

169171

170172
def _sign(x: float) -> int:
@@ -202,6 +204,17 @@ def week_ends_at(wday: WeekDay) -> None:
202204
pendulum._WEEK_ENDS_AT = wday
203205

204206

207+
def __getattr__(name: str):
208+
if name == "difference_formatter":
209+
# Lazily create
210+
from pendulum.formatting.difference_formatter import DifferenceFormatter
211+
212+
global difference_formatter
213+
difference_formatter = DifferenceFormatter()
214+
return difference_formatter
215+
raise AttributeError(name)
216+
217+
205218
__all__ = [
206219
"PreciseDiff",
207220
"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)