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 @@ [![Documentation Status](https://readthedocs.org/projects/pymysql/badge/?version=latest)](https://pymysql.readthedocs.io/) [![codecov](https://codecov.io/gh/PyMySQL/PyMySQL/branch/main/graph/badge.svg?token=ppEuaNXBW4)](https://codecov.io/gh/PyMySQL/PyMySQL) +[![Ask DeepWiki](https://deepwiki.com/badge.svg)](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("