Skip to content

Commit 63e67ad

Browse files
authored
deprecate exception classes in Cursor attributes (#782)
1 parent 29a574b commit 63e67ad

8 files changed

Lines changed: 72 additions & 60 deletions

File tree

src/MySQLdb/connections.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
want to make your own subclasses. In most cases, you will probably
55
override Connection.default_cursor with a non-standard Cursor class.
66
"""
7+
78
import re
89

910
from . import cursors, _mysql
@@ -143,11 +144,11 @@ class object, used to create cursors (keyword only)
143144
144145
:param bool local_infile:
145146
sets ``MYSQL_OPT_LOCAL_INFILE`` in ``mysql_options()`` enabling LOAD LOCAL INFILE from any path; zero disables;
146-
147+
147148
:param str local_infile_dir:
148-
sets ``MYSQL_OPT_LOAD_DATA_LOCAL_DIR`` in ``mysql_options()`` enabling LOAD LOCAL INFILE from any path;
149+
sets ``MYSQL_OPT_LOAD_DATA_LOCAL_DIR`` in ``mysql_options()`` enabling LOAD LOCAL INFILE from any path;
149150
if ``local_infile`` is set to ``True`` then this is ignored;
150-
151+
151152
supported for mysql version >= 8.0.21
152153
153154
:param bool autocommit:
@@ -244,8 +245,16 @@ class object, used to create cursors (keyword only)
244245
self.autocommit(autocommit)
245246
self.messages = []
246247

247-
def _set_attributes(self, host=None, user=None, password=None, database="", port=3306,
248-
unix_socket=None, **kwargs):
248+
def _set_attributes(
249+
self,
250+
host=None,
251+
user=None,
252+
password=None,
253+
database="",
254+
port=3306,
255+
unix_socket=None,
256+
**kwargs,
257+
):
249258
"""set some attributes for otel"""
250259
if unix_socket and not host:
251260
host = "localhost"

src/MySQLdb/converters.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
(with the copy() method), modify the copies, and then pass them to
3131
MySQL.connect().
3232
"""
33+
3334
from decimal import Decimal
3435

3536
from MySQLdb._mysql import string_literal

src/MySQLdb/cursors.py

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
This module implements Cursors of various types for MySQLdb. By
44
default, MySQLdb uses the Cursor class.
55
"""
6+
67
import re
78

89
from ._exceptions import ProgrammingError
@@ -46,20 +47,6 @@ class BaseCursor:
4647
#: Default value of max_allowed_packet is 1048576.
4748
max_stmt_length = 64 * 1024
4849

49-
from ._exceptions import (
50-
MySQLError,
51-
Warning,
52-
Error,
53-
InterfaceError,
54-
DatabaseError,
55-
DataError,
56-
OperationalError,
57-
IntegrityError,
58-
InternalError,
59-
ProgrammingError,
60-
NotSupportedError,
61-
)
62-
6350
connection = None
6451

6552
def __init__(self, connection):
@@ -344,16 +331,32 @@ def _fetch_row(self, size=1):
344331
def __iter__(self):
345332
return iter(self.fetchone, None)
346333

347-
Warning = Warning
348-
Error = Error
349-
InterfaceError = InterfaceError
350-
DatabaseError = DatabaseError
351-
DataError = DataError
352-
OperationalError = OperationalError
353-
IntegrityError = IntegrityError
354-
InternalError = InternalError
355-
ProgrammingError = ProgrammingError
356-
NotSupportedError = NotSupportedError
334+
def __getattr__(self, name):
335+
# DB-API 2.0 optional extension says these errors can be accessed
336+
# via Connection object. But MySQLdb had defined them on Cursor object.
337+
import warnings
338+
from . import _exceptions as err
339+
340+
if name in (
341+
"Warning",
342+
"Error",
343+
"InterfaceError",
344+
"DatabaseError",
345+
"DataError",
346+
"OperationalError",
347+
"IntegrityError",
348+
"InternalError",
349+
"ProgrammingError",
350+
"NotSupportedError",
351+
):
352+
# Deprecated since v2.3. Will be removed in 2027.
353+
warnings.warn(
354+
"errors should be accessed from `MySQLdb` package",
355+
DeprecationWarning,
356+
stacklevel=2,
357+
)
358+
return getattr(err, name)
359+
raise AttributeError(name)
357360

358361

359362
class CursorStoreResultMixIn:
@@ -423,7 +426,6 @@ def __iter__(self):
423426

424427

425428
class CursorUseResultMixIn:
426-
427429
"""This is a MixIn class which causes the result set to be stored
428430
in the server and sent row-by-row to client side, i.e. it uses
429431
mysql_use_result(). You MUST retrieve the entire result set and

src/MySQLdb/times.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
55
Use Python datetime module to handle date and time columns.
66
"""
7+
78
from time import localtime
89
from datetime import date, datetime, time, timedelta
910
from MySQLdb._mysql import string_literal

tests/capabilities.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
#!/usr/bin/env python -O
2-
""" Script to test database capabilities and the DB-API interface
3-
for functionality and memory leaks.
2+
"""Script to test database capabilities and the DB-API interface
3+
for functionality and memory leaks.
44
5-
Adapted from a script by M-A Lemburg.
5+
Adapted from a script by M-A Lemburg.
66
77
"""
8+
89
from time import time
910
import unittest
1011
from configdb import connection_factory

tests/dbapi20.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
#!/usr/bin/env python
2-
""" Python DB API 2.0 driver compliance unit test suite.
2+
"""Python DB API 2.0 driver compliance unit test suite.
33
4-
This software is Public Domain and may be used without restrictions.
4+
This software is Public Domain and may be used without restrictions.
55
6-
"Now we have booze and barflies entering the discussion, plus rumours of
7-
DBAs on drugs... and I won't tell you what flashes through my mind each
8-
time I read the subject line with 'Anal Compliance' in it. All around
9-
this is turning out to be a thoroughly unwholesome unit test."
6+
"Now we have booze and barflies entering the discussion, plus rumours of
7+
DBAs on drugs... and I won't tell you what flashes through my mind each
8+
time I read the subject line with 'Anal Compliance' in it. All around
9+
this is turning out to be a thoroughly unwholesome unit test."
1010
11-
-- Ian Bicking
11+
-- Ian Bicking
1212
"""
1313

1414
__rcs_id__ = "$Id$"
@@ -300,7 +300,7 @@ def test_rowcount(self):
300300
self.assertEqual(
301301
cur.rowcount,
302302
-1,
303-
"cursor.rowcount should be -1 after executing no-result " "statements",
303+
"cursor.rowcount should be -1 after executing no-result statements",
304304
)
305305
cur.execute(
306306
"insert into %sbooze values ('Victoria Bitter')" % (self.table_prefix)
@@ -410,12 +410,12 @@ def _paraminsert(self, cur):
410410
self.assertEqual(
411411
beers[0],
412412
"Cooper's",
413-
"cursor.fetchall retrieved incorrect data, or data inserted " "incorrectly",
413+
"cursor.fetchall retrieved incorrect data, or data inserted incorrectly",
414414
)
415415
self.assertEqual(
416416
beers[1],
417417
"Victoria Bitter",
418-
"cursor.fetchall retrieved incorrect data, or data inserted " "incorrectly",
418+
"cursor.fetchall retrieved incorrect data, or data inserted incorrectly",
419419
)
420420

421421
def test_executemany(self):
@@ -483,7 +483,7 @@ def test_fetchone(self):
483483
self.assertEqual(
484484
cur.fetchone(),
485485
None,
486-
"cursor.fetchone should return None if a query retrieves " "no rows",
486+
"cursor.fetchone should return None if a query retrieves no rows",
487487
)
488488
self.assertTrue(cur.rowcount in (-1, 0))
489489

tests/test_MySQLdb_dbapi20.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ def test_fetchone(self):
9898
self.assertEqual(
9999
cur.fetchone(),
100100
None,
101-
"cursor.fetchone should return None if a query retrieves " "no rows",
101+
"cursor.fetchone should return None if a query retrieves no rows",
102102
)
103103
self.assertTrue(cur.rowcount in (-1, 0))
104104

@@ -172,9 +172,7 @@ def help_nextset_setUp(self, cur):
172172
select count(*) from %(tp)sbooze;
173173
select name from %(tp)sbooze;
174174
end
175-
""" % dict(
176-
tp=self.table_prefix
177-
)
175+
""" % dict(tp=self.table_prefix)
178176
cur.execute(sql)
179177

180178
def help_nextset_tearDown(self, cur):

tests/test_cursor.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,9 @@ def test_executemany():
5757
"INSERT INTO TEST (ID, NAME) VALUES (%(id_name)s, %(name)s) ON duplicate update"
5858
)
5959
assert m is not None, "error parse %(id_name)s"
60-
assert (
61-
m.group(3) == " ON duplicate update"
62-
), "group 3 not ON duplicate update, bug in RE_INSERT_VALUES?"
60+
assert m.group(3) == " ON duplicate update", (
61+
"group 3 not ON duplicate update, bug in RE_INSERT_VALUES?"
62+
)
6363

6464
# https://github.com/PyMySQL/mysqlclient-python/issues/178
6565
m = MySQLdb.cursors.RE_INSERT_VALUES.match(
@@ -75,16 +75,16 @@ def test_executemany():
7575
# list args
7676
data = [(i,) for i in range(10)]
7777
cursor.executemany("insert into test (data) values (%s)", data)
78-
assert cursor._executed.endswith(
79-
b",(7),(8),(9)"
80-
), "execute many with %s not in one query"
78+
assert cursor._executed.endswith(b",(7),(8),(9)"), (
79+
"execute many with %s not in one query"
80+
)
8181

8282
# dict args
8383
data_dict = [{"data": i} for i in range(10)]
8484
cursor.executemany("insert into test (data) values (%(data)s)", data_dict)
85-
assert cursor._executed.endswith(
86-
b",(7),(8),(9)"
87-
), "execute many with %(data)s not in one query"
85+
assert cursor._executed.endswith(b",(7),(8),(9)"), (
86+
"execute many with %(data)s not in one query"
87+
)
8888

8989
# %% in column set
9090
cursor.execute(
@@ -97,9 +97,9 @@ def test_executemany():
9797
q = "INSERT INTO percent_test (`A%%`, `B%%`) VALUES (%s, %s)"
9898
assert MySQLdb.cursors.RE_INSERT_VALUES.match(q) is not None
9999
cursor.executemany(q, [(3, 4), (5, 6)])
100-
assert cursor._executed.endswith(
101-
b"(3, 4),(5, 6)"
102-
), "executemany with %% not in one query"
100+
assert cursor._executed.endswith(b"(3, 4),(5, 6)"), (
101+
"executemany with %% not in one query"
102+
)
103103
finally:
104104
cursor.execute("DROP TABLE IF EXISTS percent_test")
105105

0 commit comments

Comments
 (0)