diff --git a/src/MySQLdb/connections.py b/src/MySQLdb/connections.py index a61aaaed..2e9687c5 100644 --- a/src/MySQLdb/connections.py +++ b/src/MySQLdb/connections.py @@ -4,6 +4,7 @@ want to make your own subclasses. In most cases, you will probably override Connection.default_cursor with a non-standard Cursor class. """ + import re from . import cursors, _mysql @@ -143,11 +144,11 @@ class object, used to create cursors (keyword only) :param bool local_infile: sets ``MYSQL_OPT_LOCAL_INFILE`` in ``mysql_options()`` enabling LOAD LOCAL INFILE from any path; zero disables; - + :param str local_infile_dir: - sets ``MYSQL_OPT_LOAD_DATA_LOCAL_DIR`` in ``mysql_options()`` enabling LOAD LOCAL INFILE from any path; + sets ``MYSQL_OPT_LOAD_DATA_LOCAL_DIR`` in ``mysql_options()`` enabling LOAD LOCAL INFILE from any path; if ``local_infile`` is set to ``True`` then this is ignored; - + supported for mysql version >= 8.0.21 :param bool autocommit: @@ -244,8 +245,16 @@ class object, used to create cursors (keyword only) self.autocommit(autocommit) self.messages = [] - def _set_attributes(self, host=None, user=None, password=None, database="", port=3306, - unix_socket=None, **kwargs): + def _set_attributes( + self, + host=None, + user=None, + password=None, + database="", + port=3306, + unix_socket=None, + **kwargs, + ): """set some attributes for otel""" if unix_socket and not host: host = "localhost" diff --git a/src/MySQLdb/converters.py b/src/MySQLdb/converters.py index d6fdc01c..27f92d0b 100644 --- a/src/MySQLdb/converters.py +++ b/src/MySQLdb/converters.py @@ -30,6 +30,7 @@ (with the copy() method), modify the copies, and then pass them to MySQL.connect(). """ + from decimal import Decimal from MySQLdb._mysql import string_literal diff --git a/src/MySQLdb/cursors.py b/src/MySQLdb/cursors.py index c5fdf0c7..03fc2386 100644 --- a/src/MySQLdb/cursors.py +++ b/src/MySQLdb/cursors.py @@ -3,6 +3,7 @@ This module implements Cursors of various types for MySQLdb. By default, MySQLdb uses the Cursor class. """ + import re from ._exceptions import ProgrammingError @@ -46,20 +47,6 @@ class BaseCursor: #: Default value of max_allowed_packet is 1048576. max_stmt_length = 64 * 1024 - from ._exceptions import ( - MySQLError, - Warning, - Error, - InterfaceError, - DatabaseError, - DataError, - OperationalError, - IntegrityError, - InternalError, - ProgrammingError, - NotSupportedError, - ) - connection = None def __init__(self, connection): @@ -344,16 +331,32 @@ def _fetch_row(self, size=1): def __iter__(self): return iter(self.fetchone, None) - Warning = Warning - Error = Error - InterfaceError = InterfaceError - DatabaseError = DatabaseError - DataError = DataError - OperationalError = OperationalError - IntegrityError = IntegrityError - InternalError = InternalError - ProgrammingError = ProgrammingError - NotSupportedError = NotSupportedError + def __getattr__(self, name): + # DB-API 2.0 optional extension says these errors can be accessed + # via Connection object. But MySQLdb had defined them on Cursor object. + import warnings + from . import _exceptions as err + + if name in ( + "Warning", + "Error", + "InterfaceError", + "DatabaseError", + "DataError", + "OperationalError", + "IntegrityError", + "InternalError", + "ProgrammingError", + "NotSupportedError", + ): + # Deprecated since v2.3. Will be removed in 2027. + warnings.warn( + "errors should be accessed from `MySQLdb` package", + DeprecationWarning, + stacklevel=2, + ) + return getattr(err, name) + raise AttributeError(name) class CursorStoreResultMixIn: @@ -423,7 +426,6 @@ def __iter__(self): class CursorUseResultMixIn: - """This is a MixIn class which causes the result set to be stored in the server and sent row-by-row to client side, i.e. it uses mysql_use_result(). You MUST retrieve the entire result set and diff --git a/src/MySQLdb/times.py b/src/MySQLdb/times.py index 915d827b..23833b6a 100644 --- a/src/MySQLdb/times.py +++ b/src/MySQLdb/times.py @@ -4,6 +4,7 @@ Use Python datetime module to handle date and time columns. """ + from time import localtime from datetime import date, datetime, time, timedelta from MySQLdb._mysql import string_literal diff --git a/tests/capabilities.py b/tests/capabilities.py index 1e695e9e..b1073a4d 100644 --- a/tests/capabilities.py +++ b/tests/capabilities.py @@ -1,10 +1,11 @@ #!/usr/bin/env python -O -""" Script to test database capabilities and the DB-API interface - for functionality and memory leaks. +"""Script to test database capabilities and the DB-API interface +for functionality and memory leaks. - Adapted from a script by M-A Lemburg. +Adapted from a script by M-A Lemburg. """ + from time import time import unittest from configdb import connection_factory diff --git a/tests/dbapi20.py b/tests/dbapi20.py index be0f6292..78dfebf9 100644 --- a/tests/dbapi20.py +++ b/tests/dbapi20.py @@ -1,14 +1,14 @@ #!/usr/bin/env python -""" Python DB API 2.0 driver compliance unit test suite. +"""Python DB API 2.0 driver compliance unit test suite. - This software is Public Domain and may be used without restrictions. + This software is Public Domain and may be used without restrictions. - "Now we have booze and barflies entering the discussion, plus rumours of - DBAs on drugs... and I won't tell you what flashes through my mind each - time I read the subject line with 'Anal Compliance' in it. All around - this is turning out to be a thoroughly unwholesome unit test." +"Now we have booze and barflies entering the discussion, plus rumours of + DBAs on drugs... and I won't tell you what flashes through my mind each + time I read the subject line with 'Anal Compliance' in it. All around + this is turning out to be a thoroughly unwholesome unit test." - -- Ian Bicking + -- Ian Bicking """ __rcs_id__ = "$Id$" @@ -300,7 +300,7 @@ def test_rowcount(self): self.assertEqual( cur.rowcount, -1, - "cursor.rowcount should be -1 after executing no-result " "statements", + "cursor.rowcount should be -1 after executing no-result statements", ) cur.execute( "insert into %sbooze values ('Victoria Bitter')" % (self.table_prefix) @@ -410,12 +410,12 @@ def _paraminsert(self, cur): self.assertEqual( beers[0], "Cooper's", - "cursor.fetchall retrieved incorrect data, or data inserted " "incorrectly", + "cursor.fetchall retrieved incorrect data, or data inserted incorrectly", ) self.assertEqual( beers[1], "Victoria Bitter", - "cursor.fetchall retrieved incorrect data, or data inserted " "incorrectly", + "cursor.fetchall retrieved incorrect data, or data inserted incorrectly", ) def test_executemany(self): @@ -483,7 +483,7 @@ def test_fetchone(self): self.assertEqual( cur.fetchone(), None, - "cursor.fetchone should return None if a query retrieves " "no rows", + "cursor.fetchone should return None if a query retrieves no rows", ) self.assertTrue(cur.rowcount in (-1, 0)) diff --git a/tests/test_MySQLdb_dbapi20.py b/tests/test_MySQLdb_dbapi20.py index a0dd92a1..29c4c0f4 100644 --- a/tests/test_MySQLdb_dbapi20.py +++ b/tests/test_MySQLdb_dbapi20.py @@ -98,7 +98,7 @@ def test_fetchone(self): self.assertEqual( cur.fetchone(), None, - "cursor.fetchone should return None if a query retrieves " "no rows", + "cursor.fetchone should return None if a query retrieves no rows", ) self.assertTrue(cur.rowcount in (-1, 0)) @@ -172,9 +172,7 @@ def help_nextset_setUp(self, cur): select count(*) from %(tp)sbooze; select name from %(tp)sbooze; end - """ % dict( - tp=self.table_prefix - ) + """ % dict(tp=self.table_prefix) cur.execute(sql) def help_nextset_tearDown(self, cur): diff --git a/tests/test_cursor.py b/tests/test_cursor.py index 65cb236d..abb84cfb 100644 --- a/tests/test_cursor.py +++ b/tests/test_cursor.py @@ -57,9 +57,9 @@ def test_executemany(): "INSERT INTO TEST (ID, NAME) VALUES (%(id_name)s, %(name)s) ON duplicate update" ) assert m is not None, "error parse %(id_name)s" - assert ( - m.group(3) == " ON duplicate update" - ), "group 3 not ON duplicate update, bug in RE_INSERT_VALUES?" + assert m.group(3) == " ON duplicate update", ( + "group 3 not ON duplicate update, bug in RE_INSERT_VALUES?" + ) # https://github.com/PyMySQL/mysqlclient-python/issues/178 m = MySQLdb.cursors.RE_INSERT_VALUES.match( @@ -75,16 +75,16 @@ def test_executemany(): # list args data = [(i,) for i in range(10)] cursor.executemany("insert into test (data) values (%s)", data) - assert cursor._executed.endswith( - b",(7),(8),(9)" - ), "execute many with %s not in one query" + assert cursor._executed.endswith(b",(7),(8),(9)"), ( + "execute many with %s not in one query" + ) # dict args data_dict = [{"data": i} for i in range(10)] cursor.executemany("insert into test (data) values (%(data)s)", data_dict) - assert cursor._executed.endswith( - b",(7),(8),(9)" - ), "execute many with %(data)s not in one query" + assert cursor._executed.endswith(b",(7),(8),(9)"), ( + "execute many with %(data)s not in one query" + ) # %% in column set cursor.execute( @@ -97,9 +97,9 @@ def test_executemany(): q = "INSERT INTO percent_test (`A%%`, `B%%`) VALUES (%s, %s)" assert MySQLdb.cursors.RE_INSERT_VALUES.match(q) is not None cursor.executemany(q, [(3, 4), (5, 6)]) - assert cursor._executed.endswith( - b"(3, 4),(5, 6)" - ), "executemany with %% not in one query" + assert cursor._executed.endswith(b"(3, 4),(5, 6)"), ( + "executemany with %% not in one query" + ) finally: cursor.execute("DROP TABLE IF EXISTS percent_test")