Add Notes and Warnings About Optional Dependencies (#3393)

This commit is contained in:
Aditya 2022-12-02 16:38:11 +05:30 committed by GitHub
parent 4123e7aa57
commit f8be97c12c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 88 additions and 21 deletions

View file

@ -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`
-------------------------------
@ -115,6 +116,7 @@ Dont 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`
--------------------------
@ -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
--------

View file

@ -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

View file

@ -10,6 +10,9 @@ See https://telegram.org/blog/passport for info about what telegram passport is.
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

View file

@ -16,6 +16,10 @@ Usage:
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

View file

@ -64,7 +64,7 @@ from telegram.ext._utils.types import BD, BT, CCT, CD, JQ, UD, ConversationKey,
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
@ -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
@ -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",
@ -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",
@ -264,7 +262,6 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AbstractAsyncContextManager)
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]] = {}
@ -306,6 +303,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AbstractAsyncContextManager)
# 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()
@ -337,6 +335,23 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AbstractAsyncContextManager)
"""
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:
@ -511,8 +526,8 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AbstractAsyncContextManager)
)
_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(
@ -561,9 +576,9 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AbstractAsyncContextManager)
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")

View file

@ -21,6 +21,7 @@ from typing import TYPE_CHECKING, Coroutine, Dict, Generic, List, Match, NoRetur
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
@ -389,7 +390,13 @@ class CallbackContext(Generic[BT, UD, CD, BD]):
.. 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]":

View file

@ -676,7 +676,7 @@ class ConversationHandler(BaseHandler[Update, CCT]):
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),

View file

@ -201,6 +201,18 @@ class TestApplication:
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,
@ -385,6 +397,7 @@ class TestApplication:
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:

View file

@ -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()
@ -416,6 +417,7 @@ class TestApplicationBuilder:
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

View file

@ -38,7 +38,7 @@ CallbackContext.refresh_data is tested in TestBasePersistence
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}'"
@ -58,6 +58,21 @@ class TestCallbackContext:
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))

View file

@ -996,11 +996,8 @@ class TestConversationHandler:
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(
@ -1018,11 +1015,15 @@ class TestConversationHandler:
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):
@ -1031,7 +1032,7 @@ class TestConversationHandler:
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,