Skip to content

Commit c9515b3

Browse files
author
Aliaksandr Akulchyk
committed
Implement 'read_timeout' parameter
1 parent 83aa96e commit c9515b3

File tree

3 files changed

+39
-6
lines changed

3 files changed

+39
-6
lines changed

aiomysql/connection.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ def connect(host="localhost", user=None, password="",
5151
read_default_file=None, conv=decoders, use_unicode=None,
5252
client_flag=0, cursorclass=Cursor, init_command=None,
5353
connect_timeout=None, read_default_group=None,
54+
read_timeout=None,
5455
autocommit=False, echo=False,
5556
local_infile=False, loop=None, ssl=None, auth_plugin='',
5657
program_name='', server_public_key=None):
@@ -64,6 +65,7 @@ def connect(host="localhost", user=None, password="",
6465
init_command=init_command,
6566
connect_timeout=connect_timeout,
6667
read_default_group=read_default_group,
68+
read_timeout=read_timeout,
6769
autocommit=autocommit, echo=echo,
6870
local_infile=local_infile, loop=loop, ssl=ssl,
6971
auth_plugin=auth_plugin, program_name=program_name)
@@ -139,7 +141,7 @@ def __init__(self, host="localhost", user=None, password="",
139141
charset='', sql_mode=None,
140142
read_default_file=None, conv=decoders, use_unicode=None,
141143
client_flag=0, cursorclass=Cursor, init_command=None,
142-
connect_timeout=None, read_default_group=None,
144+
connect_timeout=None, read_default_group=None, read_timeout=None,
143145
autocommit=False, echo=False,
144146
local_infile=False, loop=None, ssl=None, auth_plugin='',
145147
program_name='', server_public_key=None):
@@ -171,6 +173,8 @@ def __init__(self, host="localhost", user=None, password="",
171173
when connecting.
172174
:param read_default_group: Group to read from in the configuration
173175
file.
176+
:param read_timeout: The timeout for reading from the connection in seconds
177+
(default: None - no timeout)
174178
:param autocommit: Autocommit mode. None means use server default.
175179
(default: False)
176180
:param local_infile: boolean to enable the use of LOAD DATA LOCAL
@@ -257,6 +261,7 @@ def __init__(self, host="localhost", user=None, password="",
257261

258262
self.cursorclass = cursorclass
259263
self.connect_timeout = connect_timeout
264+
self.read_timeout = read_timeout
260265

261266
self._result = None
262267
self._affected_rows = 0
@@ -654,12 +659,25 @@ async def _read_packet(self, packet_type=MysqlPacket):
654659

655660
async def _read_bytes(self, num_bytes):
656661
try:
657-
data = await self._reader.readexactly(num_bytes)
662+
if self.read_timeout:
663+
try:
664+
data = await asyncio.wait_for(
665+
self._reader.readexactly(num_bytes),
666+
self.read_timeout
667+
)
668+
except asyncio.TimeoutError as e:
669+
raise asyncio.TimeoutError("Read timeout exceeded") from e
670+
else:
671+
data = await self._reader.readexactly(num_bytes)
658672
except asyncio.IncompleteReadError as e:
659673
msg = "Lost connection to MySQL server during query"
660674
self.close()
661675
raise OperationalError(CR.CR_SERVER_LOST, msg) from e
662-
except OSError as e:
676+
except (OSError, asyncio.TimeoutError) as e:
677+
msg = f"Lost connection to MySQL server during query ({e})"
678+
self.close()
679+
raise OperationalError(CR.CR_SERVER_LOST, msg) from e
680+
except Exception as e:
663681
msg = f"Lost connection to MySQL server during query ({e})"
664682
self.close()
665683
raise OperationalError(CR.CR_SERVER_LOST, msg) from e

tests/sa/test_sa_connection.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
from unittest import mock
22

3+
import aiomysql
4+
from aiomysql import sa, Cursor
5+
36
import pytest
47
from sqlalchemy import MetaData, Table, Column, Integer, String, func, select
58
from sqlalchemy.schema import DropTable, CreateTable
69
from sqlalchemy.sql.expression import bindparam
710

8-
import aiomysql
9-
from aiomysql import sa, Cursor
10-
1111
meta = MetaData()
1212
tbl = Table('sa_tbl', meta,
1313
Column('id', Integer, nullable=False,
@@ -35,6 +35,13 @@ async def connect(**kwargs):
3535
return connect
3636

3737

38+
@pytest.mark.run_loop
39+
async def test_read_timeout(sa_connect):
40+
conn = await sa_connect(read_timeout=0.01)
41+
with pytest.raises(aiomysql.OperationalError):
42+
await conn.execute("DO SLEEP(1)")
43+
44+
3845
@pytest.mark.run_loop
3946
async def test_execute_text_select(sa_connect):
4047
conn = await sa_connect()

tests/test_connection.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ async def test_connect_timeout(connection_creator):
3232
await connection_creator(connect_timeout=0.000000000001)
3333

3434

35+
@pytest.mark.run_loop
36+
async def test_read_timeout(connection_creator):
37+
with pytest.raises(aiomysql.OperationalError):
38+
con = await connection_creator(read_timeout=0.01)
39+
cur = await con.cursor()
40+
await cur.execute("DO SLEEP(1)")
41+
42+
3543
@pytest.mark.run_loop
3644
async def test_config_file(fill_my_cnf, connection_creator, mysql_params):
3745
tests_root = os.path.abspath(os.path.dirname(__file__))

0 commit comments

Comments
 (0)