diff --git a/src/pendulum/__init__.py b/src/pendulum/__init__.py index ba9a0cb9..bf2b4972 100644 --- a/src/pendulum/__init__.py +++ b/src/pendulum/__init__.py @@ -2,6 +2,8 @@ import datetime as _datetime +from functools import cache +from typing import TYPE_CHECKING from typing import Any from typing import Union from typing import cast @@ -30,7 +32,6 @@ from pendulum.helpers import week_starts_at from pendulum.interval import Interval from pendulum.parser import parse as parse -from pendulum.testing.traveller import Traveller from pendulum.time import Time from pendulum.tz import UTC from pendulum.tz import fixed_timezone @@ -334,17 +335,43 @@ def interval( return Interval(start, end, absolute=absolute) -# Testing +if TYPE_CHECKING: + from pendulum.testing.traveller import Traveller -_traveller = Traveller(DateTime) + _traveller = Traveller(DateTime) + freeze = _traveller.freeze + travel = _traveller.travel + travel_to = _traveller.travel_to + travel_back = _traveller.travel_back +else: + # We do this in an if-not-typing block so we don't have to duplicate the function signatures. + @cache + def _traveller() -> Traveller: + # Lazy load this, so we don't eagerly load Pytest if we don't need to + from pendulum.testing.traveller import Traveller -freeze = _traveller.freeze -travel = _traveller.travel -travel_to = _traveller.travel_to -travel_back = _traveller.travel_back + return Traveller(DateTime) + + def freeze(*args, **kwargs) -> Traveller: + return _traveller().freeze(*args, **kwargs) + + def travel(*args, **kwargs): + _traveller().travel(*args, **kwargs) + + def travel_to(*args, **kwargs): + _traveller().travel_to(*args, **kwargs) + + def travel_back(*args, **kwargs): + _traveller().travel_back(*args, **kwargs) def __getattr__(name: str) -> Any: + if name == "Traveller": + # This wasn't in `__all__`, but it was defined before, so keep it for back compat + from pendulum.testing.traveller import Traveller + + return Traveller + if name == "__version__": import importlib.metadata import warnings diff --git a/src/pendulum/helpers.py b/src/pendulum/helpers.py index e9424d90..db42b31e 100644 --- a/src/pendulum/helpers.py +++ b/src/pendulum/helpers.py @@ -6,8 +6,10 @@ from datetime import date from datetime import datetime from datetime import timedelta +from functools import cache from math import copysign from typing import TYPE_CHECKING +from typing import Any from typing import TypeVar from typing import overload @@ -15,7 +17,6 @@ from pendulum.constants import DAYS_PER_MONTHS from pendulum.day import WeekDay -from pendulum.formatting.difference_formatter import DifferenceFormatter from pendulum.locales.locale import Locale @@ -23,6 +24,9 @@ # Prevent import cycles from pendulum.duration import Duration + # LazyLoaded + from pendulum.formatting.difference_formatter import DifferenceFormatter + with_extensions = os.getenv("PENDULUM_EXTENSIONS", "1") == "1" _DT = TypeVar("_DT", bound=datetime) @@ -48,7 +52,7 @@ from pendulum._helpers import precise_diff # type: ignore[assignment] from pendulum._helpers import week_day -difference_formatter = DifferenceFormatter() +difference_formatter: DifferenceFormatter @overload @@ -164,7 +168,7 @@ def format_diff( if locale is None: locale = get_locale() - return difference_formatter.format(diff, is_now, absolute, locale) + return _difference_formatter().format(diff, is_now, absolute, locale) def _sign(x: float) -> int: @@ -202,6 +206,19 @@ def week_ends_at(wday: WeekDay) -> None: pendulum._WEEK_ENDS_AT = wday +@cache +def _difference_formatter() -> DifferenceFormatter: + from pendulum.formatting.difference_formatter import DifferenceFormatter + + return DifferenceFormatter() + + +def __getattr__(name: str) -> Any: + if name == "difference_formatter": + return _difference_formatter() + raise AttributeError(name) + + __all__ = [ "PreciseDiff", "add_duration", diff --git a/src/pendulum/locales/locale.py b/src/pendulum/locales/locale.py index d396ecbc..951ba8be 100644 --- a/src/pendulum/locales/locale.py +++ b/src/pendulum/locales/locale.py @@ -2,7 +2,6 @@ import re -from importlib import import_module, resources from pathlib import Path from typing import Any from typing import ClassVar @@ -24,6 +23,8 @@ def __init__(self, locale: str, data: Any) -> None: @classmethod def load(cls, locale: str | Locale) -> Locale: + from importlib import import_module, resources + if isinstance(locale, Locale): return locale