Migration guide 2.x -> 3.0 (#1143)

* Initial commit for docs cleanup

* Update migration guide

* More docs

* Added changes description

* Small fixes
This commit is contained in:
Alex Root Junior 2023-07-29 22:36:12 +03:00 committed by GitHub
parent 2ecf9cefd7
commit 56f0d9d220
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 363 additions and 85 deletions

View file

@ -1,3 +1,5 @@
.. _enums:
#####
Enums
#####

1
CHANGES/1143.doc.rst Normal file
View file

@ -0,0 +1 @@
Improved docs, added basic migration guide (will be expanded later)

View file

@ -53,6 +53,12 @@ DEFAULT_TIMEOUT: Final[float] = 60.0
class BaseSession(abc.ABC):
"""
This is base class for all HTTP sessions in aiogram.
If you want to create your own session, you must inherit from this class.
"""
def __init__(
self,
api: TelegramAPIServer = PRODUCTION,

View file

@ -53,7 +53,7 @@ class Router:
self.chat_member = TelegramEventObserver(router=self, event_name="chat_member")
self.chat_join_request = TelegramEventObserver(router=self, event_name="chat_join_request")
self.errors = TelegramEventObserver(router=self, event_name="error")
self.errors = self.error = TelegramEventObserver(router=self, event_name="error")
self.startup = EventObserver()
self.shutdown = EventObserver()
@ -184,6 +184,12 @@ class Router:
router.sub_routers.append(self)
def include_routers(self, *routers: Router) -> None:
"""
Attach multiple routers.
:param routers:
:return:
"""
if not routers:
raise ValueError("At least one router must be provided")
for router in routers:

View file

@ -6,10 +6,16 @@ from aiogram.utils.link import docs_url
class AiogramError(Exception):
pass
"""
Base exception for all aiogram errors.
"""
class DetailedAiogramError(AiogramError):
"""
Base exception for all aiogram errors with detailed message.
"""
url: Optional[str] = None
def __init__(self, message: str) -> None:
@ -26,14 +32,24 @@ class DetailedAiogramError(AiogramError):
class CallbackAnswerException(AiogramError):
pass
"""
Exception for callback answer.
"""
class UnsupportedKeywordArgument(DetailedAiogramError):
"""
Exception raised when a keyword argument is passed as filter.
"""
url = docs_url("migration_2_to_3.html", fragment_="filtering-events")
class TelegramAPIError(DetailedAiogramError):
"""
Base exception for all Telegram API errors.
"""
label: str = "Telegram server says"
def __init__(
@ -50,10 +66,18 @@ class TelegramAPIError(DetailedAiogramError):
class TelegramNetworkError(TelegramAPIError):
"""
Base exception for all Telegram network errors.
"""
label = "HTTP Client says"
class TelegramRetryAfter(TelegramAPIError):
"""
Exception raised when flood control exceeds.
"""
url = "https://core.telegram.org/bots/faq#my-bot-is-hitting-limits-how-do-i-avoid-this"
def __init__(
@ -73,6 +97,10 @@ class TelegramRetryAfter(TelegramAPIError):
class TelegramMigrateToChat(TelegramAPIError):
"""
Exception raised when chat has been migrated to a supergroup.
"""
url = "https://core.telegram.org/bots/api#responseparameters"
def __init__(
@ -90,38 +118,66 @@ class TelegramMigrateToChat(TelegramAPIError):
class TelegramBadRequest(TelegramAPIError):
pass
"""
Exception raised when request is malformed.
"""
class TelegramNotFound(TelegramAPIError):
pass
"""
Exception raised when chat, message, user, etc. not found.
"""
class TelegramConflictError(TelegramAPIError):
pass
"""
Exception raised when bot token is already used by another application in polling mode.
"""
class TelegramUnauthorizedError(TelegramAPIError):
pass
"""
Exception raised when bot token is invalid.
"""
class TelegramForbiddenError(TelegramAPIError):
pass
"""
Exception raised when bot is kicked from chat or etc.
"""
class TelegramServerError(TelegramAPIError):
pass
"""
Exception raised when Telegram server returns 5xx error.
"""
class RestartingTelegram(TelegramServerError):
pass
"""
Exception raised when Telegram server is restarting.
It seems like this error is not used by Telegram anymore,
but it's still here for backward compatibility.
Currently, you should expect that Telegram can raise RetryAfter (with timeout 5 seconds)
error instead of this one.
"""
class TelegramEntityTooLarge(TelegramNetworkError):
"""
Exception raised when you are trying to send a file that is too large.
"""
url = "https://core.telegram.org/bots/api#sending-files"
class ClientDecodeError(AiogramError):
"""
Exception raised when client can't decode response. (Malformed response, etc.)
"""
def __init__(self, message: str, original: Exception, data: Any) -> None:
self.message = message
self.original = original

View file

@ -532,7 +532,7 @@ def as_marked_list(*items: NodeType, marker: str = "- ") -> Text:
Wrap elements as marked list
:param items:
:param marker: line marker, by default is :code:`- `
:param marker: line marker, by default is '- '
:return: Text
"""
return as_list(*(Text(marker, item) for item in items))
@ -544,7 +544,7 @@ def as_numbered_list(*items: NodeType, start: int = 1, fmt: str = "{}. ") -> Tex
:param items:
:param start: initial number, by default 1
:param fmt: number format, by default :code:`{}. `
:param fmt: number format, by default '{}. '
:return: Text
"""
return as_list(*(Text(fmt.format(index), item) for index, item in enumerate(items, start)))

View file

@ -1,3 +1,5 @@
.. _enums:
#####
Enums
#####

View file

@ -1,14 +1,14 @@
Use Custom API server
=====================
.. autoclass:: aiogram.client.telegram.TelegramAPIServer
:members:
For example, if you want to use self-hosted API server:
.. code-block:: python3
.. code-block:: python
session = AiohttpSession(
api=TelegramAPIServer.from_base('http://localhost:8082')
)
bot = Bot(..., session=session)
.. autoclass:: aiogram.client.telegram.TelegramAPIServer
:members:

View file

@ -8,3 +8,4 @@ Client sessions is used for interacting with API server.
custom_server
base
aiohttp
middleware

View file

@ -0,0 +1,75 @@
##########################
Client session middlewares
##########################
In some cases you may want to add some middlewares to the client session to customize the behavior of the client.
Some useful cases that is:
- Log the outgoing requests
- Customize the request parameters
- Handle rate limiting errors and retry the request
- others ...
So, you can do it using client session middlewares.
A client session middleware is a function (or callable class) that receives the request and the next middleware to call.
The middleware can modify the request and then call the next middleware to continue the request processing.
How to register client session middleware?
==========================================
Register using register method
------------------------------
.. code-block:: python
bot.session.middleware(RequestLogging(ignore_methods=[GetUpdates]))
Register using decorator
------------------------
.. code-block:: python
@bot.session.middleware()
async def my_middleware(
make_request: NextRequestMiddlewareType[TelegramType],
bot: "Bot",
method: TelegramMethod[TelegramType],
) -> Response[TelegramType]:
# do something with request
return await make_request(bot, method)
Example
=======
Class based session middleware
------------------------------
.. literalinclude:: ../../../aiogram/client/session/middlewares/request_logging.py
:lines: 16-
:language: python
:linenos:
.. note::
this middlewware is already implemented inside aiogram, so, if you want to use it you can
just import it :code:`from aiogram.client.session.middlewares.request_logging import RequestLogging`
Function based session middleware
---------------------------------
.. code-block:: python
async def __call__(
self,
make_request: NextRequestMiddlewareType[TelegramType],
bot: "Bot",
method: TelegramMethod[TelegramType],
) -> Response[TelegramType]:
try:
# do something with request
return await make_request(bot, method)
finally:
# do something after request

View file

@ -1,10 +0,0 @@
##########
ErrorEvent
##########
.. automodule:: aiogram.types.error_event
:members:
:member-order: bysource
:undoc-members: True
:exclude-members: model_config,model_fields

View file

@ -28,7 +28,6 @@ extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.ifconfig",
"sphinx.ext.intersphinx",
"sphinx-prompt",
"sphinx_substitution_extensions",
"sphinx_copybutton",
"sphinxcontrib.towncrier.ext",
@ -67,6 +66,6 @@ texinfo_documents = [
# add_module_names = False
towncrier_draft_autoversion_mode = 'draft'
towncrier_draft_autoversion_mode = "draft"
towncrier_draft_include_empty = False
towncrier_draft_working_directory = Path(__file__).parent.parent

View file

@ -0,0 +1,49 @@
######
Errors
######
Handling errors
===============
Is recommended way that you should use errors inside handlers using try-except block,
but in common cases you can use global errors handler at router or dispatcher level.
If you specify errors handler for router - it will be used for all handlers inside this router.
If you specify errors handler for dispatcher - it will be used for all handlers inside all routers.
.. code-block:: python
@router.error(ExceptionTypeFilter(MyCustomException), F.update.message.as_("message"))
async def handle_my_custom_exception(event: ErrorEvent, message: Message):
# do something with error
await message.answer("Oops, something went wrong!")
@router.error()
async def error_handler(event: ErrorEvent):
logger.critical("Critical error caused by %s", event.exception, exc_info=True)
# do something with error
...
.. _error-event:
ErrorEvent
==========
.. automodule:: aiogram.types.error_event
:members:
:member-order: bysource
:undoc-members: True
:exclude-members: model_config,model_fields
.. _error-types:
Error types
===========
.. automodule:: aiogram.exceptions
:members:
:member-order: bysource

View file

@ -1,3 +1,5 @@
.. _callback-data-factory
==============================
Callback Data Factory & Filter
==============================

View file

@ -17,7 +17,6 @@ Dispatcher is subclass of router and should be always is root router.
.. toctree::
observer
router
dispatcher
class_based_handlers/index
@ -25,3 +24,4 @@ Dispatcher is subclass of router and should be always is root router.
middlewares
finite_state_machine/index
flags
errors

View file

@ -1,3 +1,5 @@
.. _middlewares:
===========
Middlewares
===========

View file

@ -1,26 +0,0 @@
########
Observer
########
Observer is used for filtering and handling different events. That is part of internal API with some public methods and is recommended to don't use methods is not listed here.
In `aiogram` framework is available two variants of observer:
- `EventObserver <#eventobserver>`__
- `TelegramEventObserver <#telegrameventobserver>`__
EventObserver
=============
.. autoclass:: aiogram.dispatcher.event.event.EventObserver
:members: register, trigger, __call__
:member-order: bysource
TelegramEventObserver
=====================
.. autoclass:: aiogram.dispatcher.event.telegram.TelegramEventObserver
:members: register, trigger, __call__, bind_filter, middleware, outer_middleware
:member-order: bysource

View file

@ -3,10 +3,12 @@ Router
######
.. autoclass:: aiogram.dispatcher.router.Router
:members: __init__, include_router
:members: __init__, include_router, include_routers, resolve_used_update_types
:show-inheritance:
.. _Event observers:
Event observers
===============
@ -19,19 +21,6 @@ Here is the list of available observers and examples of how to register handlers
In these examples only decorator-style registering handlers are used, but if you don't like @decorators just use :obj:`<event type>.register(...)` method instead.
Update
------
.. code-block:: python
@router.update()
async def message_handler(update: types.Update) -> Any: pass
.. note::
By default Router already has an update handler which route all event types to another observers.
Message
-------
@ -143,9 +132,12 @@ Errors
@router.errors()
async def error_handler(exception: ErrorEvent) -> Any: pass
Is useful for handling errors from other handlers
Is useful for handling errors from other handlers, error event described :ref:`here <error-event>`
.. _Nested routers:
Nested routers
==============
@ -159,8 +151,8 @@ Nested routers
Example:
.. code-block:: python
:caption: module_2.py
:name: module_2
:caption: module_1.py
:name: module_1
router2 = Router()
@ -170,7 +162,7 @@ Example:
.. code-block:: python
:caption: module_2.py
:name: module_1
:name: module_2
from module_2 import router2
@ -179,6 +171,22 @@ Example:
router1.include_router(router2)
Update
------
.. code-block:: python
@dispatcher.update()
async def message_handler(update: types.Update) -> Any: pass
.. warning::
The only root Router (Dispatcher) can handle this type of event.
.. note::
Dispatcher already has default handler for this event type, so you can use it for handling all updates that are not handled by any other handlers.
How it works?
-------------

View file

@ -12,6 +12,7 @@ Contents
:maxdepth: 3
install
migration_2_to_3
api/index
dispatcher/index
utils/index

View file

@ -2,9 +2,6 @@
Installation
############
Stable (2.x)
============
From PyPI
---------
@ -35,10 +32,3 @@ From GitHub
.. code-block:: bash
pip install https://github.com/aiogram/aiogram/archive/refs/heads/dev-3.x.zip
From AUR
--------
.. code-block:: bash
yay -S python-aiogram3

114
docs/migration_2_to_3.rst Normal file
View file

@ -0,0 +1,114 @@
==========================
Migration FAQ (2.x -> 3.0)
==========================
.. danger::
This guide is still in progress.
This version introduces much many breaking changes and architectural improvements,
helping to reduce global variables count in your code, provides useful mechanisms
to separate your code to modules or just make sharable modules via packages on the PyPi,
makes middlewares and filters more controllable and others.
On this page you can read about points that changed corresponding to last stable 2.x version.
.. note::
This page is most like a detailed changelog than a migration guide,
but it will be updated in the future.
Feel free to contribute to this page, if you find something that is not mentioned here.
Dispatcher
==========
- :class:`Dispatcher` class no longer accepts the `Bot` instance into the initializer,
it should be passed to dispatcher only for starting polling or handling event from webhook.
Also this way adds possibility to use multiple bot instances at the same time ("multibot")
- :class:`Dispatcher` now can be extended with another Dispatcher-like
thing named :class:`Router` (:ref:`Read more » <Nested routers>`).
With routes you can easily separate your code to multiple modules
and may be share this modules between projects.
- Removed the **_handler** suffix from all event handler decorators and registering methods.
(:ref:`Read more » <Event observers>`)
- Executor entirely removed, now you can use Dispatcher directly to start polling or webhook.
Filtering events
================
- Keyword filters can no more be used, use filters explicitly. (`Read more » <https://github.com/aiogram/aiogram/issues/942>`_)
- In due to keyword filters was removed all enabled by default filters (state and content_type now is not enabled),
so you should specify them explicitly if you want to use.
For example instead of using :code:`@dp.message_handler(content_types=ContentType.PHOTO)`
you should use :code:`@router.message(F.photo)`
- Most of common filters is replaced by "magic filter". (:ref:`Read more » <magic-filters>`)
- Now by default message handler receives any content type,
if you want specific one just add the filters (Magic or any other)
- State filter now is not enabled by default, that's mean if you using :code:`state="*"` in v2
then you should not pass any state filter in v3, and vice versa,
if the state in v2 is not specified now you should specify the state.
- Added possibility to register per-router global filters, that helps to reduces
the number of repetitions in the code and makes easily way to control
for what each router will be used.
Bot API
=======
- Now all API methods is classes with validation (via `pydantic <https://docs.pydantic.dev/>`_)
(all API calls is also available as methods in the Bot class).
- Added more pre-defined Enums and moved into `aiogram.enums` sub-package. For example chat type enum now is
:class:`aiogram.enums.ChatType` instead of :class:`aiogram.types.chat.ChatType`.
(:ref:`Read more » <enums>`)
- Separated HTTP client session into container that can be reused between different
Bot instances in the application.
- API Exceptions is no more classified by specific message in due to Telegram has no documented error codes.
But all errors is classified by HTTP status code and for each method only one case can be caused with the same code,
so in most cases you should check that only error type (by status-code) without checking error message.
(:ref:`Read more » <error-types>`)
Middlewares
===========
- Middlewares can now control a execution context, e.g. using context managers (:ref:`Read more » <middlewares>`)
- All contextual data now is shared between middlewares, filters and handlers to end-to-end use.
For example now you can easily pass some data into context inside middleware and
get it in the filters layer as the same way as in the handlers via keyword arguments.
- Added mechanism named **flags**, that helps to customize handler behavior
in conjunction with middlewares. (:ref:`Read more » <flags>`)
Keyboard Markup
===============
- Now :class:`aiogram.types.inline_keyboard_markup.InlineKeyboardMarkup`
and :class:`aiogram.types.reply_keyboard_markup.ReplyKeyboardMarkup` has no methods to extend it,
instead you have to use markup builders :class:`aiogram.utils.keyboard.ReplyKeyboardBuilder`
and :class:`aiogram.utils.keyboard.KeyboardBuilder` respectively
(:ref:`Read more » <keyboard-builder>`)
Callbacks data
==============
- Callback data factory now is strictly typed via `pydantic <https://docs.pydantic.dev/>`_ models
(:ref:`Read more » <callback-data-factory>`)
Finite State machine
====================
- State filter will no more added to all handlers, you will need to specify state if you want
- Added possibility to change FSM strategy, for example if you want to control state
for each user in chat topics instead of user in chat you can specify it in the Dispatcher.
Sending Files
=============
- From now you should wrap sending files into InputFile object before send instead of passing
IO object directly to the API method. (:ref:`Read more » <sending-files>`)
Webhook
=======
- Simplified aiohttp web app configuration
- By default added possibility to upload files when you use reply into webhook

View file

@ -1,4 +1,4 @@
.. _formatting-tool
.. _formatting-tool:
==========
Formatting

View file

@ -1,3 +1,4 @@
.. _keyboard-builder
================
Keyboard builder
================

View file

@ -82,7 +82,6 @@ docs = [
"sphinx-autobuild~=2021.3.14",
"sphinx-copybutton~=0.5.2",
"furo~=2023.7.26",
"sphinx-prompt~=1.7.0",
"Sphinx-Substitution-Extensions~=2022.2.16",
"towncrier~=23.6.0",
"pygments~=2.15.1",