mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2025-03-14 11:43:50 +01:00
Try getting independent of pytz
This commit is contained in:
parent
8c02b05550
commit
84c93bb91a
8 changed files with 36 additions and 39 deletions
|
@ -158,7 +158,7 @@ PTB can be installed with optional dependencies:
|
|||
* ``pip install "python-telegram-bot[rate-limiter]"`` installs `aiolimiter~=1.1,<1.3 <https://aiolimiter.readthedocs.io/en/stable/>`_. Use this, if you want to use ``telegram.ext.AIORateLimiter``.
|
||||
* ``pip install "python-telegram-bot[webhooks]"`` installs the `tornado~=6.4 <https://www.tornadoweb.org/en/stable/>`_ library. Use this, if you want to use ``telegram.ext.Updater.start_webhook``/``telegram.ext.Application.run_webhook``.
|
||||
* ``pip install "python-telegram-bot[callback-data]"`` installs the `cachetools>=5.3.3,<5.6.0 <https://cachetools.readthedocs.io/en/latest/>`_ library. Use this, if you want to use `arbitrary callback_data <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Arbitrary-callback_data>`_.
|
||||
* ``pip install "python-telegram-bot[job-queue]"`` installs the `APScheduler~=3.10.4 <https://apscheduler.readthedocs.io/en/3.x/>`_ library and enforces `pytz>=2018.6 <https://pypi.org/project/pytz/>`_, where ``pytz`` is a dependency of ``APScheduler``. Use this, if you want to use the ``telegram.ext.JobQueue``.
|
||||
* ``pip install "python-telegram-bot[job-queue]"`` installs the `APScheduler~=3.10.4 <https://apscheduler.readthedocs.io/en/3.x/>`_ library. Use this, if you want to use the ``telegram.ext.JobQueue``.
|
||||
|
||||
To install multiple optional dependencies, separate them by commas, e.g. ``pip install "python-telegram-bot[socks,webhooks]"``.
|
||||
|
||||
|
|
|
@ -77,8 +77,6 @@ http2 = [
|
|||
job-queue = [
|
||||
# APS doesn't have a strict stability policy. Let's be cautious for now.
|
||||
"APScheduler>=3.10.4,<3.12.0",
|
||||
# pytz is required by APS and just needs the lower bound due to #2120
|
||||
"pytz>=2018.6",
|
||||
]
|
||||
passport = [
|
||||
"cryptography!=3.4,!=3.4.1,!=3.4.2,!=3.4.3,>=39.0.1",
|
||||
|
|
|
@ -27,6 +27,7 @@ Warning:
|
|||
user. Changes to this module are not considered breaking changes and may not be documented in
|
||||
the changelog.
|
||||
"""
|
||||
import contextlib
|
||||
import datetime as dtm
|
||||
import time
|
||||
from typing import TYPE_CHECKING, Optional, Union
|
||||
|
@ -34,22 +35,24 @@ from typing import TYPE_CHECKING, Optional, Union
|
|||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
|
||||
# pytz is only available if it was installed as dependency of APScheduler, so we make a little
|
||||
# workaround here
|
||||
DTM_UTC = dtm.timezone.utc
|
||||
UTC = dtm.timezone.utc
|
||||
try:
|
||||
import pytz
|
||||
|
||||
UTC = pytz.utc
|
||||
except ImportError:
|
||||
UTC = DTM_UTC # type: ignore[assignment]
|
||||
pytz = None # type: ignore[assignment]
|
||||
|
||||
|
||||
def _localize(datetime: dtm.datetime, tzinfo: dtm.tzinfo) -> dtm.datetime:
|
||||
"""Localize the datetime, where UTC is handled depending on whether pytz is available or not"""
|
||||
if tzinfo is DTM_UTC:
|
||||
return datetime.replace(tzinfo=DTM_UTC)
|
||||
return tzinfo.localize(datetime) # type: ignore[attr-defined]
|
||||
def localize(datetime: dtm.datetime, tzinfo: dtm.tzinfo) -> dtm.datetime:
|
||||
"""Localize the datetime, both for pytz and zoneinfo timezones."""
|
||||
if tzinfo is UTC:
|
||||
return datetime.replace(tzinfo=UTC)
|
||||
|
||||
with contextlib.suppress(AttributeError):
|
||||
# Since pytz might not be available, we need the suppress context manager
|
||||
if isinstance(tzinfo, pytz.BaseTzInfo):
|
||||
return tzinfo.localize(datetime)
|
||||
|
||||
return datetime.astimezone(tzinfo)
|
||||
|
||||
|
||||
def to_float_timestamp(
|
||||
|
@ -87,7 +90,7 @@ def to_float_timestamp(
|
|||
will be raised.
|
||||
tzinfo (:class:`datetime.tzinfo`, optional): If :paramref:`time_object` is a naive object
|
||||
from the :mod:`datetime` module, it will be interpreted as this timezone. Defaults to
|
||||
``pytz.utc``, if available, and :attr:`datetime.timezone.utc` otherwise.
|
||||
:attr:`datetime.timezone.utc` otherwise.
|
||||
|
||||
Note:
|
||||
Only to be used by ``telegram.ext``.
|
||||
|
@ -132,7 +135,7 @@ def to_float_timestamp(
|
|||
|
||||
aware_datetime = dtm.datetime.combine(reference_date, time_object)
|
||||
if aware_datetime.tzinfo is None:
|
||||
aware_datetime = _localize(aware_datetime, tzinfo)
|
||||
aware_datetime = localize(aware_datetime, tzinfo)
|
||||
|
||||
# if the time of day has passed today, use tomorrow
|
||||
if reference_time > aware_datetime.timetz():
|
||||
|
@ -140,7 +143,7 @@ def to_float_timestamp(
|
|||
return _datetime_to_float_timestamp(aware_datetime)
|
||||
if isinstance(time_object, dtm.datetime):
|
||||
if time_object.tzinfo is None:
|
||||
time_object = _localize(time_object, tzinfo)
|
||||
time_object = localize(time_object, tzinfo)
|
||||
return _datetime_to_float_timestamp(time_object)
|
||||
|
||||
raise TypeError(f"Unable to convert {type(time_object).__name__} object to timestamp")
|
||||
|
|
|
@ -57,9 +57,7 @@ class Defaults:
|
|||
versions.
|
||||
tzinfo (:class:`datetime.tzinfo`, optional): A timezone to be used for all date(time)
|
||||
inputs appearing throughout PTB, i.e. if a timezone naive date(time) object is passed
|
||||
somewhere, it will be assumed to be in :paramref:`tzinfo`. If the
|
||||
:class:`telegram.ext.JobQueue` is used, this must be a timezone provided
|
||||
by the ``pytz`` module. Defaults to ``pytz.utc``, if available, and
|
||||
somewhere, it will be assumed to be in :paramref:`tzinfo`. Defaults to
|
||||
:attr:`datetime.timezone.utc` otherwise.
|
||||
block (:obj:`bool`, optional): Default setting for the :paramref:`BaseHandler.block`
|
||||
parameter
|
||||
|
|
|
@ -23,7 +23,6 @@ import weakref
|
|||
from typing import TYPE_CHECKING, Any, Generic, Optional, Union, cast, overload
|
||||
|
||||
try:
|
||||
import pytz
|
||||
from apscheduler.executors.asyncio import AsyncIOExecutor
|
||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||
|
||||
|
@ -31,6 +30,7 @@ try:
|
|||
except ImportError:
|
||||
APS_AVAILABLE = False
|
||||
|
||||
from telegram._utils.datetime import UTC, localize
|
||||
from telegram._utils.logging import get_logger
|
||||
from telegram._utils.repr import build_repr_with_selected_attrs
|
||||
from telegram._utils.types import JSONDict
|
||||
|
@ -155,13 +155,13 @@ class JobQueue(Generic[CCT]):
|
|||
dict[:obj:`str`, :obj:`object`]: The configuration values as dictionary.
|
||||
|
||||
"""
|
||||
timezone: object = pytz.utc
|
||||
timezone: datetime.tzinfo = UTC
|
||||
if (
|
||||
self._application
|
||||
and isinstance(self.application.bot, ExtBot)
|
||||
and self.application.bot.defaults
|
||||
):
|
||||
timezone = self.application.bot.defaults.tzinfo or pytz.utc
|
||||
timezone = self.application.bot.defaults.tzinfo or UTC
|
||||
|
||||
return {
|
||||
"timezone": timezone,
|
||||
|
@ -197,8 +197,8 @@ class JobQueue(Generic[CCT]):
|
|||
datetime.datetime.now(tz=time.tzinfo or self.scheduler.timezone).date(), time
|
||||
)
|
||||
if date_time.tzinfo is None:
|
||||
date_time = self.scheduler.timezone.localize(date_time)
|
||||
if shift_day and date_time <= datetime.datetime.now(pytz.utc):
|
||||
date_time = localize(date_time, self.scheduler.timezone)
|
||||
if shift_day and date_time <= datetime.datetime.now(UTC):
|
||||
date_time += datetime.timedelta(days=1)
|
||||
return date_time
|
||||
return time
|
||||
|
|
|
@ -21,6 +21,7 @@ import datetime
|
|||
import functools
|
||||
import inspect
|
||||
import re
|
||||
import zoneinfo
|
||||
from collections.abc import Collection, Iterable
|
||||
from typing import Any, Callable, Optional
|
||||
|
||||
|
@ -46,7 +47,7 @@ from telegram.request import RequestData
|
|||
from tests.auxil.envvars import TEST_WITH_OPT_DEPS
|
||||
|
||||
if TEST_WITH_OPT_DEPS:
|
||||
import pytz
|
||||
pass
|
||||
|
||||
|
||||
FORWARD_REF_PATTERN = re.compile(r"ForwardRef\('(?P<class_name>\w+)'\)")
|
||||
|
@ -336,8 +337,8 @@ def build_kwargs(
|
|||
elif name == "until_date":
|
||||
if manually_passed_value not in [None, DEFAULT_NONE]:
|
||||
# Europe/Berlin
|
||||
kws[name] = pytz.timezone("Europe/Berlin").localize(
|
||||
datetime.datetime(2000, 1, 1, 0)
|
||||
kws[name] = datetime.datetime(
|
||||
2000, 1, 1, 0, tzinfo=zoneinfo.ZoneInfo("Europe/Berlin")
|
||||
)
|
||||
else:
|
||||
# naive UTC
|
||||
|
@ -587,7 +588,7 @@ async def check_defaults_handling(
|
|||
|
||||
defaults_no_custom_defaults = Defaults()
|
||||
kwargs = {kwarg: "custom_default" for kwarg in inspect.signature(Defaults).parameters}
|
||||
kwargs["tzinfo"] = pytz.timezone("America/New_York")
|
||||
kwargs["tzinfo"] = zoneinfo.ZoneInfo("America/New_York")
|
||||
kwargs.pop("disable_web_page_preview") # mutually exclusive with link_preview_options
|
||||
kwargs.pop("quote") # mutually exclusive with do_quote
|
||||
kwargs["link_preview_options"] = LinkPreviewOptions(
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
import asyncio
|
||||
import datetime
|
||||
import logging
|
||||
import sys
|
||||
import zoneinfo
|
||||
from pathlib import Path
|
||||
from uuid import uuid4
|
||||
|
||||
|
@ -44,7 +44,6 @@ from tests.auxil.envvars import GITHUB_ACTION, RUN_TEST_OFFICIAL, TEST_WITH_OPT_
|
|||
from tests.auxil.files import data_file
|
||||
from tests.auxil.networking import NonchalantHttpxRequest
|
||||
from tests.auxil.pytest_classes import PytestBot, make_bot
|
||||
from tests.auxil.timezones import BasicTimezone
|
||||
|
||||
if TEST_WITH_OPT_DEPS:
|
||||
import pytz
|
||||
|
@ -311,9 +310,8 @@ def false_update(request):
|
|||
@pytest.fixture(scope="session", params=["Europe/Berlin", "Asia/Singapore", "UTC"])
|
||||
def tzinfo(request):
|
||||
if TEST_WITH_OPT_DEPS:
|
||||
return pytz.timezone(request.param)
|
||||
hours_offset = {"Europe/Berlin": 2, "Asia/Singapore": 8, "UTC": 0}[request.param]
|
||||
return BasicTimezone(offset=datetime.timedelta(hours=hours_offset), name=request.param)
|
||||
yield pytz.timezone(request.param)
|
||||
yield zoneinfo.ZoneInfo(request.param)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
|
|
|
@ -79,7 +79,7 @@ from telegram import (
|
|||
User,
|
||||
WebAppInfo,
|
||||
)
|
||||
from telegram._utils.datetime import UTC, from_timestamp, to_timestamp
|
||||
from telegram._utils.datetime import UTC, from_timestamp, localize, to_timestamp
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||
from telegram._utils.strings import to_camel_case
|
||||
from telegram.constants import (
|
||||
|
@ -97,7 +97,7 @@ from telegram.request import BaseRequest, HTTPXRequest, RequestData
|
|||
from telegram.warnings import PTBDeprecationWarning, PTBUserWarning
|
||||
from tests.auxil.bot_method_checks import check_defaults_handling
|
||||
from tests.auxil.ci_bots import FALLBACKS
|
||||
from tests.auxil.envvars import GITHUB_ACTION, TEST_WITH_OPT_DEPS
|
||||
from tests.auxil.envvars import GITHUB_ACTION
|
||||
from tests.auxil.files import data_file
|
||||
from tests.auxil.networking import OfflineRequest, expect_bad_request
|
||||
from tests.auxil.pytest_classes import PytestBot, PytestExtBot, make_bot
|
||||
|
@ -3467,7 +3467,6 @@ class TestBotWithRequest:
|
|||
)
|
||||
assert revoked_link.is_revoked
|
||||
|
||||
@pytest.mark.skipif(not TEST_WITH_OPT_DEPS, reason="This test's implementation requires pytz")
|
||||
@pytest.mark.parametrize("datetime", argvalues=[True, False], ids=["datetime", "integer"])
|
||||
async def test_advanced_chat_invite_links(self, bot, channel_id, datetime):
|
||||
# we are testing this all in one function in order to save api calls
|
||||
|
@ -3475,7 +3474,7 @@ class TestBotWithRequest:
|
|||
add_seconds = dtm.timedelta(0, 70)
|
||||
time_in_future = timestamp + add_seconds
|
||||
expire_time = time_in_future if datetime else to_timestamp(time_in_future)
|
||||
aware_time_in_future = UTC.localize(time_in_future)
|
||||
aware_time_in_future = localize(time_in_future, UTC)
|
||||
|
||||
invite_link = await bot.create_chat_invite_link(
|
||||
channel_id, expire_date=expire_time, member_limit=10
|
||||
|
@ -3488,7 +3487,7 @@ class TestBotWithRequest:
|
|||
add_seconds = dtm.timedelta(0, 80)
|
||||
time_in_future = timestamp + add_seconds
|
||||
expire_time = time_in_future if datetime else to_timestamp(time_in_future)
|
||||
aware_time_in_future = UTC.localize(time_in_future)
|
||||
aware_time_in_future = localize(time_in_future, UTC)
|
||||
|
||||
edited_invite_link = await bot.edit_chat_invite_link(
|
||||
channel_id,
|
||||
|
|
Loading…
Add table
Reference in a new issue