Skip to content
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

warning to install optional dependencies #3393

Merged
merged 11 commits into from Dec 2, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
32 changes: 23 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,22 @@ 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
clot27 marked this conversation as resolved.
Show resolved Hide resolved
: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]`."
)
return self._job_queue

async def initialize(self) -> None:
"""Initializes the Application by initializing:

Expand Down Expand Up @@ -511,8 +525,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 +575,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
6 changes: 6 additions & 0 deletions 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,6 +390,11 @@ def job_queue(self) -> Optional["JobQueue"]:
.. seealso:: `Job Queue <https://github.com/python-telegram-bot/
python-telegram-bot/wiki/Extensions-%E2%80%93-JobQueue>`_
"""
if self._application.job_queue is None:
warn(
"No `JobQueue` set up. To use `JobQueue`, you must install PTB via "
"`pip install python-telegram-bot[job_queue]`."
)
Bibo-Joshi marked this conversation as resolved.
Show resolved Hide resolved
return self._application.job_queue

@property
Expand Down
29 changes: 21 additions & 8 deletions tests/test_application.py
Expand Up @@ -169,7 +169,7 @@ async def post_shutdown(application: Application) -> None:
)
assert app.bot is bot
assert app.update_queue is update_queue
assert app.job_queue is job_queue
assert app._job_queue is job_queue
assert app.persistence is persistence
assert app.context_types is context_types
assert app.updater is updater
Expand Down 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]`."
Bibo-Joshi marked this conversation as resolved.
Show resolved Hide resolved
)
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"
Bibo-Joshi marked this conversation as resolved.
Show resolved Hide resolved

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 Expand Up @@ -427,9 +440,9 @@ async def callback(u, c):
# app.stop() should not stop the updater!
assert app.updater.running
if job_queue:
assert not app.job_queue.scheduler.running
assert not app._job_queue.scheduler.running
Bibo-Joshi marked this conversation as resolved.
Show resolved Hide resolved
else:
assert app.job_queue is None
assert app._job_queue is None
await app.update_queue.put(2)
await asyncio.sleep(0.05)
assert not app.update_queue.empty()
Expand Down Expand Up @@ -1365,7 +1378,7 @@ def thread_target():
# Check that everything's running
assertions["app_running"] = app.running
assertions["updater_running"] = app.updater.running
assertions["job_queue_running"] = app.job_queue.scheduler.running
assertions["job_queue_running"] = app._job_queue.scheduler.running

# Check that we're getting updates
update_event.wait()
Expand All @@ -1383,7 +1396,7 @@ def thread_target():
# # Assert that everything has stopped running
assertions["app_not_running"] = not app.running
assertions["updater_not_running"] = not app.updater.running
assertions["job_queue_not_running"] = not app.job_queue.scheduler.running
assertions["job_queue_not_running"] = not app._job_queue.scheduler.running

monkeypatch.setattr(app.bot, "get_updates", get_updates)
app.add_error_handler(self.error_handler_context)
Expand Down Expand Up @@ -1572,7 +1585,7 @@ def thread_target():
# Check that everything's running
assertions["app_running"] = app.running
assertions["updater_running"] = app.updater.running
assertions["job_queue_running"] = app.job_queue.scheduler.running
assertions["job_queue_running"] = app._job_queue.scheduler.running

# Check that we're getting updates
loop = asyncio.new_event_loop()
Expand All @@ -1589,7 +1602,7 @@ def thread_target():
# # Assert that everything has stopped running
assertions["app_not_running"] = not app.running
assertions["updater_not_running"] = not app.updater.running
assertions["job_queue_not_running"] = not app.job_queue.scheduler.running
assertions["job_queue_not_running"] = not app._job_queue.scheduler.running

monkeypatch.setattr(app.bot, "set_webhook", set_webhook)
monkeypatch.setattr(app.bot, "delete_webhook", delete_webhook)
Expand Down Expand Up @@ -1945,4 +1958,4 @@ async def abort_app():
if platform.system() == "Windows":
assert received_signals == []
else:
assert received_signals == [signal.SIGINT, signal.SIGTERM, signal.SIGABRT]
assert received_signals == [signal.SIGINT, signal.SIGTERM, signal.SIGABRT]
Bibo-Joshi marked this conversation as resolved.
Show resolved Hide resolved
17 changes: 9 additions & 8 deletions tests/test_applicationbuilder.py
Expand Up @@ -53,7 +53,7 @@ class TestApplicationBuilderNoOptDeps:
def test_init(self, builder):
builder.token("token")
app = builder.build()
assert app.job_queue is None
assert app._job_queue is None


@pytest.mark.skipif(not TEST_WITH_OPT_DEPS, reason="Optional dependencies not installed")
Expand Down Expand Up @@ -127,8 +127,8 @@ class Client:
assert app.updater.bot is app.bot
assert app.updater.update_queue is app.update_queue

assert isinstance(app.job_queue, JobQueue)
assert app.job_queue.application is app
assert isinstance(app._job_queue, JobQueue)
assert app._job_queue.application is app

assert app.persistence is None
assert app.post_init is None
Expand Down Expand Up @@ -370,8 +370,8 @@ async def post_shutdown(app: Application) -> None:
.post_shutdown(post_shutdown)
.arbitrary_callback_data(True)
).build()
assert app.job_queue is job_queue
assert app.job_queue.application is app
assert app._job_queue is job_queue
assert app._job_queue.application is app
assert app.persistence is persistence
assert app.persistence.bot is app.bot
assert app.update_queue is update_queue
Expand Down Expand Up @@ -413,12 +413,13 @@ def test_no_updater(self, bot, builder):
assert app.bot.token == bot.token
assert app.updater is None
assert isinstance(app.update_queue, asyncio.Queue)
assert isinstance(app.job_queue, JobQueue)
assert app.job_queue.application is app
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
assert app.job_queue is None
assert app._job_queue is None
assert isinstance(app.update_queue, asyncio.Queue)
assert isinstance(app.updater, Updater)
31 changes: 23 additions & 8 deletions tests/test_callbackcontext.py
Expand Up @@ -38,15 +38,15 @@


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}'"
assert not c.__dict__, f"got missing slot(s): {c.__dict__}"
assert len(mro_slots(c)) == len(set(mro_slots(c))), "duplicate slot"

def test_from_job(self, app):
job = app.job_queue.run_once(lambda x: x, 10)
job = app._job_queue.run_once(lambda x: x, 10)

callback_context = CallbackContext.from_job(job, app)

Expand All @@ -55,9 +55,24 @@ def test_from_job(self, app):
assert callback_context.user_data is None
assert callback_context.bot_data is app.bot_data
assert callback_context.bot is app.bot
assert callback_context.job_queue is app.job_queue
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
Bibo-Joshi marked this conversation as resolved.
Show resolved Hide resolved
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 All @@ -69,7 +84,7 @@ def test_from_update(self, app):
assert callback_context.user_data == {}
assert callback_context.bot_data is app.bot_data
assert callback_context.bot is app.bot
assert callback_context.job_queue is app.job_queue
assert callback_context.job_queue is app._job_queue
assert callback_context.update_queue is app.update_queue

callback_context_same_user_chat = CallbackContext.from_update(update, app)
Expand Down Expand Up @@ -99,7 +114,7 @@ def test_from_update_not_update(self, app):
assert callback_context.user_data is None
assert callback_context.bot_data is app.bot_data
assert callback_context.bot is app.bot
assert callback_context.job_queue is app.job_queue
assert callback_context.job_queue is app._job_queue
assert callback_context.update_queue is app.update_queue

callback_context = CallbackContext.from_update("", app)
Expand All @@ -108,7 +123,7 @@ def test_from_update_not_update(self, app):
assert callback_context.user_data is None
assert callback_context.bot_data is app.bot_data
assert callback_context.bot is app.bot
assert callback_context.job_queue is app.job_queue
assert callback_context.job_queue is app._job_queue
assert callback_context.update_queue is app.update_queue

def test_from_error(self, app):
Expand All @@ -127,7 +142,7 @@ def test_from_error(self, app):
assert callback_context.user_data == {}
assert callback_context.bot_data is app.bot_data
assert callback_context.bot is app.bot
assert callback_context.job_queue is app.job_queue
assert callback_context.job_queue is app._job_queue
assert callback_context.update_queue is app.update_queue
assert callback_context.coroutine is coroutine

Expand All @@ -144,7 +159,7 @@ def test_from_error_job_user_chat_data(self, app):
assert callback_context.user_data == {}
assert callback_context.bot_data is app.bot_data
assert callback_context.bot is app.bot
assert callback_context.job_queue is app.job_queue
assert callback_context.job_queue is app._job_queue
assert callback_context.update_queue is app.update_queue
assert callback_context.job is job

Expand Down