Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ dmypy.json
!.vscode/launch.json
# Version file generated by hatch-vcs
src/pytest_databases/_version.py

CLAUDE.md
GEMINI.md
.ruff_cache


Expand Down
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ exclude: "^docs/conf.py"

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
rev: v6.0.0
hooks:
- id: trailing-whitespace
- id: check-added-large-files
Expand All @@ -19,7 +19,7 @@ repos:

# Ruff replaces black, flake8, autoflake and isort
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: "v0.11.11" # make sure this is always consistent with hatch configs
rev: "v0.12.9" # make sure this is always consistent with hatch configs
hooks:
- id: ruff
args: [--config, ./pyproject.toml]
Expand Down
14 changes: 4 additions & 10 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,11 @@
"python.autoComplete.extraPaths": ["${workspaceFolder}/src"],
"python.terminal.activateEnvInCurrentTerminal": true,
"python.terminal.executeInFileDir": true,
"python.testing.pytestEnabled": true,
"autoDocstring.guessTypes": false,
"python.testing.pytestEnabled": true,
"python.analysis.autoImportCompletions": true,
"python.analysis.autoFormatStrings": true,
"editor.formatOnSave": true,
"notebook.formatOnSave.enabled": true,
"black-formatter.args": ["--line-length=120"],
"notebook.formatOnSave.enabled": true,
"evenBetterToml.formatter.reorderKeys": true,
"evenBetterToml.formatter.trailingNewline": true,
"evenBetterToml.formatter.columnWidth": 120,
Expand All @@ -61,10 +59,7 @@
"python.analysis.fixAll": [
"source.unusedImports",
"source.convertImportFormat"
],
"sqltools.disableReleaseNotifications": true,
"sqltools.disableNodeDetectNotifications": true,
"cloudcode.duetAI.enable": true,
],
"cloudcode.compute.sshInternalIp": true,
"python.testing.pytestArgs": ["tests"],
"markdownlint.run": "onSave",
Expand All @@ -88,6 +83,5 @@
"tag:yaml.org,2002:python/name:material.extensions.emoji.to_svg",
"tag:yaml.org,2002:python/name:material.extensions.emoji.twemoji",
"tag:yaml.org,2002:python/name:pymdownx.superfences.fence_code_format"
],
"python.analysis.extraPaths": ["${workspaceFolder}/src"]
],
}
6 changes: 3 additions & 3 deletions docs/supported-databases/mysql.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ Usage Example
.. code-block:: python

import pytest
import mysql.connector
import pymysql
from pytest_databases.docker.mysql import MySQLService

pytest_plugins = ["pytest_databases.docker.mysql"]

def test(mysql_service: MySQLService) -> None:
with mysql.connector.connect(
with pymysql.connect(
host=mysql_service.host,
port=mysql_service.port,
user=mysql_service.user,
Expand All @@ -33,7 +33,7 @@ Usage Example
resp = cursor.fetchone()
assert resp is not None and resp[0] == 1

def test(mysql_connection: mysql.connector.MySQLConnection) -> None:
def test(mysql_connection: pymysql.Connection) -> None:
with mysql_connection.cursor() as cursor:
cursor.execute("CREATE TABLE if not exists simple_table as SELECT 1 as the_value")
cursor.execute("select * from simple_table")
Expand Down
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ keydb = ["redis"]
mariadb = ["mariadb"]
minio = ["minio"]
mssql = ["pymssql"]
mysql = ["mysql-connector-python"]
mysql = ["pymysql[rsa]"]
oracle = ["oracledb"]
postgres = ["psycopg>=3"]
redis = ["redis"]
Expand All @@ -86,7 +86,8 @@ dev = [
"pytest-xdist",
"pytest-sugar",
"slotscheck",
"psycopg-binary", # This fixes tests failing on M series CPUs.
"psycopg-binary",
"pymysql[rsa,ed25519]", # This fixes tests failing on M series CPUs.
# lint
"mypy",
"ruff",
Expand Down
53 changes: 32 additions & 21 deletions src/pytest_databases/docker/mysql.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from dataclasses import dataclass
from typing import TYPE_CHECKING

import mysql.connector
import pymysql
import pytest

from pytest_databases._service import DockerService, ServiceContainer
Expand All @@ -13,7 +13,7 @@
if TYPE_CHECKING:
from collections.abc import Generator

from mysql.connector.abstracts import MySQLConnectionAbstract
from pymysql import Connection

from pytest_databases.types import XdistIsolationLevel

Expand Down Expand Up @@ -50,23 +50,23 @@ def _provide_mysql_service(

def check(_service: ServiceContainer) -> bool:
try:
conn = mysql.connector.connect(
conn = pymysql.connect(
host=_service.host,
port=_service.port,
user=user,
database=database,
password=password,
)
except mysql.connector.errors.OperationalError as exc:
if "Lost connection" in exc.msg: # type: ignore
except pymysql.OperationalError as exc:
if "Lost connection" in str(exc):
return False
raise

try:
with conn.cursor() as cursor:
cursor.execute("select 1 as is_available")
resp = cursor.fetchone()
return resp is not None and resp[0] == 1 # type: ignore
return resp is not None and resp[0] == 1
finally:
with contextlib.suppress(Exception):
conn.close()
Expand All @@ -80,6 +80,21 @@ def check(_service: ServiceContainer) -> bool:
else:
db_name += suffix

# For MySQL 8, we need to handle authentication plugin compatibility
exec_commands = (
f'mysql --user=root --password={root_password} -e "CREATE DATABASE {db_name};'
f"GRANT ALL PRIVILEGES ON *.* TO '{user}'@'%'; "
)

# For MySQL 8, change the authentication plugin to mysql_native_password
# to avoid issues with caching_sha2_password and cryptography library
if "mysql:8" in image or "mysql:9" in image:
exec_commands += (
f"ALTER USER '{user}'@'%' IDENTIFIED WITH mysql_native_password BY '{password}'; "
)

exec_commands += 'FLUSH PRIVILEGES;"'

with docker_service.run(
image=image,
check=check,
Expand All @@ -95,11 +110,7 @@ def check(_service: ServiceContainer) -> bool:
},
timeout=60,
pause=0.5,
exec_after_start=(
f'mysql --user=root --password={root_password} -e "CREATE DATABASE {db_name};'
f"GRANT ALL PRIVILEGES ON *.* TO '{user}'@'%'; "
'FLUSH PRIVILEGES;"'
),
exec_after_start=exec_commands,
transient=isolation_level == "server",
platform=platform,
) as service:
Expand Down Expand Up @@ -166,41 +177,41 @@ def mysql_8_service(


@pytest.fixture(scope="session")
def mysql_56_connection(mysql_56_service: MySQLService) -> Generator[MySQLConnectionAbstract, None, None]:
with mysql.connector.connect(
def mysql_56_connection(mysql_56_service: MySQLService) -> Generator[Connection, None, None]:
with pymysql.connect(
host=mysql_56_service.host,
port=mysql_56_service.port,
user=mysql_56_service.user,
database=mysql_56_service.db,
password=mysql_56_service.password,
) as conn:
yield conn # type: ignore
yield conn


@pytest.fixture(scope="session")
def mysql_57_connection(mysql_57_service: MySQLService) -> Generator[MySQLConnectionAbstract, None, None]:
with mysql.connector.connect(
def mysql_57_connection(mysql_57_service: MySQLService) -> Generator[Connection, None, None]:
with pymysql.connect(
host=mysql_57_service.host,
port=mysql_57_service.port,
user=mysql_57_service.user,
database=mysql_57_service.db,
password=mysql_57_service.password,
) as conn:
yield conn # type: ignore
yield conn


@pytest.fixture(scope="session")
def mysql_connection(mysql_8_connection: MySQLConnectionAbstract) -> MySQLConnectionAbstract:
def mysql_connection(mysql_8_connection: Connection) -> Connection:
return mysql_8_connection


@pytest.fixture(scope="session")
def mysql_8_connection(mysql_8_service: MySQLService) -> Generator[MySQLConnectionAbstract, None, None]:
with mysql.connector.connect(
def mysql_8_connection(mysql_8_service: MySQLService) -> Generator[Connection, None, None]:
with pymysql.connect(
host=mysql_8_service.host,
port=mysql_8_service.port,
user=mysql_8_service.user,
database=mysql_8_service.db,
password=mysql_8_service.password,
) as conn:
yield conn # type: ignore
yield conn
4 changes: 2 additions & 2 deletions tests/test_mysql.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
)
def test_service_fixture(pytester: pytest.Pytester, service_fixture: str) -> None:
pytester.makepyfile(f"""
import mysql.connector
import pymysql
pytest_plugins = ["pytest_databases.docker.mysql"]

def test({service_fixture}):
with mysql.connector.connect(
with pymysql.connect(
host={service_fixture}.host,
port={service_fixture}.port,
user={service_fixture}.user,
Expand Down
Loading
Loading