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

Usetimedelta to represent time periods in classes#4750

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

Open
aelkheir wants to merge14 commits intomaster
base:master
Choose a base branch
Loading
fromfeature/4575-timedelta-classes

Conversation

aelkheir
Copy link
Member

@aelkheiraelkheir commentedApr 9, 2025
edited
Loading

Second part of#4575.

Affected classes (basicaly anywhere "in seconds" appears in api docs)
ClassattributeTypedocs
ChatFullInfoslow_mode_delayIntegerOptional. For supergroups, the minimum allowed delay between consecutive messages sent by each unprivileged user; in seconds
ChatFullInfomessage_auto_delete_timeIntegerOptional. The time after which all messages sent to the chat will be automatically deleted; in seconds
AnimationdurationIntegerDuration of the video in seconds as defined by the sender
AudiodurationIntegerDuration of the audio in seconds as defined by the sender
VideodurationIntegerDuration of the video in seconds as defined by the sender
Videostart_timestampIntegerOptional. Timestamp in seconds from which the video will play in the message
VideoNotedurationIntegerDuration of the video in seconds as defined by the sender
VoicedurationIntegerDuration of the audio in seconds as defined by the sender
PaidMediaPreviewdurationIntegerOptional. Duration of the media in seconds as defined by the sender
Pollopen_periodIntegerOptional. Amount of time in seconds the poll will be active after creation
Locationlive_periodIntegerOptional. Time relative to the message sending date, during which the location can be updated; in seconds. For active live locations only.
MessageAutoDeleteTimerChangedmessage_auto_delete_timeIntegerNew auto-delete time for messages in the chat; in seconds
VideoChatEndeddurationIntegerVideo chat duration in seconds
ChatInviteLinksubscription_periodIntegerOptional. The number of seconds the subscription will be active for before the next payment
ResponseParametersretry_afterIntegerOptional. In case of exceeding flood control, the number of seconds left to wait before the request can be repeated
InputMediaVideodurationIntegerOptional. Video duration in seconds
InputMediaAnimationdurationIntegerOptional. Animation duration in seconds
InputMediaAudiodurationIntegerOptional. Duration of the audio in seconds
InputPaidMediaVideodurationIntegerOptional. Video duration in seconds
InlineQueryResultGifgif_durationIntegerOptional. Duration of the GIF in seconds
InlineQueryResultMpeg4Gifmpeg4_durationIntegerOptional. Video duration in seconds
InlineQueryResultVideovideo_durationIntegerOptional. Video duration in seconds
InlineQueryResultAudioaudio_durationIntegerOptional. Audio duration in seconds
InlineQueryResultVoicevoice_durationIntegerOptional. Recording duration in seconds
InlineQueryResultLocationlive_periodIntegerOptional. Period in seconds during which the location can be updated, should be between 60 and 86400, or 0x7FFFFFFF for live locations that can be edited indefinitely.
InputLocationMessageContentlive_periodIntegerOptional. Period in seconds during which the location can be updated, should be between 60 and 86400, or 0x7FFFFFFF for live locations that can be edited indefinitely.

Check-list for PRs

  • Added.. versionadded:: NEXT.VERSION,.. versionchanged:: NEXT.VERSION,.. deprecated:: NEXT.VERSION or.. versionremoved:: NEXT.VERSION to the docstrings for user facing changes (for methods/class descriptions, arguments and attributes)
  • Created new or adapted existing unit tests
  • Documented code changes according to theCSI standard <https://standards.mousepawmedia.com/en/stable/csi.html>__
  • Checked theStability Policy <https://docs.python-telegram-bot.org/stability_policy.html>_ in case of deprecations or changes to documented behavior

If the PR contains API changes (otherwise, you can ignore this passage)

  • Checked the Bot API specific sections of theStability Policy <https://docs.python-telegram-bot.org/stability_policy.html>_

  • Created a PR to remove functionality deprecated in the previous Bot API release (see here <https://docs.python-telegram-bot.org/en/stable/stability_policy.html#case-2>_)

  • If relevant:

    • Added or updated documentation for the changed class(es) and/or method(s)

github-actionsbot pushed a commit that referenced this pull requestApr 9, 2025
@aelkheir
Copy link
MemberAuthor

aelkheir commentedApr 9, 2025
edited
Loading

One thought before i proceed with the rest of the classes.

Both attributes and arguments should only accept timedeltas in the long run.#4575 (comment)

why deprecate class args, i think it would be convenient to allow passing either30 ortimedelta(seconds=30). it would also be consistent with how bot methods work as of#4651. deprecating class attrs is sensible however.

@Bibo-Joshi
Copy link
Member

Hey, thanks for getting started so quickly:)
About your question: TelegramObject types are not really intended to be manually initialized by the user but rather by the de_json methods - that's in contrast to the bot methods. I view them as mere data storages. Note that the conversions from json to datetime or off of the TG classes also all happen in de_json and not in init. This approach can certainly be questioned in general (i.e. what would be a sensible interface for classes wrapping API types). In the current set-up, I'd see consistency as the more important argument 😅

@aelkheir
Copy link
MemberAuthor

Aha okay. but would it still be necessary to issue a separate deprecation warning in the init methods as was outlined in#4575 (comment) (very helpful summary btw 😄 ). one is already being issued on property access.

@Bibo-Joshi
Copy link
Member

I would say, so yes. Attribute access and passing the parameter are two independent operations, both of which will be affected :)

@aelkheir
Copy link
MemberAuthor

I see! I was thinking more from the perspective that

TelegramObject types are not really intended to be manually initialized by the user rather by the de_json.

whom are we intending to warn then

anyways I did include both warnings for completeness and the cases where users are manually initializing objects

@aelkheiraelkheir mentioned this pull requestApr 24, 2025
14 tasks
- ChatFullInfo.slow_mode_delay.- ChatFullInfo.message_auto_delete_time.Conflicts:telegram/_chatfullinfo.pytests/test_chatfullinfo.pytests/test_official/exceptions.py
Conflicts:tests/test_chatfullinfo.py
Conflicts:tests/test_chatfullinfo.py
This includes ``duration`` param of the following:- Animation               - Audio- Video                   - VideoNote- Voice                   - PaidMediaPreview- VideoChatEnded          - InputMediaVideo- InputMediaAnimation     - InputMediaAudio- InputPaidMediaVideo
Conflicts:tests/test_paidmedia.pytests/test_videochat.py
- InlineQueryResultGif.gif_duration- InlineQueryResultMpeg4Gif.mpeg4_duration- InlineQueryResultVideo.video_duration- InlineQueryResultAudio.audio_duration- InlineQueryResultVoice.voice_duration- InlineQueryResultLocation.live_period
- Video.start_timestamp- Poll.open_period- Location.live_period- MessageAutoDeleteTimerChanged.message_auto_delete_time- ChatInviteLink.subscription_period- InputLocationMessageContent.live_period
This also discovered `Bot.get_updates.timeout` thas wasmissed (?) in#4651.
@aelkheiraelkheirforce-pushed thefeature/4575-timedelta-classes branch frombc3cf6d to466e0b0CompareMay 17, 2025 23:35
@aelkheir
Copy link
MemberAuthor

Almost ready for a review. just a few things to discuss.

  • Not sure if I should follow the deprecation process for fieldResponseParameter.retry_after which maps to PTB's classRetryAfter(TelegramError). a suggestion is to letself.retry_after continue being andint. and only alter the message of the error with a timedelta value derived fromselr.retry_after. So it prints:
    Flood control exceeded. Retry in 0:08:20.
    instead of what it currently prints:
    Flood control exceeded. Retry in 500 seconds
    Reason being that it's underlingint type is not user-facing. and also not to break older pickles of the class.
  • Automatic detection of time periods in test_official works great (only needed a few adjustments). Anyway it also detectedBot.get_updates.timeout (getUpdates) which i think was missed in#4651 (?). I have currently adjustedBot.get_updates.timeout but without altering the signature of other methods that call it (i.eUpdater.[_]start_polling) which also have atimeout parameter.

Checks are really mad, will debug tomorrow :')

@Bibo-Joshi
Copy link
Member

Hey, thanks for the updates - looks like you've spend quite some energy in this, awesome :) I haven't managed to look at the code yet, so at least giving my 2cts on your comments

  • RetryAfter: I'd personally also like a timedelta here, nice catch actually! In terms on interface changes I don't see a reason not to do that. If updating the pickle behavior becomes difficult I'd be okay with staying with an integer. Mind that pickle behavior doesn't need to be backward compatibile - we explicitly exclude that in the stability policy and I guess picklung exceptions is a rather small use case …
  • thanks for also catchingget_updates. Even though I'm not quite sure why I skipped it, I think it may be because I also skipped all the other timeout parameters. Accepting timedelta for them as well might make sense as well but here the issue would be that the abstractBaseRquest.do_request should then also broaden the signature. So I'd postpones that discussion for now. forget_updates.timeout I'd say you can feel free to update also the signatures that forward the value toget_updates.timeout :)

@harshil21harshil21 added 🛠 refactorchange type: refactor 🔌 enhancementpr description: enhancement labelsMay 22, 2025
Copy link
Member

@harshil21harshil21 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

a simple change, but we have to change so many files and tests for this :(

PR is mostly good, I'm just double checking things-

@@ -0,0 +1,35 @@
features = "Use `timedelta` to represent time periods in classes"
deprecations = """In this release, we're migrating attributes that represent durations/time periods from :ob:`int` type to Python's native :class:`datetime.timedelta`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Suggested change
deprecations ="""In this release, we're migrating attributes that represent durations/time periods from :ob:`int` type to Python's native :class:`datetime.timedelta`.
deprecations ="""In this release, we're migrating attributes that represent durations/time periods from :obj:`int` type to Python's native :class:`datetime.timedelta`.

features = "Use `timedelta` to represent time periods in classes"
deprecations = """In this release, we're migrating attributes that represent durations/time periods from :ob:`int` type to Python's native :class:`datetime.timedelta`.

Enable the ``PTB_TIMEDELTA`` environment variable to adopt :obj:`timedelta` now. Support for :obj:`int` values is deprecated and will be removed in a future major version.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Suggested change
Enable the ``PTB_TIMEDELTA`` environment variable to adopt :obj:`timedelta` now. Support for :obj:`int` values is deprecated and will be removed in a future major version.
Set the ``PTB_TIMEDELTA=1`` environment variable to adopt :obj:`timedelta` now. Support for :obj:`int` values is deprecated and will be removed in a future major version.

it's not clear to me what happens when you set the env variable, are timedelta objects returned? If that's the case, the release note should mention that.

@@ -101,3 +101,5 @@
.. |org-verify| replace:: `on behalf of the organization <https://telegram.org/verify#third-party-verification>`__

.. |time-period-input| replace:: :class:`datetime.timedelta` objects are accepted in addition to plain :obj:`int` values.

.. |time-period-int-deprecated| replace:: In a future major version this will be of type :obj:`datetime.timedelta`. You can opt-in early by setting the `PTB_TIMEDELTA` environment variable.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Suggested change
.. |time-period-int-deprecated|replace:: In a future major version this will be of type:obj:`datetime.timedelta`. You can opt-in early by settingthe`PTB_TIMEDELTA` environment variable.
.. |time-period-int-deprecated|replace:: In a future major version this will be of type:obj:`datetime.timedelta`. You can opt-in early by setting `PTB_TIMEDELTA=1` as an environment variable.

Comment on lines +4597 to +4601
read_timeout = (
(arg_read_timeout + timeout.total_seconds())
if isinstance(timeout, dtm.timedelta)
else (arg_read_timeout + timeout if isinstance(timeout, int) else arg_read_timeout)
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Suggested change
read_timeout= (
(arg_read_timeout+timeout.total_seconds())
ifisinstance(timeout,dtm.timedelta)
else (arg_read_timeout+timeoutifisinstance(timeout,int)elsearg_read_timeout)
)
read_timeout= (
(arg_read_timeout+timeout.total_seconds())
ifisinstance(timeout,dtm.timedelta)
else (arg_read_timeout+timeoutiftimeoutelsearg_read_timeout)
)

ah I don't think I've ever seen nested ternary if statements in python haha. Regarding the change, we should check for truthiness oftimeout like before, because your case didn't cover if timeout was a float.


defget_timedelta_value(value:Optional[dtm.timedelta])->Optional[Union[float,dtm.timedelta]]:
"""
Convert a `datetime.timedelta` to seconds or return it as-is, based on environment config.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

should mention here that it returns a timedelta if the env var is set, otherwise a float.

@@ -80,18 +93,21 @@ class Video(_BaseThumbedMedium):
the video in the message.

.. versionadded:: 21.11
start_timestamp (:obj:`int`): Optional, Timestamp in seconds from which the video
will play in the message
start_timestamp (:obj:`int` | :class:`datetime.timedelta`): Optional, Timestamp in seconds
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Suggested change
start_timestamp (:obj:`int`| :class:`datetime.timedelta`):Optional,Timestampinseconds
start_timestamp (:obj:`int`| :class:`datetime.timedelta`):Optional.Timestampinseconds

Comment on lines +148 to +149
@property
def duration(self) -> Union[int, dtm.timedelta]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I might be wrong, but I think we have to add a docstring for the properties here and remove it from the Attributes section up top. If you can build the docs and check how they look and confirm if they're good that would be nice.

assert animation_dict["thumbnail"] == animation.thumbnail.to_dict()
assert animation_dict["file_name"] == animation.file_name
assert animation_dict["mime_type"] == animation.mime_type
assert animation_dict["file_size"] == animation.file_size

def test_time_period_properties(self, PTB_TIMEDELTA, animation):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I don't think it's idiomatic to have an all caps fixture name in the argument. Maybe rename it?

@pytest.fixture(scope="module", params=["true", "false", None])
def PTB_TIMEDELTA(request):
# Here we manually use monkeypatch to give this fixture module scope
monkeypatch = pytest.MonkeyPatch()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

why doesn't specifying monkeypatch as a parameter work?

Comment on lines +106 to +107
"TransactionPartnerUser": {
"subscription_period":int,# actual: Union[int, dtm.timedelta]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

do we still need this?

@harshil21harshil21 requested a review fromCopilotMay 22, 2025 07:53
Copy link

@CopilotCopilotAI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Pull Request Overview

This PR migrates numerous time-based attributes from plain integer seconds to accept and returndatetime.timedelta alongsideint values, enhancing API ergonomics and future-proofing duration handling.

  • IntroducesTimePeriod type alias,parse_period_arg input parsing, andget_timedelta_value output normalization for all affected duration/live-period fields.
  • Updates docstrings with.. versionchanged:: and.. deprecated:: annotations, addsproperty getters, and implementsto_dict/de_json to correctly serializetimedelta.
  • Adjustsget_updates in the Bot API to allowtimeout astimedelta.

Reviewed Changes

Copilot reviewed 52 out of 52 changed files in this pull request and generated 2 comments.

FileDescription
telegram/_files/inputmedia.pyAddedparse_period_arg andget_timedelta_value usage for duration, input media; enhancedto_dict.
telegram/_inline/inlinequeryresultvoice.pyConvertedvoice_duration toTimePeriod, added property andto_dict serialization.
telegram/_files/voice.pyUpdatedVoice.duration to accepttimedelta, added de/serialization and property.
Comments suppressed due to low confidence (3)

telegram/_files/inputmedia.py:303

  • Thestart_timestamp field is not passed throughparse_period_arg, so providing atimedelta won’t populate_start_timestamp. Replace this withself._start_timestamp = parse_period_arg(start_timestamp) and update__slots__ accordingly.
self.start_timestamp: Optional[int] = start_timestamp

telegram/_files/inputmedia.py:212

  • InputPaidMediaVideo defines_duration and_start_timestamp but does not overrideto_dict, so these fields won’t be serialized. Add ato_dict method similar to other media classes to includeduration andstart_timestamp in the output.
class InputPaidMediaVideo(InputPaidMedia):

telegram/_files/inputmedia.py:34

  • [nitpick] There are no tests forInputPaidMediaVideo handling oftimedelta vs.int. Please add unit tests to verify parsing, property output, andto_dict serialization for both input types.
parse_period_arg, parse_sequence_arg

@@ -110,6 +112,19 @@ def _parse_thumbnail_input(thumbnail: Optional[FileInput]) -> Optional[Union[str
else thumbnail
)

def to_dict(self, recursive: bool = True) -> JSONDict:
Copy link
Preview

CopilotAIMay 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

[nitpick] The conversion logic for durations to seconds is duplicated across multipleto_dict implementations. Consider extracting this into a shared helper or mixin to DRY up the code.

Copilot uses AI. Check for mistakes.

def to_dict(self, recursive: bool = True) -> JSONDict:
"""See :meth:`telegram.TelegramObject.to_dict`."""
out = super().to_dict(recursive)
if isinstance(self, (InputMediaAnimation, InputMediaVideo, InputMediaAudio)):
Copy link
Preview

CopilotAIMay 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

The genericto_dict only handlesInputMediaAnimation,InputMediaVideo, andInputMediaAudio. It should also includeInputPaidMediaVideo so its_duration and_start_timestamp fields are properly emitted.

Suggested change
ifisinstance(self, (InputMediaAnimation,InputMediaVideo,InputMediaAudio)):
ifisinstance(self, (InputMediaAnimation,InputMediaVideo,InputMediaAudio,InputPaidMediaVideo)):

Copilot uses AI. Check for mistakes.

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

Copilot code reviewCopilotCopilot left review comments

@harshil21harshil21harshil21 requested changes

Requested changes must be addressed to merge this pull request.

Assignees
No one assigned
Labels
🔌 enhancementpr description: enhancement🛠 refactorchange type: refactor
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

3 participants
@aelkheir@Bibo-Joshi@harshil21

[8]ページ先頭

©2009-2025 Movatter.jp