Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-PyMySQL for openSUSE:Factory checked in at 2025-09-30 17:41:28 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-PyMySQL (Old) and /work/SRC/openSUSE:Factory/.python-PyMySQL.new.11973 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-PyMySQL" Tue Sep 30 17:41:28 2025 rev:24 rq:1308066 version:1.1.2 Changes: -------- --- /work/SRC/openSUSE:Factory/python-PyMySQL/python-PyMySQL.changes 2025-04-29 16:42:47.812279924 +0200 +++ /work/SRC/openSUSE:Factory/.python-PyMySQL.new.11973/python-PyMySQL.changes 2025-09-30 17:43:23.387788270 +0200 @@ -1,0 +2,16 @@ +Mon Sep 29 21:06:17 UTC 2025 - Dirk Müller <[email protected]> + +- update to 1.1.2: + * Prevent UnboundLocalError in very rare situation. + * Close underlying `SocketIO` soon when Connection is closed + for PyPy. + * Fix importing PyMySQL fail on CPython 3.13 when + `getpass.getuser()` raises OSEError. + * Make charset name "utf8" case insensitive. + * `Connection.kill()` uses `KILL` query instead of + `COM_KILL`command to support MySQL 8.4. + * Fix SSL error on CPython 3.13 caused by strict TLS default + setting. https://github.com/PyMySQL/PyMySQL/pull/1198 + * Fix auth switch request handling. + +------------------------------------------------------------------- Old: ---- PyMySQL-1.1.1.tar.gz New: ---- PyMySQL-1.1.2.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-PyMySQL.spec ++++++ --- /var/tmp/diff_new_pack.AWsu6s/_old 2025-09-30 17:43:24.407831855 +0200 +++ /var/tmp/diff_new_pack.AWsu6s/_new 2025-09-30 17:43:24.407831855 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-PyMySQL # -# Copyright (c) 2025 SUSE LLC +# Copyright (c) 2025 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -25,7 +25,7 @@ %{?sle15_python_module_pythons} Name: python-PyMySQL -Version: 1.1.1 +Version: 1.1.2 Release: 0 Summary: Pure Python MySQL Driver License: MIT ++++++ PyMySQL-1.1.1.tar.gz -> PyMySQL-1.1.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyMySQL-1.1.1/.coveragerc new/PyMySQL-1.1.2/.coveragerc --- old/PyMySQL-1.1.1/.coveragerc 2024-05-21 13:02:25.000000000 +0200 +++ new/PyMySQL-1.1.2/.coveragerc 2025-08-24 14:53:42.000000000 +0200 @@ -2,6 +2,7 @@ branch = True source = pymysql + tests omit = pymysql/tests/* pymysql/tests/thirdparty/test_MySQLdb/* diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyMySQL-1.1.1/.github/workflows/codeql-analysis.yml new/PyMySQL-1.1.2/.github/workflows/codeql-analysis.yml --- old/PyMySQL-1.1.1/.github/workflows/codeql-analysis.yml 2024-05-21 13:02:25.000000000 +0200 +++ new/PyMySQL-1.1.2/.github/workflows/codeql-analysis.yml 1970-01-01 01:00:00.000000000 +0100 @@ -1,62 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: "CodeQL" - -on: - push: - branches: [ main ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ main ] - schedule: - - cron: '34 7 * * 2' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - - strategy: - fail-fast: false - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: "python" - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v3 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyMySQL-1.1.1/.github/workflows/codesee-arch-diagram.yml new/PyMySQL-1.1.2/.github/workflows/codesee-arch-diagram.yml --- old/PyMySQL-1.1.1/.github/workflows/codesee-arch-diagram.yml 2024-05-21 13:02:25.000000000 +0200 +++ new/PyMySQL-1.1.2/.github/workflows/codesee-arch-diagram.yml 1970-01-01 01:00:00.000000000 +0100 @@ -1,23 +0,0 @@ -# This workflow was added by CodeSee. Learn more at https://codesee.io/ -# This is v2.0 of this workflow file -on: - push: - branches: - - main - pull_request_target: - types: [opened, synchronize, reopened] - -name: CodeSee - -permissions: read-all - -jobs: - codesee: - runs-on: ubuntu-latest - continue-on-error: true - name: Analyze the repo with CodeSee - steps: - - uses: Codesee-io/codesee-action@v2 - with: - codesee-token: ${{ secrets.CODESEE_ARCH_DIAG_API_TOKEN }} - codesee-url: https://app.codesee.io diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyMySQL-1.1.1/.github/workflows/lint.yaml new/PyMySQL-1.1.2/.github/workflows/lint.yaml --- old/PyMySQL-1.1.1/.github/workflows/lint.yaml 2024-05-21 13:02:25.000000000 +0200 +++ new/PyMySQL-1.1.2/.github/workflows/lint.yaml 2025-08-24 14:53:42.000000000 +0200 @@ -13,13 +13,12 @@ lint: runs-on: ubuntu-latest steps: - - name: checkout - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - name: lint - uses: chartboost/ruff-action@v1 + - uses: astral-sh/ruff-action@v3 + + - name: format + run: ruff format --diff - - name: check format - uses: chartboost/ruff-action@v1 - with: - args: "format --diff" + - name: lint + run: ruff check --diff diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyMySQL-1.1.1/.github/workflows/lock.yml new/PyMySQL-1.1.2/.github/workflows/lock.yml --- old/PyMySQL-1.1.1/.github/workflows/lock.yml 2024-05-21 13:02:25.000000000 +0200 +++ new/PyMySQL-1.1.2/.github/workflows/lock.yml 1970-01-01 01:00:00.000000000 +0100 @@ -1,17 +0,0 @@ -name: 'Lock Threads' - -on: - schedule: - - cron: '30 9 * * 1' - -permissions: - issues: write - pull-requests: write - -jobs: - lock-threads: - if: github.repository == 'PyMySQL/PyMySQL' - runs-on: ubuntu-latest - steps: - - uses: dessant/lock-threads@v5 - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyMySQL-1.1.1/.github/workflows/test.yaml new/PyMySQL-1.1.2/.github/workflows/test.yaml --- old/PyMySQL-1.1.1/.github/workflows/test.yaml 2024-05-21 13:02:25.000000000 +0200 +++ new/PyMySQL-1.1.2/.github/workflows/test.yaml 2025-08-24 14:53:42.000000000 +0200 @@ -18,30 +18,37 @@ fail-fast: false matrix: include: - - db: "mariadb:10.4" - py: "3.8" + - db: "mariadb:12" # rolling release + py: "3.13" + # pynacl 1.5 doesn't support Python 3.14 yet. + # https://github.com/pyca/pynacl/commit/d33028e43b814615a33e231925eaddb0f679fa2b - - db: "mariadb:10.5" - py: "3.7" + - db: "mariadb:11.8" + py: "3.12" - - db: "mariadb:10.6" + - db: "mariadb:11.4" py: "3.11" - - db: "mariadb:10.6" - py: "3.12" + - db: "mariadb:10.11" + py: "3.10" - - db: "mariadb:lts" + - db: "mariadb:10.6" py: "3.9" - - db: "mysql:5.7" - py: "pypy-3.8" - - db: "mysql:8.0" + py: "pypy3.11" + mysql_auth: true + + - db: "mysql:9" + py: "3.14" + mysql_auth: true + + - db: "mysql:8.4" py: "3.9" mysql_auth: true - - db: "mysql:8.0" - py: "3.10" + - db: "mysql:5.7" + py: "3.9" services: mysql: @@ -56,7 +63,7 @@ - /run/mysqld:/run/mysqld steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Workaround MySQL container permissions if: startsWith(matrix.db, 'mysql') @@ -106,4 +113,4 @@ - name: Upload coverage reports to Codecov if: github.repository == 'PyMySQL/PyMySQL' - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyMySQL-1.1.1/CHANGELOG.md new/PyMySQL-1.1.2/CHANGELOG.md --- old/PyMySQL-1.1.1/CHANGELOG.md 2024-05-21 13:02:25.000000000 +0200 +++ new/PyMySQL-1.1.2/CHANGELOG.md 2025-08-24 14:53:42.000000000 +0200 @@ -7,6 +7,19 @@ * `db` and `passwd` will emit DeprecationWarning in v1.2. See #933. * `Connection.ping(reconnect)` change the default to not reconnect. +## v1.1.2 + +Release date: 2025-08-24 + +* Prevent UnboundLocalError in very rare situation. https://github.com/PyMySQL/PyMySQL/pull/1174 +* Close underlying `SocketIO` soon when Connection is closed for PyPy. https://github.com/PyMySQL/PyMySQL/issues/1183 +* Fix importing PyMySQL fail on CPython 3.13 when `getpass.getuser()` raises OSEError. https://github.com/PyMySQL/PyMySQL/pull/1190 +* Make charset name "utf8" case insensitive. https://github.com/PyMySQL/PyMySQL/pull/1195 +* `Connection.kill()` uses `KILL` query instead of `COM_KILL`command to support MySQL 8.4. https://github.com/PyMySQL/PyMySQL/pull/1197 +* Fix SSL error on CPython 3.13 caused by strict TLS default setting. https://github.com/PyMySQL/PyMySQL/pull/1198 +* Fix auth switch request handling. https://github.com/PyMySQL/PyMySQL/pull/1200 + + ## v1.1.1 Release date: 2024-05-21 @@ -21,6 +34,7 @@ * Prohibit dict parameter for `Cursor.execute()`. It didn't produce valid SQL and might cause SQL injection. (CVE-2024-36039) +* Added ssl_key_password param. #1145 ## v1.1.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyMySQL-1.1.1/README.md new/PyMySQL-1.1.2/README.md --- old/PyMySQL-1.1.1/README.md 2024-05-21 13:02:25.000000000 +0200 +++ new/PyMySQL-1.1.2/README.md 2025-08-24 14:53:42.000000000 +0200 @@ -1,19 +1,20 @@ [](https://pymysql.readthedocs.io/) [](https://codecov.io/gh/PyMySQL/PyMySQL) +[](https://deepwiki.com/PyMySQL/PyMySQL) # PyMySQL -This package contains a pure-Python MySQL client library, based on [PEP -249](https://www.python.org/dev/peps/pep-0249/). +This package contains a pure-Python MySQL and MariaDB client library, based on +[PEP 249](https://www.python.org/dev/peps/pep-0249/). ## Requirements - Python -- one of the following: - - [CPython](https://www.python.org/) : 3.7 and newer + - [CPython](https://www.python.org/) : 3.9 and newer - [PyPy](https://pypy.org/) : Latest 3.x version - MySQL Server -- one of the following: - - [MySQL](https://www.mysql.com/) \>= 5.7 - - [MariaDB](https://mariadb.org/) \>= 10.4 + - [MySQL](https://www.mysql.com/) LTS versions + - [MariaDB](https://mariadb.org/) LTS versions ## Installation @@ -92,6 +93,7 @@ - DB-API 2.0: <https://www.python.org/dev/peps/pep-0249/> - MySQL Reference Manuals: <https://dev.mysql.com/doc/> +- Getting Help With MariaDB <https://mariadb.com/kb/en/getting-help-with-mariadb/> - MySQL client/server protocol: <https://dev.mysql.com/doc/internals/en/client-server-protocol.html> - "Connector" channel in MySQL Community Slack: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyMySQL-1.1.1/docs/requirements.txt new/PyMySQL-1.1.2/docs/requirements.txt --- old/PyMySQL-1.1.1/docs/requirements.txt 2024-05-21 13:02:25.000000000 +0200 +++ new/PyMySQL-1.1.2/docs/requirements.txt 2025-08-24 14:53:42.000000000 +0200 @@ -1,2 +1,2 @@ -sphinx~=7.2 -sphinx-rtd-theme~=2.0.0 +sphinx~=8.0 +sphinx-rtd-theme~=3.0.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyMySQL-1.1.1/docs/source/user/development.rst new/PyMySQL-1.1.2/docs/source/user/development.rst --- old/PyMySQL-1.1.1/docs/source/user/development.rst 2024-05-21 13:02:25.000000000 +0200 +++ new/PyMySQL-1.1.2/docs/source/user/development.rst 2025-08-24 14:53:42.000000000 +0200 @@ -28,7 +28,7 @@ $ cp ci/database.json pymysql/tests/databases.json $ $EDITOR pymysql/tests/databases.json -To run all the tests, execute the script ``runtests.py``:: +To run all the tests, you can use pytest:: - $ pip install pytest + $ pip install -r requirements-dev.txt $ pytest -v pymysql diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyMySQL-1.1.1/pymysql/__init__.py new/PyMySQL-1.1.2/pymysql/__init__.py --- old/PyMySQL-1.1.1/pymysql/__init__.py 2024-05-21 13:02:25.000000000 +0200 +++ new/PyMySQL-1.1.2/pymysql/__init__.py 2025-08-24 14:53:42.000000000 +0200 @@ -49,8 +49,8 @@ # PyMySQL version. # Used by setuptools and connection_attrs -VERSION = (1, 1, 1, "final", 1) -VERSION_STRING = "1.1.1" +VERSION = (1, 1, 2, "final") +VERSION_STRING = "1.1.2" ### for mysqlclient compatibility ### Django checks mysqlclient version. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyMySQL-1.1.1/pymysql/_auth.py new/PyMySQL-1.1.2/pymysql/_auth.py --- old/PyMySQL-1.1.1/pymysql/_auth.py 2024-05-21 13:02:25.000000000 +0200 +++ new/PyMySQL-1.1.2/pymysql/_auth.py 2025-08-24 14:53:42.000000000 +0200 @@ -166,6 +166,8 @@ if pkt.is_auth_switch_request(): conn.salt = pkt.read_all() + if conn.salt.endswith(b"\0"): + conn.salt = conn.salt[:-1] if not conn.server_public_key and conn.password: # Request server public key if DEBUG: @@ -215,9 +217,11 @@ if pkt.is_auth_switch_request(): # Try from fast auth - if DEBUG: - print("caching sha2: Trying fast path") conn.salt = pkt.read_all() + if conn.salt.endswith(b"\0"): # str.removesuffix is available in 3.9 + conn.salt = conn.salt[:-1] + if DEBUG: + print(f"caching sha2: Trying fast path. salt={conn.salt.hex()!r}") scrambled = scramble_caching_sha2(conn.password, conn.salt) pkt = _roundtrip(conn, scrambled) # else: fast auth is tried in initial handshake diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyMySQL-1.1.1/pymysql/charset.py new/PyMySQL-1.1.2/pymysql/charset.py --- old/PyMySQL-1.1.1/pymysql/charset.py 2024-05-21 13:02:25.000000000 +0200 +++ new/PyMySQL-1.1.2/pymysql/charset.py 2025-08-24 14:53:42.000000000 +0200 @@ -45,9 +45,10 @@ return self._by_id[id] def by_name(self, name): + name = name.lower() if name == "utf8": name = "utf8mb4" - return self._by_name.get(name.lower()) + return self._by_name.get(name) _charsets = Charsets() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyMySQL-1.1.1/pymysql/connections.py new/PyMySQL-1.1.2/pymysql/connections.py --- old/PyMySQL-1.1.1/pymysql/connections.py 2024-05-21 13:02:25.000000000 +0200 +++ new/PyMySQL-1.1.2/pymysql/connections.py 2025-08-24 14:53:42.000000000 +0200 @@ -40,11 +40,14 @@ DEFAULT_USER = getpass.getuser() del getpass -except (ImportError, KeyError): - # KeyError occurs when there's no entry in OS database for a current user. +except (ImportError, KeyError, OSError): + # When there's no entry in OS database for a current user: + # KeyError is raised in Python 3.12 and below. + # OSError is raised in Python 3.13+ DEFAULT_USER = None DEBUG = False +_DEFAULT_AUTH_PLUGIN = None # if this is not None, use it instead of server's default. TEXT_TYPES = { FIELD_TYPE.BIT, @@ -161,6 +164,7 @@ """ _sock = None + _rfile = None _auth_plugin_name = "" _closed = False _secure = False @@ -374,6 +378,12 @@ capath = sslp.get("capath") hasnoca = ca is None and capath is None ctx = ssl.create_default_context(cafile=ca, capath=capath) + + # Python 3.13 enables VERIFY_X509_STRICT by default. + # But self signed certificates that are generated by MySQL automatically + # doesn't pass the verification. + ctx.verify_flags &= ~ssl.VERIFY_X509_STRICT + ctx.check_hostname = not hasnoca and sslp.get("check_hostname", True) verify_mode_value = sslp.get("verify_mode") if verify_mode_value is None: @@ -430,6 +440,8 @@ def _force_close(self): """Close connection without QUIT message.""" + if self._rfile: + self._rfile.close() if self._sock: try: self._sock.close() @@ -571,9 +583,9 @@ return self._affected_rows def kill(self, thread_id): - arg = struct.pack("<I", thread_id) - self._execute_command(COMMAND.COM_PROCESS_KILL, arg) - return self._read_ok_packet() + if not isinstance(thread_id, int): + raise TypeError("thread_id must be an integer") + self.query(f"KILL {thread_id:d}") def ping(self, reconnect=True): """ @@ -696,12 +708,7 @@ if self.autocommit_mode is not None: self.autocommit(self.autocommit_mode) except BaseException as e: - self._rfile = None - if sock is not None: - try: - sock.close() - except: # noqa - pass + self._force_close() if isinstance(e, (OSError, IOError)): exc = err.OperationalError( @@ -812,16 +819,10 @@ def _read_query_result(self, unbuffered=False): self._result = None + result = MySQLResult(self) if unbuffered: - try: - result = MySQLResult(self) - result.init_unbuffered_query() - except: - result.unbuffered_active = False - result.connection = None - raise + result.init_unbuffered_query() else: - result = MySQLResult(self) result.read() self._result = result if result.server_status is not None: @@ -1158,6 +1159,9 @@ else: self._auth_plugin_name = data[i:server_end].decode("utf-8") + if _DEFAULT_AUTH_PLUGIN is not None: # for tests + self._auth_plugin_name = _DEFAULT_AUTH_PLUGIN + def get_server_info(self): return self.server_version @@ -1212,17 +1216,16 @@ :raise OperationalError: If the connection to the MySQL server is lost. :raise InternalError: """ - self.unbuffered_active = True first_packet = self.connection._read_packet() if first_packet.is_ok_packet(): - self._read_ok_packet(first_packet) - self.unbuffered_active = False self.connection = None + self._read_ok_packet(first_packet) elif first_packet.is_load_local_packet(): - self._read_load_local_packet(first_packet) - self.unbuffered_active = False - self.connection = None + try: + self._read_load_local_packet(first_packet) + finally: + self.connection = None else: self.field_count = first_packet.read_length_encoded_integer() self._get_descriptions() @@ -1231,6 +1234,7 @@ # value of a 64bit unsigned integer. Since we're emulating MySQLdb, # we set it to this instead of None, which would be preferred. self.affected_rows = 18446744073709551615 + self.unbuffered_active = True def _read_ok_packet(self, first_packet): ok_packet = OKPacketWrapper(first_packet) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyMySQL-1.1.1/pymysql/tests/test_charset.py new/PyMySQL-1.1.2/pymysql/tests/test_charset.py --- old/PyMySQL-1.1.1/pymysql/tests/test_charset.py 2024-05-21 13:02:25.000000000 +0200 +++ new/PyMySQL-1.1.2/pymysql/tests/test_charset.py 2025-08-24 14:53:42.000000000 +0200 @@ -21,5 +21,24 @@ ) # utf8 is alias of utf8mb4 since MySQL 8.0, and PyMySQL v1.1. - utf8 = pymysql.charset.charset_by_name("utf8") - assert utf8 == utf8mb4 + lowercase_utf8 = pymysql.charset.charset_by_name("utf8") + assert lowercase_utf8 == utf8mb4 + + # Regardless of case, UTF8 (which is special cased) should resolve to the same thing + uppercase_utf8 = pymysql.charset.charset_by_name("UTF8") + mixedcase_utf8 = pymysql.charset.charset_by_name("UtF8") + assert uppercase_utf8 == lowercase_utf8 + assert mixedcase_utf8 == lowercase_utf8 + + +def test_case_sensitivity(): + lowercase_latin1 = pymysql.charset.charset_by_name("latin1") + assert lowercase_latin1 is not None + + # lowercase and uppercase should resolve to the same charset + uppercase_latin1 = pymysql.charset.charset_by_name("LATIN1") + assert uppercase_latin1 == lowercase_latin1 + + # lowercase and mixed case should resolve to the same charset + mixedcase_latin1 = pymysql.charset.charset_by_name("LaTiN1") + assert mixedcase_latin1 == lowercase_latin1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyMySQL-1.1.1/pymysql/tests/test_connection.py new/PyMySQL-1.1.2/pymysql/tests/test_connection.py --- old/PyMySQL-1.1.1/pymysql/tests/test_connection.py 2024-05-21 13:02:25.000000000 +0200 +++ new/PyMySQL-1.1.2/pymysql/tests/test_connection.py 2025-08-24 14:53:42.000000000 +0200 @@ -558,8 +558,8 @@ sock.close() def test_ssl_connect(self): - dummy_ssl_context = mock.Mock(options=0) - with mock.patch("pymysql.connections.Connection.connect"), mock.patch( + dummy_ssl_context = mock.Mock(options=0, verify_flags=0) + with mock.patch( "pymysql.connections.ssl.create_default_context", new=mock.Mock(return_value=dummy_ssl_context), ) as create_default_context: @@ -570,6 +570,7 @@ "key": "key", "cipher": "cipher", }, + defer_connect=True, ) assert create_default_context.called assert dummy_ssl_context.check_hostname @@ -581,8 +582,8 @@ ) dummy_ssl_context.set_ciphers.assert_called_with("cipher") - dummy_ssl_context = mock.Mock(options=0) - with mock.patch("pymysql.connections.Connection.connect"), mock.patch( + dummy_ssl_context = mock.Mock(options=0, verify_flags=0) + with mock.patch( "pymysql.connections.ssl.create_default_context", new=mock.Mock(return_value=dummy_ssl_context), ) as create_default_context: @@ -592,6 +593,7 @@ "cert": "cert", "key": "key", }, + defer_connect=True, ) assert create_default_context.called assert dummy_ssl_context.check_hostname @@ -603,8 +605,8 @@ ) dummy_ssl_context.set_ciphers.assert_not_called - dummy_ssl_context = mock.Mock(options=0) - with mock.patch("pymysql.connections.Connection.connect"), mock.patch( + dummy_ssl_context = mock.Mock(options=0, verify_flags=0) + with mock.patch( "pymysql.connections.ssl.create_default_context", new=mock.Mock(return_value=dummy_ssl_context), ) as create_default_context: @@ -615,6 +617,7 @@ "key": "key", "password": "password", }, + defer_connect=True, ) assert create_default_context.called assert dummy_ssl_context.check_hostname @@ -626,13 +629,14 @@ ) dummy_ssl_context.set_ciphers.assert_not_called - dummy_ssl_context = mock.Mock(options=0) - with mock.patch("pymysql.connections.Connection.connect"), mock.patch( + dummy_ssl_context = mock.Mock(options=0, verify_flags=0) + with mock.patch( "pymysql.connections.ssl.create_default_context", new=mock.Mock(return_value=dummy_ssl_context), ) as create_default_context: pymysql.connect( ssl_ca="ca", + defer_connect=True, ) assert create_default_context.called assert not dummy_ssl_context.check_hostname @@ -640,8 +644,8 @@ dummy_ssl_context.load_cert_chain.assert_not_called dummy_ssl_context.set_ciphers.assert_not_called - dummy_ssl_context = mock.Mock(options=0) - with mock.patch("pymysql.connections.Connection.connect"), mock.patch( + dummy_ssl_context = mock.Mock(options=0, verify_flags=0) + with mock.patch( "pymysql.connections.ssl.create_default_context", new=mock.Mock(return_value=dummy_ssl_context), ) as create_default_context: @@ -649,6 +653,7 @@ ssl_ca="ca", ssl_cert="cert", ssl_key="key", + defer_connect=True, ) assert create_default_context.called assert not dummy_ssl_context.check_hostname @@ -661,8 +666,8 @@ dummy_ssl_context.set_ciphers.assert_not_called for ssl_verify_cert in (True, "1", "yes", "true"): - dummy_ssl_context = mock.Mock(options=0) - with mock.patch("pymysql.connections.Connection.connect"), mock.patch( + dummy_ssl_context = mock.Mock(options=0, verify_flags=0) + with mock.patch( "pymysql.connections.ssl.create_default_context", new=mock.Mock(return_value=dummy_ssl_context), ) as create_default_context: @@ -670,6 +675,7 @@ ssl_cert="cert", ssl_key="key", ssl_verify_cert=ssl_verify_cert, + defer_connect=True, ) assert create_default_context.called assert not dummy_ssl_context.check_hostname @@ -682,8 +688,8 @@ dummy_ssl_context.set_ciphers.assert_not_called for ssl_verify_cert in (None, False, "0", "no", "false"): - dummy_ssl_context = mock.Mock(options=0) - with mock.patch("pymysql.connections.Connection.connect"), mock.patch( + dummy_ssl_context = mock.Mock(options=0, verify_flags=0) + with mock.patch( "pymysql.connections.ssl.create_default_context", new=mock.Mock(return_value=dummy_ssl_context), ) as create_default_context: @@ -691,6 +697,7 @@ ssl_cert="cert", ssl_key="key", ssl_verify_cert=ssl_verify_cert, + defer_connect=True, ) assert create_default_context.called assert not dummy_ssl_context.check_hostname @@ -704,8 +711,8 @@ for ssl_ca in ("ca", None): for ssl_verify_cert in ("foo", "bar", ""): - dummy_ssl_context = mock.Mock(options=0) - with mock.patch("pymysql.connections.Connection.connect"), mock.patch( + dummy_ssl_context = mock.Mock(options=0, verify_flags=0) + with mock.patch( "pymysql.connections.ssl.create_default_context", new=mock.Mock(return_value=dummy_ssl_context), ) as create_default_context: @@ -714,6 +721,7 @@ ssl_cert="cert", ssl_key="key", ssl_verify_cert=ssl_verify_cert, + defer_connect=True, ) assert create_default_context.called assert not dummy_ssl_context.check_hostname @@ -727,8 +735,8 @@ ) dummy_ssl_context.set_ciphers.assert_not_called - dummy_ssl_context = mock.Mock(options=0) - with mock.patch("pymysql.connections.Connection.connect"), mock.patch( + dummy_ssl_context = mock.Mock(options=0, verify_flags=0) + with mock.patch( "pymysql.connections.ssl.create_default_context", new=mock.Mock(return_value=dummy_ssl_context), ) as create_default_context: @@ -737,6 +745,7 @@ ssl_cert="cert", ssl_key="key", ssl_verify_identity=True, + defer_connect=True, ) assert create_default_context.called assert dummy_ssl_context.check_hostname @@ -748,8 +757,8 @@ ) dummy_ssl_context.set_ciphers.assert_not_called - dummy_ssl_context = mock.Mock(options=0) - with mock.patch("pymysql.connections.Connection.connect"), mock.patch( + dummy_ssl_context = mock.Mock(options=0, verify_flags=0) + with mock.patch( "pymysql.connections.ssl.create_default_context", new=mock.Mock(return_value=dummy_ssl_context), ) as create_default_context: @@ -759,6 +768,7 @@ ssl_key="key", ssl_key_password="password", ssl_verify_identity=True, + defer_connect=True, ) assert create_default_context.called assert dummy_ssl_context.check_hostname @@ -770,8 +780,8 @@ ) dummy_ssl_context.set_ciphers.assert_not_called - dummy_ssl_context = mock.Mock(options=0) - with mock.patch("pymysql.connections.Connection.connect"), mock.patch( + dummy_ssl_context = mock.Mock(options=0, verify_flags=0) + with mock.patch( "pymysql.connections.ssl.create_default_context", new=mock.Mock(return_value=dummy_ssl_context), ) as create_default_context: @@ -782,11 +792,12 @@ "cert": "cert", "key": "key", }, + defer_connect=True, ) assert not create_default_context.called - dummy_ssl_context = mock.Mock(options=0) - with mock.patch("pymysql.connections.Connection.connect"), mock.patch( + dummy_ssl_context = mock.Mock(options=0, verify_flags=0) + with mock.patch( "pymysql.connections.ssl.create_default_context", new=mock.Mock(return_value=dummy_ssl_context), ) as create_default_context: @@ -795,6 +806,7 @@ ssl_ca="ca", ssl_cert="cert", ssl_key="key", + defer_connect=True, ) assert not create_default_context.called @@ -854,7 +866,7 @@ mapping = con.encoders.copy() mapping[Foo] = escape_foo - #self.assertEqual(con.escape({"foo": Foo()}, mapping), {"foo": "bar"}) + # self.assertEqual(con.escape({"foo": Foo()}, mapping), {"foo": "bar"}) with self.assertRaises(TypeError): con.escape({"foo": Foo()}) @@ -883,3 +895,40 @@ con.commit() cur.execute("SELECT 3") self.assertEqual(cur.fetchone()[0], 3) + + def test_force_close_closes_socketio(self): + con = self.connect() + sock = con._sock + fileno = sock.fileno() + rfile = con._rfile + + con._force_close() + assert rfile.closed + assert sock._closed + assert sock.fileno() != fileno # should be set to -1 + + def test_socket_closed_on_exception_in_connect(self): + con = self.connect(defer_connect=True) + sock = None + rfile = None + fileno = -1 + + def _request_authentication(): + nonlocal sock, rfile, fileno + sock = con._sock + assert sock is not None + fileno = sock.fileno() + rfile = con._rfile + assert rfile is not None + raise TypeError + + con._request_authentication = _request_authentication + + with pytest.raises(TypeError): + con.connect() + assert not con.open + assert con._rfile is None + assert con._sock is None + assert rfile.closed + assert sock._closed + assert sock.fileno() != fileno # should be set to -1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyMySQL-1.1.1/pyproject.toml new/PyMySQL-1.1.2/pyproject.toml --- old/PyMySQL-1.1.1/pyproject.toml 2024-05-21 13:02:25.000000000 +0200 +++ new/PyMySQL-1.1.2/pyproject.toml 2025-08-24 14:53:42.000000000 +0200 @@ -7,23 +7,15 @@ ] dependencies = [] -requires-python = ">=3.7" +requires-python = ">=3.8" readme = "README.md" -license = {text = "MIT License"} +license = "MIT" keywords = ["MySQL"] classifiers = [ "Development Status :: 5 - Production/Stable", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", "Topic :: Database", ] dynamic = ["version"] @@ -39,9 +31,10 @@ [project.urls] "Project" = "https://github.com/PyMySQL/PyMySQL" "Documentation" = "https://pymysql.readthedocs.io/" +"Changelog" = "https://github.com/PyMySQL/PyMySQL/blob/main/CHANGELOG.md" [build-system] -requires = ["setuptools>=61"] +requires = ["setuptools>=77"] build-backend = "setuptools.build_meta" [tool.setuptools.packages.find] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/PyMySQL-1.1.1/tests/test_auth.py new/PyMySQL-1.1.2/tests/test_auth.py --- old/PyMySQL-1.1.1/tests/test_auth.py 2024-05-21 13:02:25.000000000 +0200 +++ new/PyMySQL-1.1.2/tests/test_auth.py 2025-08-24 14:53:42.000000000 +0200 @@ -71,6 +71,19 @@ con.query("FLUSH PRIVILEGES") con.close() + # Fast path after auth_switch_request + pymysql.connections._DEFAULT_AUTH_PLUGIN = "mysql_native_password" + con = pymysql.connect( + user="user_caching_sha2", + password=pass_caching_sha2, + host=host, + port=port, + ssl=ssl, + ) + con.query("FLUSH PRIVILEGES") + con.close() + pymysql.connections._DEFAULT_AUTH_PLUGIN = None + def test_caching_sha2_password_ssl(): con = pymysql.connect( @@ -88,7 +101,20 @@ password=pass_caching_sha2, host=host, port=port, - ssl=None, + ssl=ssl, + ) + con.query("FLUSH PRIVILEGES") + con.close() + + # Fast path after auth_switch_request + pymysql.connections._DEFAULT_AUTH_PLUGIN = "mysql_native_password" + con = pymysql.connect( + user="user_caching_sha2", + password=pass_caching_sha2, + host=host, + port=port, + ssl=ssl, ) con.query("FLUSH PRIVILEGES") con.close() + pymysql.connections._DEFAULT_AUTH_PLUGIN = None
