diff --git a/.coveragerc b/.coveragerc
index a9ec9942..efa9a2ff 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -2,6 +2,7 @@
branch = True
source =
pymysql
+ tests
omit = pymysql/tests/*
pymysql/tests/thirdparty/test_MySQLdb/*
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
deleted file mode 100644
index df49979e..00000000
--- a/.github/workflows/codeql-analysis.yml
+++ /dev/null
@@ -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 --git a/.github/workflows/codesee-arch-diagram.yml b/.github/workflows/codesee-arch-diagram.yml
deleted file mode 100644
index 806d41d1..00000000
--- a/.github/workflows/codesee-arch-diagram.yml
+++ /dev/null
@@ -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 --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml
index 269211c2..07ea6603 100644
--- a/.github/workflows/lint.yaml
+++ b/.github/workflows/lint.yaml
@@ -13,13 +13,12 @@ jobs:
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 --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml
deleted file mode 100644
index 21449e3b..00000000
--- a/.github/workflows/lock.yml
+++ /dev/null
@@ -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 --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index 6d59d8c4..6abc96b7 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -19,29 +19,30 @@ jobs:
matrix:
include:
- db: "mariadb:10.4"
- py: "3.8"
+ py: "3.13"
- db: "mariadb:10.5"
- py: "3.7"
+ py: "3.11"
- db: "mariadb:10.6"
- py: "3.11"
+ py: "3.10"
- db: "mariadb:10.6"
- py: "3.12"
+ py: "3.9"
- db: "mariadb:lts"
- py: "3.9"
+ py: "3.8"
- db: "mysql:5.7"
- py: "pypy-3.8"
+ py: "pypy-3.10"
- db: "mysql:8.0"
- py: "3.9"
+ py: "3.13"
mysql_auth: true
- - db: "mysql:8.0"
- py: "3.10"
+ - db: "mysql:8.4"
+ py: "3.8"
+ mysql_auth: true
services:
mysql:
@@ -106,4 +107,4 @@ jobs:
- name: Upload coverage reports to Codecov
if: github.repository == 'PyMySQL/PyMySQL'
- uses: codecov/codecov-action@v4
+ uses: codecov/codecov-action@v5
diff --git a/README.md b/README.md
index a91c6008..7d8bfc86 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,11 @@
[](https://pymysql.readthedocs.io/)
[](https://codecov.io/gh/PyMySQL/PyMySQL)
+[](https://deepwiki.com/PyMySQL/PyMySQL)
# PyMySQL
-This package contains a pure-Python MySQL and MariaDB 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
@@ -92,6 +93,7 @@ This example will print:
- DB-API 2.0:
- MySQL Reference Manuals:
+- Getting Help With MariaDB
- MySQL client/server protocol:
- "Connector" channel in MySQL Community Slack:
diff --git a/docs/requirements.txt b/docs/requirements.txt
index 01406623..48319f03 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,2 +1,2 @@
-sphinx~=7.2
-sphinx-rtd-theme~=2.0.0
+sphinx~=8.0
+sphinx-rtd-theme~=3.0.0
diff --git a/pymysql/_auth.py b/pymysql/_auth.py
index 8ce744fb..4790449b 100644
--- a/pymysql/_auth.py
+++ b/pymysql/_auth.py
@@ -166,6 +166,8 @@ def sha256_password_auth(conn, pkt):
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 @@ def caching_sha2_password_auth(conn, pkt):
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 --git a/pymysql/charset.py b/pymysql/charset.py
index b1c1ca8b..ec8e14e2 100644
--- a/pymysql/charset.py
+++ b/pymysql/charset.py
@@ -45,9 +45,10 @@ def by_id(self, id):
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 --git a/pymysql/connections.py b/pymysql/connections.py
index 5f60377e..99fcfcd0 100644
--- a/pymysql/connections.py
+++ b/pymysql/connections.py
@@ -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,
@@ -375,6 +378,12 @@ def _create_ssl_ctx(self, sslp):
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:
@@ -574,9 +583,9 @@ def affected_rows(self):
return self._affected_rows
def kill(self, thread_id):
- arg = struct.pack("