Skip to content

Commit

Permalink
Add Notes and Warnings About Optional Dependencies (#3393)
Browse files Browse the repository at this point in the history
  • Loading branch information
clot27 committed Dec 2, 2022
1 parent 4123e7a commit f8be97c
Show file tree
Hide file tree
Showing 11 changed files with 88 additions and 21 deletions.
3 changes: 3 additions & 0 deletions docs/source/examples.rst
Expand Up @@ -40,6 +40,7 @@ up a job to send a message to that user after 30 seconds. The user can
also cancel the timer by sending ``/unset``. To learn more about the
``JobQueue``, read `this wiki
article <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-%E2%80%93-JobQueue>`__.
Note: To use ``JobQueue``, you must install PTB via ``pip install python-telegram-bot[job-queue]``

:any:`examples.conversationbot`
-------------------------------
Expand Down Expand Up @@ -115,6 +116,7 @@ Don’t forget to enable and configure payments with
`@BotFather <https://telegram.me/BotFather>`_. Check out this
`guide <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Telegram-Passport>`__
on Telegram passports in PTB.
Note: To use Telegram Passport, you must install PTB via ``pip install python-telegram-bot[passport]``

:any:`examples.paymentbot`
--------------------------
Expand Down Expand Up @@ -162,6 +164,7 @@ combination with ``telegram.ext.Application``.

This example showcases how PTBs “arbitrary callback data” feature can be
used.
Note: To use arbitrary callback data, you must install PTB via ``pip install python-telegram-bot[callback-data]``

Pure API
--------
Expand Down
4 changes: 4 additions & 0 deletions examples/arbitrarycallbackdatabot.py
Expand Up @@ -6,6 +6,10 @@
For detailed info on arbitrary callback data, see the wiki page at
https://github.com/python-telegram-bot/python-telegram-bot/wiki/Arbitrary-callback_data
Note:
To use arbitrary callback data, you must install PTB via
`pip install python-telegram-bot[callback-data]`
"""
import logging
from typing import List, Tuple, cast
Expand Down
3 changes: 3 additions & 0 deletions examples/passportbot.py
Expand Up @@ -10,6 +10,9 @@
See https://github.com/python-telegram-bot/python-telegram-bot/wiki/Telegram-Passport
for how to use Telegram Passport properly with python-telegram-bot.
Note:
To use Telegram Passport, you must install PTB via
`pip install python-telegram-bot[passport]`
"""
import logging
from pathlib import Path
Expand Down
4 changes: 4 additions & 0 deletions examples/timerbot.py
Expand Up @@ -16,6 +16,10 @@
Basic Alarm Bot example, sends a message after a set time.
Press Ctrl-C on the command line or send a signal to the process to stop the
bot.
Note:
To use arbitrary callback data, you must install ptb via
`pip install python-telegram-bot[callback-data]`
"""

import logging
Expand Down
33 changes: 24 additions & 9 deletions telegram/ext/_application.py
Expand Up @@ -64,7 +64,7 @@

if TYPE_CHECKING:
from telegram import Message
from telegram.ext import ConversationHandler
from telegram.ext import ConversationHandler, JobQueue
from telegram.ext._applicationbuilder import InitApplicationBuilder
from telegram.ext._jobqueue import Job

Expand Down Expand Up @@ -151,8 +151,6 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AbstractAsyncContextManager)
update_queue (:class:`asyncio.Queue`): The synchronized queue that will contain the
updates.
updater (:class:`telegram.ext.Updater`): Optional. The updater used by this application.
job_queue (:class:`telegram.ext.JobQueue`): Optional. The :class:`telegram.ext.JobQueue`
instance to pass onto handler callbacks.
chat_data (:obj:`types.MappingProxyType`): A dictionary handlers can use to store data for
the chat. For each integer chat id, the corresponding value of this mapping is
available as :attr:`telegram.ext.CallbackContext.chat_data` in handler callbacks for
Expand Down Expand Up @@ -218,6 +216,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AbstractAsyncContextManager)
"_concurrent_updates_sem",
"_conversation_handler_conversations",
"_initialized",
"_job_queue",
"_running",
"_user_data",
"_user_ids_to_be_deleted_in_persistence",
Expand All @@ -228,7 +227,6 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AbstractAsyncContextManager)
"context_types",
"error_handlers",
"handlers",
"job_queue",
"persistence",
"post_init",
"post_shutdown",
Expand Down Expand Up @@ -264,7 +262,6 @@ def __init__(

self.bot = bot
self.update_queue = update_queue
self.job_queue = job_queue
self.context_types = context_types
self.updater = updater
self.handlers: Dict[int, List[BaseHandler]] = {}
Expand Down Expand Up @@ -306,6 +303,7 @@ def __init__(
# A number of low-level helpers for the internal logic
self._initialized = False
self._running = False
self._job_queue = job_queue
self.__update_fetcher_task: Optional[asyncio.Task] = None
self.__update_persistence_task: Optional[asyncio.Task] = None
self.__update_persistence_event = asyncio.Event()
Expand Down Expand Up @@ -337,6 +335,23 @@ def concurrent_updates(self) -> int:
"""
return self._concurrent_updates

@property
def job_queue(self) -> Optional["JobQueue"]:
"""
:class:`telegram.ext.JobQueue`: The :class:`JobQueue` used by the
:class:`telegram.ext.Application`.
.. seealso:: `Job Queue <https://github.com/python-telegram-bot/
python-telegram-bot/wiki/Extensions-%E2%80%93-JobQueue>`_
"""
if self._job_queue is None:
warn(
"No `JobQueue` set up. To use `JobQueue`, you must install PTB via "
"`pip install python-telegram-bot[job_queue]`.",
stacklevel=2,
)
return self._job_queue

async def initialize(self) -> None:
"""Initializes the Application by initializing:
Expand Down Expand Up @@ -511,8 +526,8 @@ async def start(self) -> None:
)
_logger.debug("Loop for updating persistence started")

if self.job_queue:
await self.job_queue.start() # type: ignore[union-attr]
if self._job_queue:
await self._job_queue.start() # type: ignore[union-attr]
_logger.debug("JobQueue started")

self.__update_fetcher_task = asyncio.create_task(
Expand Down Expand Up @@ -561,9 +576,9 @@ async def stop(self) -> None:
await self.__update_fetcher_task
_logger.debug("Application stopped fetching of updates.")

if self.job_queue:
if self._job_queue:
_logger.debug("Waiting for running jobs to finish")
await self.job_queue.stop(wait=True) # type: ignore[union-attr]
await self._job_queue.stop(wait=True) # type: ignore[union-attr]
_logger.debug("JobQueue stopped")

_logger.debug("Waiting for `create_task` calls to be processed")
Expand Down
9 changes: 8 additions & 1 deletion telegram/ext/_callbackcontext.py
Expand Up @@ -21,6 +21,7 @@

from telegram._callbackquery import CallbackQuery
from telegram._update import Update
from telegram._utils.warnings import warn
from telegram.ext._extbot import ExtBot
from telegram.ext._utils.types import BD, BT, CD, UD

Expand Down Expand Up @@ -389,7 +390,13 @@ def job_queue(self) -> Optional["JobQueue"]:
.. seealso:: `Job Queue <https://github.com/python-telegram-bot/
python-telegram-bot/wiki/Extensions-%E2%80%93-JobQueue>`_
"""
return self._application.job_queue
if self._application._job_queue is None: # pylint: disable=protected-access
warn(
"No `JobQueue` set up. To use `JobQueue`, you must install PTB via "
"`pip install python-telegram-bot[job_queue]`.",
stacklevel=2,
)
return self._application._job_queue # pylint: disable=protected-access

@property
def update_queue(self) -> "Queue[object]":
Expand Down
2 changes: 1 addition & 1 deletion telegram/ext/_conversationhandler.py
Expand Up @@ -676,7 +676,7 @@ def _schedule_job(
try:
# both job_queue & conversation_timeout are checked before calling _schedule_job
j_queue = application.job_queue
self.timeout_jobs[conversation_key] = j_queue.run_once(
self.timeout_jobs[conversation_key] = j_queue.run_once( # type: ignore[union-attr]
self._trigger_timeout,
self.conversation_timeout, # type: ignore[arg-type]
data=_ConversationTimeoutContext(conversation_key, update, application, context),
Expand Down
13 changes: 13 additions & 0 deletions tests/test_application.py
Expand Up @@ -201,6 +201,18 @@ async def post_shutdown(application: Application) -> None:
post_shutdown=None,
)

def test_job_queue(self, bot, app, recwarn):
expected_warning = (
"No `JobQueue` set up. To use `JobQueue`, you must install PTB via "
"`pip install python-telegram-bot[job_queue]`."
)
assert app.job_queue is app._job_queue
application = ApplicationBuilder().token(bot.token).job_queue(None).build()
assert application.job_queue is None
assert len(recwarn) == 1
assert str(recwarn[0].message) == expected_warning
assert recwarn[0].filename == __file__, "wrong stacklevel"

def test_custom_context_init(self, bot):
cc = ContextTypes(
context=CustomContext,
Expand Down Expand Up @@ -385,6 +397,7 @@ def test_builder(self, app):
builder_2.token(app.bot.token)

@pytest.mark.parametrize("job_queue", (True, False))
@pytest.mark.filterwarnings("ignore::telegram.warnings.PTBUserWarning")
async def test_start_stop_processing_updates(self, bot, job_queue):
# TODO: repeat a similar test for create_task, persistence processing and job queue
if job_queue:
Expand Down
2 changes: 2 additions & 0 deletions tests/test_applicationbuilder.py
Expand Up @@ -50,6 +50,7 @@ def builder():

@pytest.mark.skipif(TEST_WITH_OPT_DEPS, reason="Optional dependencies are installed")
class TestApplicationBuilderNoOptDeps:
@pytest.mark.filterwarnings("ignore::telegram.warnings.PTBUserWarning")
def test_init(self, builder):
builder.token("token")
app = builder.build()
Expand Down Expand Up @@ -416,6 +417,7 @@ def test_no_updater(self, bot, builder):
assert isinstance(app.job_queue, JobQueue)
assert app.job_queue.application is app

@pytest.mark.filterwarnings("ignore::telegram.warnings.PTBUserWarning")
def test_no_job_queue(self, bot, builder):
app = builder.token(bot.token).job_queue(None).build()
assert app.bot.token == bot.token
Expand Down
17 changes: 16 additions & 1 deletion tests/test_callbackcontext.py
Expand Up @@ -38,7 +38,7 @@


class TestCallbackContext:
def test_slot_behaviour(self, app, mro_slots, recwarn):
def test_slot_behaviour(self, app, mro_slots):
c = CallbackContext(app)
for attr in c.__slots__:
assert getattr(c, attr, "err") != "err", f"got extra slot '{attr}'"
Expand All @@ -58,6 +58,21 @@ def test_from_job(self, app):
assert callback_context.job_queue is app.job_queue
assert callback_context.update_queue is app.update_queue

def test_job_queue(self, bot, app, recwarn):
expected_warning = (
"No `JobQueue` set up. To use `JobQueue`, you must install PTB via "
"`pip install python-telegram-bot[job_queue]`."
)

callback_context = CallbackContext(app)
assert callback_context.job_queue is app.job_queue
app = ApplicationBuilder().job_queue(None).token(bot.token).build()
callback_context = CallbackContext(app)
assert callback_context.job_queue is None
assert len(recwarn) == 1
assert str(recwarn[0].message) == expected_warning
assert recwarn[0].filename == __file__, "wrong stacklevel"

def test_from_update(self, app):
update = Update(
0, message=Message(0, None, Chat(1, "chat"), from_user=User(1, "user", False))
Expand Down
19 changes: 10 additions & 9 deletions tests/test_conversationhandler.py
Expand Up @@ -996,11 +996,8 @@ async def test_no_running_job_queue_warning(self, app, bot, user1, recwarn, jq):
fallbacks=self.fallbacks,
conversation_timeout=0.5,
)
# save app.job_queue in temp variable jqueue
# and then set app.job_queue to None.
jqueue = app.job_queue
if not jq:
app.job_queue = None
app = ApplicationBuilder().token(bot.token).job_queue(None).build()
app.add_handler(handler)

message = Message(
Expand All @@ -1018,11 +1015,15 @@ async def test_no_running_job_queue_warning(self, app, bot, user1, recwarn, jq):
async with app:
await app.process_update(Update(update_id=0, message=message))
await asyncio.sleep(0.5)
assert len(recwarn) == 1
assert str(recwarn[0].message).startswith("Ignoring `conversation_timeout`")
assert ("is not running" if jq else "has no JobQueue.") in str(recwarn[0].message)
if jq:
assert len(recwarn) == 1
else:
assert len(recwarn) == 2
assert str(recwarn[0].message if jq else recwarn[1].message).startswith(
"Ignoring `conversation_timeout`"
)
assert ("is not running" if jq else "No `JobQueue` set up.") in str(recwarn[0].message)
# now set app.job_queue back to it's original value
app.job_queue = jqueue

async def test_schedule_job_exception(self, app, bot, user1, monkeypatch, caplog):
def mocked_run_once(*a, **kw):
Expand All @@ -1031,7 +1032,7 @@ def mocked_run_once(*a, **kw):
class DictJB(JobQueue):
pass

app.job_queue = DictJB()
app = ApplicationBuilder().token(bot.token).job_queue(DictJB()).build()
monkeypatch.setattr(app.job_queue, "run_once", mocked_run_once)
handler = ConversationHandler(
entry_points=self.entry_points,
Expand Down

0 comments on commit f8be97c

Please sign in to comment.