8000 Add `JobQueue.scheduler_configuration` and Corresponding Warnings (#3… · guillemap/python-telegram-bot@f67e8c0 · GitHub
[go: up one dir, main page]

Skip to content

Commit f67e8c0

Browse files
authored
Add JobQueue.scheduler_configuration and Corresponding Warnings (python-telegram-bot#3913)
1 parent af130ef commit f67e8c0

File tree

2 files changed

+64
-10
lines changed

2 files changed

+64
-10
lines changed

telegram/ext/_jobqueue.py

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,17 @@ class JobQueue(Generic[CCT]):
7676
Attributes:
7777
scheduler (:class:`apscheduler.schedulers.asyncio.AsyncIOScheduler`): The scheduler.
7878
79+
Warning:
80+
This scheduler is configured by :meth:`set_application`. Additional configuration
81+
settings can be made by users. However, calling
82+
:meth:`~apscheduler.schedulers.base.BaseScheduler.configure` will delete any
83+
previous configuration settings. Therefore, please make sure to pass the values
84+
returned by :attr:`scheduler_configuration` to the method call in addition to your
85+
custom values.
86+
Alternatively, you can also use methods like
87+
:meth:`~apscheduler.schedulers.base.BaseScheduler.add_jobstore` to avoid using
88+
:meth:`~apscheduler.schedulers.base.BaseScheduler.configure` altogether.
89+
7990
.. versionchanged:: 20.0
8091
Uses :class:`~apscheduler.schedulers.asyncio.AsyncIOScheduler` instead of
8192
:class:`~apscheduler.schedulers.background.BackgroundScheduler`
@@ -94,9 +105,7 @@ def __init__(self) -> None:
94105

95106
self._application: Optional[weakref.ReferenceType[Application]] = None
96107
self._executor = AsyncIOExecutor()
97-
self.scheduler: AsyncIOScheduler = AsyncIOScheduler(
98-
timezone=pytz.utc, executors={"default": self._executor}
99-
)
108+
self.scheduler: AsyncIOScheduler = AsyncIOScheduler(**self.scheduler_configuration)
100109

101110
def __repr__(self) -> str:
102111
"""Give a string representation of the JobQueue in the form ``JobQueue[application=...]``.
@@ -119,6 +128,43 @@ def application(self) -> "Application[Any, CCT, Any, Any, Any, JobQueue[CCT]]":
119128
return application
120129
raise RuntimeError("The application instance is no longer alive.")
121130

131+
@property
132+
def scheduler_configuration(self) -> JSONDict:
133+
"""Provides configuration values that are used by :class:`JobQueue` for :attr:`scheduler`.
134+
135+
Tip:
136+
Since calling
137+
:meth:`scheduler.configure() <apscheduler.schedulers.base.BaseScheduler.configure>`
138+
deletes any previous setting, please make sure to pass these values to the method call
139+
in addition to your custom values:
140+
141+
.. code-block:: python
142+
143+
scheduler.configure(..., **job_queue.scheduler_configuration)
144+
145+
Alternatively, you can also use methods like
146+
:meth:`~apscheduler.schedulers.base.BaseScheduler.add_jobstore` to avoid using
147+
:meth:`~apscheduler.schedulers.base.BaseScheduler.configure` altogether.
148+
149+
.. versionadded:: NEXT.VERSION
150+
151+
Returns:
152+
Dict[:obj:`str`, :obj:`object`]: The configuration values as dictionary.
153+
154+
"""
155+
timezone: object = pytz.utc
156+
if (
157+
self._application
158+
and isinstance(self.application.bot, ExtBot)
159+
and self.application.bot.defaults
160+
):
161+
timezone = self.application.bot.defaults.tzinfo or pytz.utc
162+
163+
return {
164+
"timezone": timezone,
165+
"executors": {"default": self._executor},
166+
}
167+
122168
def _tz_now(self) -> datetime.datetime:
123169
return datetime.datetime.now(self.scheduler.timezone)
124170

@@ -166,11 +212,7 @@ def set_application(
166212
167213
"""
168214
self._application = weakref.ref(application)
169-
if isinstance(application.bot, ExtBot) and application.bot.defaults:
170-
self.scheduler.configure(
171-
timezone=application.bot.defaults.tzinfo or pytz.utc,
172-
executors={"default": self._executor},
173-
)
215+
self.scheduler.configure(**self.scheduler_configuration)
174216

175217
@staticmethod
176218
async def job_callback(job_queue: "JobQueue[CCT]", job: "Job[CCT]") -> None:

tests/ext/test_jobqueue.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
import pytest
2727

28-
from telegram.ext import ApplicationBuilder, CallbackContext, ContextTypes, Job, JobQueue
28+
from telegram.ext import ApplicationBuilder, CallbackContext, ContextTypes, Defaults, Job, JobQueue
2929
from telegram.warnings import PTBUserWarning
3030
from tests.auxil.envvars import GITHUB_ACTION, TEST_WITH_OPT_DEPS
3131
from tests.auxil.pytest_classes import make_bot
@@ -106,6 +106,15 @@ def _reset(self):
106106
self.job_time = 0
107107
self.received_error = None
108108

109+
def test_scheduler_configuration(self, job_queue, timezone, bot):
110+
# Unfortunately, we can't really test the executor setting explicitly without relying
111+
# on protected attributes. However, this should be tested enough implicitly via all the
112+
# other tests in here
113+
assert job_queue.scheduler_configuration["timezone"] is UTC
114+
115+
tz_app = ApplicationBuilder().defaults(Defaults(tzinfo=timezone)).token(bot.token).build()
116+
assert tz_app.job_queue.scheduler_configuration["timezone"] is timezone
117+
109118
async def job_run_once(self, context):
110119
if (
111120
isinstance(context, CallbackContext)
@@ -578,7 +587,7 @@ async def test_process_error_that_raises_errors(self, job_queue, app, caplog):
578587
assert rec.name == "telegram.ext.Application"
579588
assert "No error handlers are registered" in rec.getMessage()
580589

581-
async def test_custom_context(self, bot, job_queue):
590+
async def< A92E /span> test_custom_context(self, bot):
582591
application = (
583592
ApplicationBuilder()
584593
.token(bot.token)
@@ -589,6 +598,7 @@ async def test_custom_context(self, bot, job_queue):
589598
)
590599
.build()
591600
)
601+
job_queue = JobQueue()
592602
job_queue.set_application(application)
593603

594604
async def callback(context):
@@ -599,9 +609,11 @@ async def callback(context):
599609
type(context.bot_data),
600610
)
601611

612+
await job_queue.start()
602613
job_queue.run_once(callback, 0.1)
603614
await asyncio.sleep(0.15)
604615
assert self.result == (CustomContext, None, None, int)
616+
await job_queue.stop()
605617

606618
async def test_attribute_error(self):
607619
job = Job(self.job_run_once)

0 commit comments

Comments
 (0)
0