コード例 #1
0
    def _build_request(self, get_updates: bool) -> BaseRequest:
        prefix = "_get_updates_" if get_updates else "_"
        if not isinstance(getattr(self, f"{prefix}request"), DefaultValue):
            return getattr(self, f"{prefix}request")

        proxy_url = DefaultValue.get_value(getattr(self, f"{prefix}proxy_url"))
        if get_updates:
            connection_pool_size = (DefaultValue.get_value(
                getattr(self, f"{prefix}connection_pool_size")) or 1)
        else:
            connection_pool_size = (DefaultValue.get_value(
                getattr(self, f"{prefix}connection_pool_size")) or 256)

        timeouts = dict(
            connect_timeout=getattr(self, f"{prefix}connect_timeout"),
            read_timeout=getattr(self, f"{prefix}read_timeout"),
            write_timeout=getattr(self, f"{prefix}write_timeout"),
            pool_timeout=getattr(self, f"{prefix}pool_timeout"),
        )
        # Get timeouts that were actually set-
        effective_timeouts = {
            key: value
            for key, value in timeouts.items()
            if not isinstance(value, DefaultValue)
        }

        return HTTPXRequest(
            connection_pool_size=connection_pool_size,
            proxy_url=proxy_url,
            **effective_timeouts,
        )
コード例 #2
0
    def _insert_defaults(self, data: Dict[str, object]) -> None:
        """Inserts the defaults values for optional kwargs for which tg.ext.Defaults provides
        convenience functionality, i.e. the kwargs with a tg.utils.helpers.DefaultValue default

        data is edited in-place. As timeout is not passed via the kwargs, it needs to be passed
        separately and gets returned.

        This can only work, if all kwargs that may have defaults are passed in data!
        """
        # if we have Defaults, we
        # 1) replace all DefaultValue instances with the relevant Defaults value. If there is none,
        #    we fall back to the default value of the bot method
        # 2) convert all datetime.datetime objects to timestamps wrt the correct default timezone
        # 3) set the correct parse_mode for all InputMedia objects
        for key, val in data.items():
            # 1)
            if isinstance(val, DefaultValue):
                data[key] = (self.defaults.api_defaults.get(key, val.value)
                             if self.defaults else DefaultValue.get_value(val))

            # 2)
            elif isinstance(val, datetime):
                data[key] = to_timestamp(
                    val,
                    tzinfo=self.defaults.tzinfo if self.defaults else None)

            # 3)
            elif isinstance(val,
                            InputMedia) and val.parse_mode is DEFAULT_NONE:
                val.parse_mode = self.defaults.parse_mode if self.defaults else None
            elif key == "media" and isinstance(val, list):
                for media in val:
                    if media.parse_mode is DEFAULT_NONE:
                        media.parse_mode = self.defaults.parse_mode if self.defaults else None
コード例 #3
0
 def test_slot_behaviour(self, mro_slots):
     inst = DefaultValue(1)
     for attr in inst.__slots__:
         assert getattr(inst, attr,
                        "err") != "err", f"got extra slot '{attr}'"
     assert len(mro_slots(inst)) == len(set(
         mro_slots(inst))), "duplicate slot"
コード例 #4
0
class TestDefaultValue:
    def test_slot_behaviour(self, mro_slots):
        inst = DefaultValue(1)
        for attr in inst.__slots__:
            assert getattr(inst, attr,
                           "err") != "err", f"got extra slot '{attr}'"
        assert len(mro_slots(inst)) == len(set(
            mro_slots(inst))), "duplicate slot"

    def test_identity(self):
        df_1 = DefaultValue(1)
        df_2 = DefaultValue(2)
        assert df_1 is not df_2
        assert df_1 != df_2

    @pytest.mark.parametrize(
        "value,expected",
        [
            ({}, False),
            ({
                1: 2
            }, True),
            (None, False),
            (True, True),
            (1, True),
            (0, False),
            (False, False),
            ([], False),
            ([1], True),
        ],
    )
    def test_truthiness(self, value, expected):
        assert bool(DefaultValue(value)) == expected

    @pytest.mark.parametrize("value", [
        "string", 1, True, [1, 2, 3], {
            1: 3
        },
        DefaultValue(1),
        User(1, "first", False)
    ])
    def test_string_representations(self, value):
        df = DefaultValue(value)
        assert str(df) == f"DefaultValue({value})"
        assert repr(df) == repr(value)

    def test_as_function_argument(self):
        default_one = DefaultValue(1)

        def foo(arg=default_one):
            if arg is default_one:
                return 1
            else:
                return 2

        assert foo() == 1
        assert foo(None) == 2
        assert foo(1) == 2
コード例 #5
0
    def test_as_function_argument(self):
        default_one = DefaultValue(1)

        def foo(arg=default_one):
            if arg is default_one:
                return 1
            else:
                return 2

        assert foo() == 1
        assert foo(None) == 2
        assert foo(1) == 2
コード例 #6
0
    def build(
        self: "ApplicationBuilder[BT, CCT, UD, CD, BD, JQ]",
    ) -> Application[BT, CCT, UD, CD, BD, JQ]:
        """Builds a :class:`telegram.ext.Application` with the provided arguments.

        Calls :meth:`telegram.ext.JobQueue.set_application` and
        :meth:`telegram.ext.BasePersistence.set_bot` if appropriate.

        Returns:
            :class:`telegram.ext.Application`
        """
        job_queue = DefaultValue.get_value(self._job_queue)
        persistence = DefaultValue.get_value(self._persistence)
        # If user didn't set updater
        if isinstance(self._updater, DefaultValue) or self._updater is None:
            if isinstance(self._bot, DefaultValue):  # and didn't set a bot
                bot: Bot = self._build_ext_bot()  # build a bot
            else:
                bot = self._bot
            # now also build an updater/update_queue for them
            update_queue = DefaultValue.get_value(self._update_queue)

            if self._updater is None:
                updater = None
            else:
                updater = Updater(bot=bot, update_queue=update_queue)
        else:  # if they set an updater, get all necessary attributes for Application from Updater:
            updater = self._updater
            bot = self._updater.bot
            update_queue = self._updater.update_queue

        application: Application[
            BT, CCT, UD, CD, BD, JQ] = DefaultValue.get_value(  # pylint: disable=not-callable
                self._application_class)(
                    bot=bot,
                    update_queue=update_queue,
                    updater=updater,
                    concurrent_updates=DefaultValue.get_value(
                        self._concurrent_updates),
                    job_queue=job_queue,
                    persistence=persistence,
                    context_types=DefaultValue.get_value(self._context_types),
                    post_init=self._post_init,
                    post_shutdown=self._post_shutdown,
                    **self.
                    _application_kwargs,  # For custom Application subclasses
                )

        if job_queue is not None:
            job_queue.set_application(application)

        if persistence is not None:
            # This raises an exception if persistence.store_data.callback_data is True
            # but self.bot is not an instance of ExtBot - so no need to check that later on
            persistence.set_bot(bot)

        return application
コード例 #7
0
    def _build_ext_bot(self) -> ExtBot:
        if isinstance(self._token, DefaultValue):
            raise RuntimeError("No bot token was set.")

        return ExtBot(
            token=self._token,
            base_url=DefaultValue.get_value(self._base_url),
            base_file_url=DefaultValue.get_value(self._base_file_url),
            private_key=DefaultValue.get_value(self._private_key),
            private_key_password=DefaultValue.get_value(
                self._private_key_password),
            defaults=DefaultValue.get_value(self._defaults),
            arbitrary_callback_data=DefaultValue.get_value(
                self._arbitrary_callback_data),
            request=self._build_request(get_updates=False),
            get_updates_request=self._build_request(get_updates=True),
        )
コード例 #8
0
 def __init__(self: "InitApplicationBuilder"):
     self._token: DVInput[str] = DefaultValue("")
     self._base_url: DVInput[str] = DefaultValue(
         "https://api.telegram.org/bot")
     self._base_file_url: DVInput[str] = DefaultValue(
         "https://api.telegram.org/file/bot")
     self._connection_pool_size: DVInput[int] = DEFAULT_NONE
     self._proxy_url: DVInput[str] = DEFAULT_NONE
     self._connect_timeout: ODVInput[float] = DEFAULT_NONE
     self._read_timeout: ODVInput[float] = DEFAULT_NONE
     self._write_timeout: ODVInput[float] = DEFAULT_NONE
     self._pool_timeout: ODVInput[float] = DEFAULT_NONE
     self._request: DVInput["BaseRequest"] = DEFAULT_NONE
     self._get_updates_connection_pool_size: DVInput[int] = DEFAULT_NONE
     self._get_updates_proxy_url: DVInput[str] = DEFAULT_NONE
     self._get_updates_connect_timeout: ODVInput[float] = DEFAULT_NONE
     self._get_updates_read_timeout: ODVInput[float] = DEFAULT_NONE
     self._get_updates_write_timeout: ODVInput[float] = DEFAULT_NONE
     self._get_updates_pool_timeout: ODVInput[float] = DEFAULT_NONE
     self._get_updates_request: DVInput["BaseRequest"] = DEFAULT_NONE
     self._private_key: ODVInput[bytes] = DEFAULT_NONE
     self._private_key_password: ODVInput[bytes] = DEFAULT_NONE
     self._defaults: ODVInput["Defaults"] = DEFAULT_NONE
     self._arbitrary_callback_data: DVInput[Union[bool,
                                                  int]] = DEFAULT_FALSE
     self._bot: DVInput[Bot] = DEFAULT_NONE
     self._update_queue: DVInput[Queue] = DefaultValue(Queue())
     self._job_queue: ODVInput["JobQueue"] = DefaultValue(JobQueue())
     self._persistence: ODVInput["BasePersistence"] = DEFAULT_NONE
     self._context_types: DVInput[ContextTypes] = DefaultValue(
         ContextTypes())
     self._application_class: DVInput[Type[Application]] = DefaultValue(
         Application)
     self._application_kwargs: Dict[str, object] = {}
     self._concurrent_updates: DVInput[Union[int, bool]] = DEFAULT_FALSE
     self._updater: ODVInput[Updater] = DEFAULT_NONE
     self._post_init: Optional[Callable[[Application],
                                        Coroutine[Any, Any, None]]] = None
     self._post_shutdown: Optional[Callable[[Application],
                                            Coroutine[Any, Any,
                                                      None]]] = None
コード例 #9
0
def check_defaults_type(ptb_param: inspect.Parameter) -> bool:
    return True if DefaultValue.get_value(ptb_param.default) is None else False
コード例 #10
0
    async def handle_update(  # type: ignore[override]
        self,
        update: Update,
        application: "Application",
        check_result: _CheckUpdateType,
        context: CallbackContext,
    ) -> Optional[object]:
        """Send the update to the callback for the current state and BaseHandler

        Args:
            check_result: The result from :meth:`check_update`. For this handler it's a tuple of
                the conversation state, key, handler, and the handler's check result.
            update (:class:`telegram.Update`): Incoming telegram update.
            application (:class:`telegram.ext.Application`): Application that originated the
                update.
            context (:class:`telegram.ext.CallbackContext`): The context as provided by
                the application.

        """
        current_state, conversation_key, handler, handler_check_result = check_result
        raise_dp_handler_stop = False

        async with self._timeout_jobs_lock:
            # Remove the old timeout job (if present)
            timeout_job = self.timeout_jobs.pop(conversation_key, None)

            if timeout_job is not None:
                timeout_job.schedule_removal()

        # Resolution order of "block":
        # 1. Setting of the selected handler
        # 2. Setting of the ConversationHandler
        # 3. Default values of the bot
        if handler.block is not DEFAULT_TRUE:
            block = handler.block
        else:
            if self._block is not DEFAULT_TRUE:
                block = self._block
            elif isinstance(application.bot,
                            ExtBot) and application.bot.defaults is not None:
                block = application.bot.defaults.block
            else:
                block = DefaultValue.get_value(handler.block)

        try:  # Now create task or await the callback
            if block:
                new_state: object = await handler.handle_update(
                    update, application, handler_check_result, context)
            else:
                new_state = application.create_task(
                    coroutine=handler.handle_update(update, application,
                                                    handler_check_result,
                                                    context),
                    update=update,
                )
        except ApplicationHandlerStop as exception:
            new_state = exception.state
            raise_dp_handler_stop = True
        async with self._timeout_jobs_lock:
            if self.conversation_timeout:
                if application.job_queue is None:
                    warn(
                        "Ignoring `conversation_timeout` because the Application has no JobQueue.",
                    )
                elif not application.job_queue.scheduler.running:
                    warn(
                        "Ignoring `conversation_timeout` because the Applications JobQueue is "
                        "not running.", )
                else:
                    # Add the new timeout job
                    # checking if the new state is self.END is done in _schedule_job
                    if isinstance(new_state, asyncio.Task):
                        application.create_task(
                            self._schedule_job_delayed(new_state, application,
                                                       update, context,
                                                       conversation_key),
                            update=update,
                        )
                    else:
                        self._schedule_job(new_state, application, update,
                                           context, conversation_key)

        if isinstance(self.map_to_parent,
                      dict) and new_state in self.map_to_parent:
            self._update_state(self.END, conversation_key)
            if raise_dp_handler_stop:
                raise ApplicationHandlerStop(self.map_to_parent.get(new_state))
            return self.map_to_parent.get(new_state)

        if current_state != self.WAITING:
            self._update_state(new_state, conversation_key)

        if raise_dp_handler_stop:
            # Don't pass the new state here. If we're in a nested conversation, the parent is
            # expecting None as return value.
            raise ApplicationHandlerStop()
        # Signals a possible parent conversation to stay in the current state
        return None
コード例 #11
0
 def test_string_representations(self, value):
     df = DefaultValue(value)
     assert str(df) == f"DefaultValue({value})"
     assert repr(df) == repr(value)
コード例 #12
0
 def test_truthiness(self, value, expected):
     assert bool(DefaultValue(value)) == expected
コード例 #13
0
 def test_identity(self):
     df_1 = DefaultValue(1)
     df_2 = DefaultValue(2)
     assert df_1 is not df_2
     assert df_1 != df_2