Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
fix: correct client shutdown
  • Loading branch information
0zd0 committed Mar 16, 2026
commit e2df10f2c7f80953772b650bc05d3f4f4ab94bdb
6 changes: 3 additions & 3 deletions hydrogram/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ def __init__(

self.parser = Parser(self)

self.session = None
self.session: Session | None = None

self.media_sessions = {}
self.media_sessions_lock = asyncio.Lock()
Expand All @@ -318,8 +318,8 @@ def __init__(
self.save_file_semaphore = asyncio.Semaphore(self.max_concurrent_transmissions)
self.get_file_semaphore = asyncio.Semaphore(self.max_concurrent_transmissions)

self.is_connected = None
self.is_initialized = None
self.is_connected: bool | None = None
self.is_initialized: bool | None = None

self.takeout_id = None

Expand Down
14 changes: 4 additions & 10 deletions hydrogram/methods/auth/disconnect.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,11 @@ async def disconnect(
self: "hydrogram.Client",
):
"""Disconnect the client from Telegram servers.

Raises:
ConnectionError: In case you try to disconnect an already disconnected client or in case you try to
disconnect a client that needs to be terminated first.
"""
if not self.is_connected:
raise ConnectionError("Client is already disconnected")
if self.session:
await self.session.stop()

if self.is_initialized:
raise ConnectionError("Can't disconnect an initialized client")
if self.storage:
await self.storage.close()

await self.session.stop()
await self.storage.close()
self.is_connected = False
30 changes: 15 additions & 15 deletions hydrogram/methods/auth/terminate.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with Hydrogram. If not, see <http://www.gnu.org/licenses/>.

import asyncio
import contextlib
import logging

import hydrogram
Expand All @@ -33,29 +34,28 @@ async def terminate(

This method does the opposite of :meth:`~hydrogram.Client.initialize`.
It will stop the dispatcher and shut down updates and download workers.

Raises:
ConnectionError: In case you try to terminate a client that is already terminated.
"""
if not self.is_initialized:
raise ConnectionError("Client is already terminated")

if self.takeout_id:
await self.invoke(raw.functions.account.FinishTakeoutSession())
log.info("Takeout session %s finished", self.takeout_id)
with contextlib.suppress(Exception):
await self.invoke(raw.functions.account.FinishTakeoutSession())
log.info("Takeout session %s finished", self.takeout_id)

await self.storage.save()
await self.dispatcher.stop()
if self.storage:
await self.storage.save()

for media_session in self.media_sessions.values():
await media_session.stop()
if self.dispatcher:
await self.dispatcher.stop()

self.media_sessions.clear()
if self.media_sessions:
for media_session in self.media_sessions.values():
await media_session.stop()
self.media_sessions.clear()

self.updates_watchdog_event.set()

if self.updates_watchdog_task is not None:
await self.updates_watchdog_task
with contextlib.suppress(asyncio.TimeoutError, asyncio.CancelledError):
await asyncio.wait_for(self.updates_watchdog_task, timeout=1.0)

self.updates_watchdog_event.clear()

Expand Down
16 changes: 8 additions & 8 deletions hydrogram/session/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,14 @@ async def stop(self):

self.stored_msg_ids.clear()

if self.recv_task and not self.recv_task.done():
self.recv_task.cancel()

with contextlib.suppress(asyncio.CancelledError, asyncio.TimeoutError, RuntimeError):
await asyncio.wait_for(self.recv_task, timeout=1.0)

self.recv_task = None

self.ping_task_event.set()

if self.ping_task is not None:
Expand All @@ -186,14 +194,6 @@ async def stop(self):
if self.connection:
await self.connection.close()

if self.recv_task and not self.recv_task.done():
self.recv_task.cancel()

with contextlib.suppress(asyncio.CancelledError, asyncio.TimeoutError, RuntimeError):
await asyncio.wait_for(self.recv_task, timeout=1.0)

self.recv_task = None

if not self.is_media and callable(self.client.disconnect_handler):
try:
await self.client.disconnect_handler(self.client)
Expand Down