mirror of
https://github.com/sqlalchemy/sqlalchemy.git
synced 2026-05-06 08:56:51 -04:00
54a2991b50
Change-Id: If56ec107c0fd34333a48281486ac6e6fbc80cdde
384 lines
11 KiB
Python
384 lines
11 KiB
Python
"""Nox configuration for SQLAlchemy."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
from typing import Dict
|
|
from typing import List
|
|
from typing import Set
|
|
|
|
import nox
|
|
|
|
nox.needs_version = ">=2025.10.16"
|
|
|
|
|
|
if True:
|
|
sys.path.insert(0, ".")
|
|
from tools.toxnox import apply_pytest_opts
|
|
from tools.toxnox import tox_parameters
|
|
|
|
|
|
PYTHON_VERSIONS = ["3.10", "3.11", "3.12", "3.13", "3.13t", "3.14", "3.14t"]
|
|
DATABASES = ["sqlite", "sqlite_file", "postgresql", "mysql", "oracle", "mssql"]
|
|
CEXT = ["_auto", "cext", "nocext"]
|
|
GREENLET = ["_greenlet", "nogreenlet"]
|
|
BACKENDONLY = ["_all", "backendonly", "memusage"]
|
|
|
|
# table of ``--dbdriver`` names to use on the pytest command line, which
|
|
# match to dialect names
|
|
DB_CLI_NAMES = {
|
|
"sqlite": {
|
|
"nogreenlet": {"sqlite", "pysqlite_numeric"},
|
|
"greenlet": {"aiosqlite"},
|
|
},
|
|
"sqlite_file": {
|
|
"nogreenlet": {"sqlite"},
|
|
"greenlet": {"aiosqlite"},
|
|
},
|
|
"postgresql": {
|
|
"nogreenlet": {"psycopg2", "pg8000", "psycopg"},
|
|
"greenlet": {"asyncpg", "psycopg_async"},
|
|
},
|
|
"mysql": {
|
|
"nogreenlet": {"mysqldb", "pymysql", "mariadbconnector"},
|
|
"greenlet": {"asyncmy", "aiomysql"},
|
|
},
|
|
"oracle": {
|
|
"nogreenlet": {"cx_oracle", "oracledb"},
|
|
"greenlet": {"oracledb_async"},
|
|
},
|
|
"mssql": {"nogreenlet": {"pyodbc", "pymssql"}, "greenlet": {"aioodbc"}},
|
|
}
|
|
|
|
|
|
def _setup_for_driver(
|
|
session: nox.Session,
|
|
cmd: List[str],
|
|
basename: str,
|
|
greenlet: bool = False,
|
|
) -> None:
|
|
|
|
# install driver deps listed out in pyproject.toml
|
|
nogreenlet_deps = f"tests-{basename.replace('_', '-')}"
|
|
greenlet_deps = f"tests-{basename.replace('_', '-')}-asyncio"
|
|
|
|
deps = nox.project.dependency_groups(
|
|
pyproject,
|
|
(greenlet_deps if greenlet else nogreenlet_deps),
|
|
)
|
|
if deps:
|
|
session.install(*deps)
|
|
|
|
# set up top level ``--db`` sent to pytest command line, which looks
|
|
# up a base URL in the [db] section of setup.cfg. Environment variable
|
|
# substitution used by CI is also available.
|
|
|
|
# e.g. TOX_POSTGRESQL, TOX_MYSQL, etc.
|
|
dburl_env = f"TOX_{basename.upper()}"
|
|
# e.g. --db postgresql, --db mysql, etc.
|
|
default_dburl = f"--db {basename}"
|
|
cmd.extend(os.environ.get(dburl_env, default_dburl).split())
|
|
|
|
# set up extra drivers using --dbdriver. this first looks in
|
|
# an environment variable before making use of the DB_CLI_NAMES
|
|
# lookup table
|
|
|
|
# e.g. EXTRA_PG_DRIVERS, EXTRA_MYSQL_DRIVERS, etc.
|
|
if basename == "postgresql":
|
|
extra_driver_env = "EXTRA_PG_DRIVERS"
|
|
else:
|
|
extra_driver_env = f"EXTRA_{basename.upper()}_DRIVERS"
|
|
env_dbdrivers = os.environ.get(extra_driver_env, None)
|
|
if env_dbdrivers:
|
|
cmd.extend(env_dbdrivers.split())
|
|
return
|
|
|
|
# use fixed names in DB_CLI_NAMES
|
|
extra_drivers: Dict[str, Set[str]] = DB_CLI_NAMES[basename]
|
|
dbdrivers = extra_drivers["nogreenlet"]
|
|
if greenlet:
|
|
dbdrivers.update(extra_drivers["greenlet"])
|
|
|
|
for dbdriver in dbdrivers:
|
|
cmd.extend(["--dbdriver", dbdriver])
|
|
|
|
|
|
pyproject = nox.project.load_toml("pyproject.toml")
|
|
|
|
nox.options.sessions = ["tests"]
|
|
nox.options.tags = ["py"]
|
|
|
|
|
|
@nox.session()
|
|
@tox_parameters(
|
|
["python", "database", "cext", "greenlet", "backendonly"],
|
|
[
|
|
PYTHON_VERSIONS,
|
|
DATABASES,
|
|
CEXT,
|
|
GREENLET,
|
|
BACKENDONLY,
|
|
],
|
|
)
|
|
def tests(
|
|
session: nox.Session,
|
|
database: str,
|
|
greenlet: str,
|
|
backendonly: str,
|
|
cext: str,
|
|
) -> None:
|
|
"""run the main test suite"""
|
|
|
|
_tests(
|
|
session,
|
|
database,
|
|
greenlet=greenlet == "_greenlet",
|
|
backendonly=backendonly == "backendonly",
|
|
platform_intensive=backendonly == "memusage",
|
|
cext=cext,
|
|
)
|
|
|
|
|
|
@nox.session(name="coverage")
|
|
@tox_parameters(
|
|
["database", "cext", "backendonly"],
|
|
[DATABASES, CEXT, ["_all", "backendonly"]],
|
|
base_tag="coverage",
|
|
)
|
|
def coverage(
|
|
session: nox.Session, database: str, cext: str, backendonly: str
|
|
) -> None:
|
|
"""Run tests with coverage."""
|
|
|
|
_tests(
|
|
session,
|
|
database,
|
|
cext,
|
|
timing_intensive=False,
|
|
backendonly=backendonly == "backendonly",
|
|
coverage=True,
|
|
)
|
|
|
|
|
|
@nox.session(name="github-cext-greenlet")
|
|
def github_cext_greenlet(session: nox.Session) -> None:
|
|
"""run tests for github actions"""
|
|
|
|
_tests(
|
|
session,
|
|
"sqlite",
|
|
"cext",
|
|
greenlet=True,
|
|
timing_intensive=False,
|
|
platform_intensive=True,
|
|
)
|
|
|
|
|
|
@nox.session(name="github-cext")
|
|
def github_cext(session: nox.Session) -> None:
|
|
"""run tests for github actions"""
|
|
|
|
_tests(
|
|
session,
|
|
"sqlite",
|
|
"cext",
|
|
greenlet=False,
|
|
timing_intensive=False,
|
|
platform_intensive=True,
|
|
)
|
|
|
|
|
|
@nox.session(name="github-nocext")
|
|
def github_nocext(session: nox.Session) -> None:
|
|
"""run tests for github actions"""
|
|
|
|
_tests(session, "sqlite", "cext", greenlet=False, platform_intensive=True)
|
|
|
|
|
|
def _tests(
|
|
session: nox.Session,
|
|
database: str,
|
|
cext: str = "_auto",
|
|
greenlet: bool = True,
|
|
backendonly: bool = False,
|
|
platform_intensive: bool = False,
|
|
timing_intensive: bool = True,
|
|
coverage: bool = False,
|
|
) -> None:
|
|
|
|
# ensure external PYTHONPATH not interfering
|
|
session.env["PYTHONPATH"] = ""
|
|
|
|
# PYTHONNOUSERSITE - this *MUST* be set so that the ./lib/ import
|
|
# set up explicitly in test/conftest.py is *disabled*, so that
|
|
# when SQLAlchemy is built into the .nox area, we use that and not the
|
|
# local checkout, at least when usedevelop=False
|
|
session.env["PYTHONNOUSERSITE"] = "1"
|
|
|
|
freethreaded = isinstance(session.python, str) and session.python.endswith(
|
|
"t"
|
|
)
|
|
|
|
if freethreaded:
|
|
session.env["PYTHON_GIL"] = "0"
|
|
|
|
# greenlet frequently crashes with freethreading, so omit
|
|
# for the near future
|
|
greenlet = False
|
|
|
|
session.env["SQLALCHEMY_WARN_20"] = "1"
|
|
|
|
if cext == "cext":
|
|
session.env["REQUIRE_SQLALCHEMY_CEXT"] = "1"
|
|
elif cext == "nocext":
|
|
session.env["DISABLE_SQLALCHEMY_CEXT"] = "1"
|
|
|
|
includes_excludes: dict[str, list[str]] = {"k": [], "m": []}
|
|
|
|
if coverage:
|
|
timing_intensive = False
|
|
|
|
if platform_intensive:
|
|
# platform_intensive refers to test/aaa_profiling/test_memusage.py.
|
|
# it's only run exclusively of all other tests. does not include
|
|
# greenlet related tests
|
|
greenlet = False
|
|
# with "-m memory_intensive", only that suite will run, all
|
|
# other tests will be deselected by pytest
|
|
includes_excludes["m"].append("memory_intensive")
|
|
elif backendonly:
|
|
# with "-m backendonly", only tests with the backend pytest mark
|
|
# (or pytestplugin equivalent, like __backend__) will be selected
|
|
# by pytest
|
|
includes_excludes["m"].append("backend")
|
|
else:
|
|
includes_excludes["m"].append("not memory_intensive")
|
|
|
|
# the mypy suite is also run exclusively from the test_mypy
|
|
# session
|
|
includes_excludes["m"].append("not mypy")
|
|
|
|
if not timing_intensive:
|
|
includes_excludes["m"].append("not timing_intensive")
|
|
|
|
cmd = ["python", "-m", "pytest"]
|
|
|
|
cmd.extend(os.environ.get("TOX_WORKERS", "-n4").split())
|
|
|
|
if coverage:
|
|
assert not platform_intensive
|
|
includes_excludes["k"].append("not aaa_profiling")
|
|
session.install("-e", ".")
|
|
session.install(*nox.project.dependency_groups(pyproject, "coverage"))
|
|
else:
|
|
session.install(".")
|
|
|
|
session.install(*nox.project.dependency_groups(pyproject, "tests"))
|
|
|
|
if greenlet:
|
|
session.install(
|
|
*nox.project.dependency_groups(pyproject, "tests_greenlet")
|
|
)
|
|
else:
|
|
# note: if on SQLAlchemy 2.0, for "nogreenlet" need to do an explicit
|
|
# uninstall of greenlet since it's included in sqlalchemy dependencies
|
|
# in 2.1 it's an optional dependency
|
|
session.run("pip", "uninstall", "-y", "greenlet")
|
|
|
|
_setup_for_driver(session, cmd, database, greenlet=greenlet)
|
|
|
|
for letter, collection in includes_excludes.items():
|
|
if collection:
|
|
cmd.extend([f"-{letter}", " and ".join(collection)])
|
|
|
|
posargs = apply_pytest_opts(
|
|
session,
|
|
"sqlalchemy",
|
|
[
|
|
database,
|
|
cext,
|
|
"_greenlet" if greenlet else "nogreenlet",
|
|
"memusage" if platform_intensive else "_nomemusage",
|
|
"backendonly" if backendonly else "_notbackendonly",
|
|
],
|
|
coverage=coverage,
|
|
)
|
|
|
|
if database in ["oracle", "mssql", "sqlite_file"]:
|
|
cmd.extend(["--write-idents", "db_idents.txt"])
|
|
|
|
cmd.extend(posargs)
|
|
|
|
try:
|
|
session.run(*cmd)
|
|
finally:
|
|
# Run cleanup for oracle/mssql
|
|
if database in ["oracle", "mssql", "sqlite_file"] and os.path.exists(
|
|
"db_idents.txt"
|
|
):
|
|
session.run("python", "reap_dbs.py", "db_idents.txt")
|
|
os.unlink("db_idents.txt")
|
|
|
|
|
|
@nox.session(name="pep484")
|
|
def test_pep484(session: nox.Session) -> None:
|
|
"""Run mypy type checking."""
|
|
|
|
session.install(*nox.project.dependency_groups(pyproject, "mypy"))
|
|
|
|
session.install("-e", ".")
|
|
|
|
session.run(
|
|
"mypy",
|
|
"noxfile.py",
|
|
"./lib/sqlalchemy",
|
|
)
|
|
|
|
|
|
@nox.session(name="mypy")
|
|
def test_mypy(session: nox.Session) -> None:
|
|
"""run the typing integration test suite"""
|
|
|
|
session.install(*nox.project.dependency_groups(pyproject, "mypy"))
|
|
|
|
session.install("-e", ".")
|
|
|
|
posargs = apply_pytest_opts(
|
|
session,
|
|
"sqlalchemy",
|
|
["mypy"],
|
|
)
|
|
|
|
cmd = ["pytest", "-m", "mypy"]
|
|
|
|
session.run(*cmd, *posargs)
|
|
|
|
|
|
@nox.session(name="pep8")
|
|
def test_pep8(session: nox.Session) -> None:
|
|
"""Run linting and formatting checks."""
|
|
|
|
session.install("-e", ".")
|
|
|
|
session.install(*nox.project.dependency_groups(pyproject, "lint"))
|
|
|
|
for cmd in [
|
|
"flake8 ./lib/ ./test/ ./examples/ noxfile.py "
|
|
"setup.py doc/build/conf.py",
|
|
"flake8 --extend-ignore='' ./lib/sqlalchemy/ext/asyncio "
|
|
"./lib/sqlalchemy/orm/scoping.py",
|
|
"black --check ./lib/ ./test/ ./examples/ setup.py doc/build/conf.py",
|
|
"slotscheck -m sqlalchemy",
|
|
"python ./tools/format_docs_code.py --check",
|
|
"python ./tools/generate_tuple_map_overloads.py --check",
|
|
"python ./tools/generate_proxy_methods.py --check",
|
|
"python ./tools/sync_test_files.py --check",
|
|
"python ./tools/generate_sql_functions.py --check",
|
|
"python ./tools/normalize_file_headers.py --check",
|
|
"python ./tools/cython_imports.py --check",
|
|
"python ./tools/walk_packages.py",
|
|
]:
|
|
|
|
session.run(*cmd.split())
|