Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Bump APS & Deprecatepytz Support#4582

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
19 commits
Select commitHold shift + click to select a range
8c02b05
Update apscheduler requirement from ~=3.10.4 to >=3.10.4,<3.12.0
dependabot[bot]Dec 7, 2024
fcccd32
Update apscheduler requirement from ~=3.10.4 to >=3.10.4,<3.12.0
dependabot[bot]Dec 29, 2024
84c93bb
Try getting independent of pytz
Bibo-JoshiDec 30, 2024
6e37a38
Merge branch 'master' into dependabot/pip/apscheduler-gte-3.10.4-and-…
Bibo-JoshiDec 30, 2024
5cc7da8
Get existing tests to run
Bibo-JoshiDec 30, 2024
1945ce7
Extend defaults testing to other _date parameters
Bibo-JoshiDec 30, 2024
e846251
Mark pytz support as deprecated
Bibo-JoshiDec 30, 2024
0747587
Merge remote-tracking branch 'origin/dependabot/pip/apscheduler-gte-3…
Bibo-JoshiDec 30, 2024
37dfdf8
try fixing workflows
Bibo-JoshiDec 30, 2024
ad4d460
try fixing jobqueue tests
Bibo-JoshiDec 30, 2024
5008606
try again
Bibo-JoshiDec 30, 2024
1f8124f
add tzdata to unit test requirements
Bibo-JoshiDec 30, 2024
9cabf0e
Review
Bibo-JoshiDec 31, 2024
92851a4
Revert debug changes and improve TEST_WITH_OPT_DEPS handling
Bibo-JoshiDec 31, 2024
ebb71ab
try adding some debug things
Bibo-JoshiDec 31, 2024
a98bc65
Try fixing github action env var
Bibo-JoshiDec 31, 2024
f992b7e
Remove debug assertions
Bibo-JoshiDec 31, 2024
1bb8ffc
Merge branch 'master' into dependabot/pip/apscheduler-gte-3.10.4-and-…
Bibo-JoshiDec 31, 2024
b085b38
Merge branch 'master' into dependabot/pip/apscheduler-gte-3.10.4-and-…
Bibo-JoshiJan 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion.github/workflows/unit_tests.yml
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,7 +64,8 @@ jobs:

# Test the rest
export TEST_WITH_OPT_DEPS='true'
pip install .[all]
# need to manually install pytz here, because it's no longer in the optional reqs
pip install .[all] pytz
# `-n auto --dist worksteal` uses pytest-xdist to run tests on multiple CPU
# workers. Increasing number of workers has little effect on test duration, but it seems
# to increase flakyness.
Expand Down
2 changes: 1 addition & 1 deletionREADME.rst
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -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]"``.

Expand Down
4 changes: 1 addition & 3 deletionspyproject.toml
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -76,9 +76,7 @@ http2 = [
]
job-queue = [
# APS doesn't have a strict stability policy. Let's be cautious for now.
"APScheduler~=3.10.4",
# pytz is required by APS and just needs the lower bound due to #2120
"pytz>=2018.6",
"APScheduler>=3.10.4,<3.12.0",
]
passport = [
"cryptography!=3.4,!=3.4.1,!=3.4.2,!=3.4.3,>=39.0.1",
Expand Down
6 changes: 5 additions & 1 deletionrequirements-unit-tests.txt
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,4 +16,8 @@ pytest-xdist==3.6.1
flaky>=3.8.1

# used in test_official for parsing tg docs
beautifulsoup4
beautifulsoup4

# For testing with timezones. Might not be needed on all systems, but to ensure that unit tests
# run correctly on all systems, we include it here.
tzdata
41 changes: 27 additions & 14 deletionstelegram/_utils/datetime.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,29 +27,34 @@
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

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, 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)

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]
if datetime.tzinfo is None:
return datetime.replace(tzinfo=tzinfo)
return datetime.astimezone(tzinfo)


def to_float_timestamp(
Expand DownExpand Up@@ -87,7 +92,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``.
Expand DownExpand Up@@ -121,6 +126,12 @@ def to_float_timestamp(
return reference_timestamp + time_object

if tzinfo is None:
# We do this here rather than in the signature to ensure that we can make calls like
# to_float_timestamp(
# time, tzinfo=bot.defaults.tzinfo if bot.defaults else None
# )
# This ensures clean separation of concerns, i.e. the default timezone should not be
# the responsibility of the caller
tzinfo = UTC

if isinstance(time_object, dtm.time):
Expand All@@ -132,15 +143,17 @@ 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)
# datetime.combine uses the tzinfo of `time_object`, which might be None
# so we still need to localize
aware_datetime = localize(aware_datetime, tzinfo)

# if the time of day has passed today, use tomorrow
if reference_time > aware_datetime.timetz():
aware_datetime += dtm.timedelta(days=1)
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")
Expand Down
22 changes: 19 additions & 3 deletionstelegram/ext/_defaults.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -57,10 +57,13 @@ 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.

.. deprecated:: NEXT.VERSION
Support for ``pytz`` timezones is deprecated and will be removed in future
versions.

block (:obj:`bool`, optional): Default setting for the :paramref:`BaseHandler.block`
parameter
of handlers and error handlers registered through :meth:`Application.add_handler` and
Expand DownExpand Up@@ -148,6 +151,19 @@ def __init__(
self._block: bool = block
self._protect_content: Optional[bool] = protect_content

if "pytz" in str(self._tzinfo.__class__):
# TODO: When dropping support, make sure to update _utils.datetime accordingly
warn(
message=PTBDeprecationWarning(
version="NEXT.VERSION",
message=(
"Support for pytz timezones is deprecated and will be removed in "
"future versions."
),
),
stacklevel=2,
)

if disable_web_page_preview is not None and link_preview_options is not None:
raise ValueError(
"`disable_web_page_preview` and `link_preview_options` are mutually exclusive."
Expand Down
12 changes: 7 additions & 5 deletionstelegram/ext/_jobqueue.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -23,14 +23,14 @@
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

APS_AVAILABLE = True
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
Expand DownExpand Up@@ -155,13 +155,13 @@ def scheduler_configuration(self) -> JSONDict:
dict[:obj:`str`, :obj:`object`]: The configuration values as dictionary.

"""
timezone:object =pytz.utc
timezone:dtm.tzinfo =UTC
if (
self._application
and isinstance(self.application.bot, ExtBot)
and self.application.bot.defaults
):
timezone = self.application.bot.defaults.tzinfo orpytz.utc
timezone = self.application.bot.defaults.tzinfo orUTC

return {
"timezone": timezone,
Expand DownExpand Up@@ -197,8 +197,10 @@ def _parse_time_input(
dtm.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 <= dtm.datetime.now(pytz.utc):
# dtm.combine uses the tzinfo of `time`, which might be None, so we still have
# to localize it
date_time = localize(date_time, self.scheduler.timezone)
if shift_day and date_time <= dtm.datetime.now(UTC):
date_time += dtm.timedelta(days=1)
return date_time
return time
Expand Down
59 changes: 36 additions & 23 deletionstests/_utils/test_datetime.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,6 +18,7 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
import datetime as dtm
import time
import zoneinfo

import pytest

Expand DownExpand Up@@ -55,18 +56,38 @@


class TestDatetime:
@staticmethod
def localize(dt, tzinfo):
if TEST_WITH_OPT_DEPS:
return tzinfo.localize(dt)
return dt.replace(tzinfo=tzinfo)

def test_helpers_utc(self):
# Here we just test, that we got the correct UTC variant
if not TEST_WITH_OPT_DEPS:
assert tg_dtm.UTC is tg_dtm.DTM_UTC
else:
assert tg_dtm.UTC is not tg_dtm.DTM_UTC
def test_localize_utc(self):
dt = dtm.datetime(2023, 1, 1, 12, 0, 0)
localized_dt = tg_dtm.localize(dt, tg_dtm.UTC)
assert localized_dt.tzinfo == tg_dtm.UTC
assert localized_dt == dt.replace(tzinfo=tg_dtm.UTC)

@pytest.mark.skipif(not TEST_WITH_OPT_DEPS, reason="pytz not installed")
def test_localize_pytz(self):
dt = dtm.datetime(2023, 1, 1, 12, 0, 0)
import pytz

tzinfo = pytz.timezone("Europe/Berlin")
localized_dt = tg_dtm.localize(dt, tzinfo)
assert localized_dt.hour == dt.hour
assert localized_dt.tzinfo is not None
assert tzinfo.utcoffset(dt) is not None

def test_localize_zoneinfo_naive(self):
dt = dtm.datetime(2023, 1, 1, 12, 0, 0)
tzinfo = zoneinfo.ZoneInfo("Europe/Berlin")
localized_dt = tg_dtm.localize(dt, tzinfo)
assert localized_dt.hour == dt.hour
assert localized_dt.tzinfo is not None
assert tzinfo.utcoffset(dt) is not None

def test_localize_zoneinfo_aware(self):
dt = dtm.datetime(2023, 1, 1, 12, 0, 0, tzinfo=dtm.timezone.utc)
tzinfo = zoneinfo.ZoneInfo("Europe/Berlin")
localized_dt = tg_dtm.localize(dt, tzinfo)
assert localized_dt.hour == dt.hour + 1
assert localized_dt.tzinfo is not None
assert tzinfo.utcoffset(dt) is not None

def test_to_float_timestamp_absolute_naive(self):
"""Conversion from timezone-naive datetime to timestamp.
Expand All@@ -75,20 +96,12 @@ def test_to_float_timestamp_absolute_naive(self):
datetime = dtm.datetime(2019, 11, 11, 0, 26, 16, 10**5)
assert tg_dtm.to_float_timestamp(datetime) == 1573431976.1

def test_to_float_timestamp_absolute_naive_no_pytz(self, monkeypatch):
"""Conversion from timezone-naive datetime to timestamp.
Naive datetimes should be assumed to be in UTC.
"""
monkeypatch.setattr(tg_dtm, "UTC", tg_dtm.DTM_UTC)
datetime = dtm.datetime(2019, 11, 11, 0, 26, 16, 10**5)
assert tg_dtm.to_float_timestamp(datetime) == 1573431976.1

def test_to_float_timestamp_absolute_aware(self, timezone):
"""Conversion from timezone-aware datetime to timestamp"""
# we're parametrizing this with two different UTC offsets to exclude the possibility
# of an xpass when the test is run in a timezone with the same UTC offset
test_datetime = dtm.datetime(2019, 11, 11, 0, 26, 16, 10**5)
datetime =self.localize(test_datetime, timezone)
datetime =tg_dtm.localize(test_datetime, timezone)
assert (
tg_dtm.to_float_timestamp(datetime)
== 1573431976.1 - timezone.utcoffset(test_datetime).total_seconds()
Expand DownExpand Up@@ -126,7 +139,7 @@ def test_to_float_timestamp_time_of_day_timezone(self, timezone):
ref_datetime = dtm.datetime(1970, 1, 1, 12)
utc_offset = timezone.utcoffset(ref_datetime)
ref_t, time_of_day = tg_dtm._datetime_to_float_timestamp(ref_datetime), ref_datetime.time()
aware_time_of_day =self.localize(ref_datetime, timezone).timetz()
aware_time_of_day =tg_dtm.localize(ref_datetime, timezone).timetz()

# first test that naive time is assumed to be utc:
assert tg_dtm.to_float_timestamp(time_of_day, ref_t) == pytest.approx(ref_t)
Expand DownExpand Up@@ -169,7 +182,7 @@ def test_from_timestamp_aware(self, timezone):
# we're parametrizing this with two different UTC offsets to exclude the possibility
# of an xpass when the test is run in a timezone with the same UTC offset
test_datetime = dtm.datetime(2019, 11, 11, 0, 26, 16, 10**5)
datetime =self.localize(test_datetime, timezone)
datetime =tg_dtm.localize(test_datetime, timezone)
assert (
tg_dtm.from_timestamp(1573431976.1 - timezone.utcoffset(test_datetime).total_seconds())
== datetime
Expand Down
40 changes: 24 additions & 16 deletionstests/auxil/bot_method_checks.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,6 +21,7 @@
import functools
import inspect
import re
import zoneinfo
from collections.abc import Collection, Iterable
from typing import Any, Callable, Optional

Expand All@@ -40,15 +41,11 @@
Sticker,
TelegramObject,
)
from telegram._utils.datetime import to_timestamp
from telegram._utils.defaultvalue import DEFAULT_NONE, DefaultValue
from telegram.constants import InputMediaType
from telegram.ext import Defaults, ExtBot
from telegram.request import RequestData
from tests.auxil.envvars import TEST_WITH_OPT_DEPS

if TEST_WITH_OPT_DEPS:
import pytz


FORWARD_REF_PATTERN = re.compile(r"ForwardRef\('(?P<class_name>\w+)'\)")
""" A pattern to find a class name in a ForwardRef typing annotation.
Expand DownExpand Up@@ -344,10 +341,10 @@ def build_kwargs(
# Some special casing for methods that have "exactly one of the optionals" type args
elif name in ["location", "contact", "venue", "inline_message_id"]:
kws[name] = True
elif name == "until_date":
elif name.endswith("_date"):
if manually_passed_value not in [None, DEFAULT_NONE]:
# Europe/Berlin
kws[name] =pytz.timezone("Europe/Berlin").localize(dtm.datetime(2000, 1, 1, 0))
kws[name] = dtm.datetime(2000, 1, 1, 0, tzinfo=zoneinfo.ZoneInfo("Europe/Berlin"))
else:
# naive UTC
kws[name] = dtm.datetime(2000, 1, 1, 0)
Expand DownExpand Up@@ -395,6 +392,15 @@ def make_assertion_for_link_preview_options(
)


_EUROPE_BERLIN_TS = to_timestamp(
dtm.datetime(2000, 1, 1, 0, tzinfo=zoneinfo.ZoneInfo("Europe/Berlin"))
)
_UTC_TS = to_timestamp(dtm.datetime(2000, 1, 1, 0), tzinfo=zoneinfo.ZoneInfo("UTC"))
_AMERICA_NEW_YORK_TS = to_timestamp(
dtm.datetime(2000, 1, 1, 0, tzinfo=zoneinfo.ZoneInfo("America/New_York"))
)


async def make_assertion(
url,
request_data: RequestData,
Expand DownExpand Up@@ -530,14 +536,16 @@ def check_input_media(m: dict):
)

# Check datetime conversion
until_date = data.pop("until_date", None)
if until_date:
if manual_value_expected and until_date != 946681200:
pytest.fail("Non-naive until_date should have been interpreted as Europe/Berlin.")
if not any((manually_passed_value, expected_defaults_value)) and until_date != 946684800:
pytest.fail("Naive until_date should have been interpreted as UTC")
if default_value_expected and until_date != 946702800:
pytest.fail("Naive until_date should have been interpreted as America/New_York")
date_keys = [key for key in data if key.endswith("_date")]
for key in date_keys:
date_param = data.pop(key)
if date_param:
if manual_value_expected and date_param != _EUROPE_BERLIN_TS:
pytest.fail(f"Non-naive `{key}` should have been interpreted as Europe/Berlin.")
if not any((manually_passed_value, expected_defaults_value)) and date_param != _UTC_TS:
pytest.fail(f"Naive `{key}` should have been interpreted as UTC")
if default_value_expected and date_param != _AMERICA_NEW_YORK_TS:
pytest.fail(f"Naive `{key}` should have been interpreted as America/New_York")

if method_name in ["get_file", "get_small_file", "get_big_file"]:
# This is here mainly for PassportFile.get_file, which calls .set_credentials on the
Expand DownExpand Up@@ -596,7 +604,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(
Expand Down
Loading
Loading

[8]ページ先頭

©2009-2025 Movatter.jp