- name: Install dependencies
run: |
python -m pip install --upgrade pip
- pip install --upgrade tox setuptools
+ pip install --upgrade nox setuptools
pip list
- name: Run tests
- run: tox -e github-${{ matrix.build-type }} -- ${{ matrix.pytest-args }}
+ run: nox -v -s github-${{ matrix.build-type }} -- ${{ matrix.pytest-args }}
- run-tox:
- name: ${{ matrix.tox-env }}-${{ matrix.python-version }}
+ run-nox:
+ name: ${{ matrix.nox-env }}-${{ matrix.python-version }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- "ubuntu-22.04"
python-version:
- - "3.12"
- tox-env:
+ - "3.13"
+ nox-env:
- mypy
- - lint
- pep484
+ - pep8
fail-fast: false
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- pip install --upgrade tox setuptools
+ pip install --upgrade nox setuptools
pip list
- - name: Run tox
- run: tox -e ${{ matrix.tox-env }} ${{ matrix.pytest-args }}
+ - name: Run nox
+ run: nox -v -s ${{ matrix.nox-env }} -- ${{ matrix.pytest-args }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- pip install --upgrade tox setuptools
+ pip install --upgrade nox setuptools
pip list
- name: Run tests
- run: tox -e github-${{ matrix.build-type }} -- ${{ matrix.pytest-args }}
- env:
- # under free threading, make sure to disable GIL
- PYTHON_GIL: ${{ contains(matrix.python-version, 't') && '0' || '' }}
+ run: nox -v -s github-${{ matrix.build-type }} -- ${{ matrix.pytest-args }}
continue-on-error: ${{ matrix.python-version == 'pypy-3.10' }}
- run-tox:
- name: ${{ matrix.tox-env }}-${{ matrix.python-version }}
+ run-nox:
+ name: ${{ matrix.nox-env }}-${{ matrix.python-version }}
runs-on: ${{ matrix.os }}
strategy:
# run this job using this matrix, excluding some combinations below.
os:
- "ubuntu-22.04"
python-version:
- - "3.12"
- "3.13"
- tox-env:
+ nox-env:
- mypy
- pep484
-
- include:
- # run lint only on 3.12
- - tox-env: lint
- python-version: "3.12"
- os: "ubuntu-22.04"
+ - pep8
fail-fast: false
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- pip install --upgrade tox setuptools
+ pip install --upgrade nox setuptools
pip list
- - name: Run tox
- run: tox -e ${{ matrix.tox-env }} ${{ matrix.pytest-args }}
+ - name: Run nox
+ run: nox -v -e ${{ matrix.nox-env }} ${{ matrix.pytest-args }}
*.orig
*,cover
/.tox
+/.nox
/venv/
.venv
*.egg-info
# don't come in if --with-cextensions isn't specified.
recursive-include lib *.pyx *.pxd *.txt *.typed
-include README* AUTHORS LICENSE CHANGES* tox.ini
+include README* AUTHORS LICENSE CHANGES* tox.ini noxfile.py
prune doc/build/output
--- /dev/null
+.. change::
+ :tags: change, tests
+
+ A noxfile.py has been added to allow testing with nox. This is a direct
+ port of 2.1's move to nox, however leaves the tox.ini file in place and
+ retains all test documentation in terms of tox. Version 2.1 will move to
+ nox fully, including deprecation warnings for tox and new testing
+ documentation.
--- /dev/null
+"""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
+
+if True:
+ sys.path.insert(0, ".")
+ from tools.toxnox import extract_opts
+ from tools.toxnox import tox_parameters
+
+
+PYTHON_VERSIONS = [
+ "3.7",
+ "3.8",
+ "3.9",
+ "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)
+
+
+@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)
+
+
+@nox.session(name="github-nocext")
+def github_nocext(session: nox.Session) -> None:
+ """run tests for github actions"""
+
+ _tests(session, "sqlite", "cext", greenlet=False)
+
+
+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,
+ mypy: bool = False,
+) -> None:
+ # 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"]
+
+ if coverage:
+ assert not platform_intensive
+ cmd.extend(
+ [
+ "--cov=sqlalchemy",
+ "--cov-append",
+ "--cov-report",
+ "term",
+ "--cov-report",
+ "xml",
+ ],
+ )
+ includes_excludes["k"].append("not aaa_profiling")
+
+ cmd.extend(os.environ.get("TOX_WORKERS", "-n4").split())
+
+ if coverage:
+ 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, opts = extract_opts(session.posargs, "generate-junit", "dry-run")
+
+ if opts.generate_junit:
+ # produce individual junit files that are per-database
+ junitfile = f"junit-{database}.xml"
+ cmd.extend(["--junitxml", junitfile])
+
+ if database in ["oracle", "mssql", "sqlite_file"]:
+ cmd.extend(["--write-idents", "db_idents.txt"])
+
+ cmd.extend(posargs)
+
+ if opts.dry_run:
+ print(f"DRY RUN: command is: \n{' '.join(cmd)}")
+ return
+
+ try:
+ session.run(*cmd)
+ finally:
+ # Run cleanup for oracle/mssql
+ if database in ["oracle", "mssql", "sqlite_file"]:
+ if 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, opts = extract_opts(session.posargs, "generate-junit")
+
+ cmd = ["pytest", "-m", "mypy"]
+ if opts.generate_junit:
+ # produce individual junit files that are per-database
+ junitfile = "junit-mypy.xml"
+ cmd.extend(["--junitxml", junitfile])
+
+ 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 "
+ "noxfile.py",
+ # test with cython and without cython exts running
+ "slotscheck -m sqlalchemy",
+ "env DISABLE_SQLALCHEMY_CEXT_RUNTIME=1 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/walk_packages.py",
+ ]:
+
+ session.run(*cmd.split(), external=True)
"cython>=0.29.24; platform_python_implementation == 'CPython'", # Skip cython when using pypy
]
+[dependency-groups]
+tests = [
+ "pytest>=7.0.0,<8.4",
+ "pytest-xdist",
+]
+
+coverage = ["pytest-cov"]
+
+tests-greenlet = ["sqlalchemy[asyncio]"]
+
+tests-sqlite = []
+
+tests-sqlite-asyncio = [
+ {include-group = "tests-greenlet"},
+ {include-group = "tests-sqlite"},
+ "sqlalchemy[aiosqlite]"
+]
+
+tests-sqlite-file = [{include-group = "tests-sqlite"}]
+tests-sqlite-file-asyncio = [{include-group = "tests-sqlite-asyncio"}]
+
+tests-postgresql = [
+ "sqlalchemy[postgresql]",
+ "sqlalchemy[postgresql-psycopg]",
+ "sqlalchemy[postgresql-pg8000]",
+]
+
+tests-postgresql-asyncio = [
+ {include-group = "tests-greenlet"},
+ {include-group = "tests-postgresql"},
+ "sqlalchemy[postgresql-asyncpg]"
+]
+
+tests-mysql = [
+ "sqlalchemy[mysql]",
+ "sqlalchemy[pymysql]",
+ "sqlalchemy[mysql-connector]",
+
+ # originally to fix https://jira.mariadb.org/browse/CONPY-318,
+ # more recent versions still have attribute errors and other random
+ # problems
+ "mariadb>=1.0.1,!=1.1.2,!=1.1.5,!=1.1.10,<1.1.13",
+
+]
+tests-mysql-asyncio = [
+ {include-group = "tests-greenlet"},
+ {include-group = "tests-mysql"},
+ "sqlalchemy[aiomysql]",
+ "sqlalchemy[asyncmy]",
+]
+
+tests-oracle-asyncio = [
+ {include-group = "tests-greenlet"},
+ {include-group = "tests-oracle"},
+]
+tests-oracle = [
+ "sqlalchemy[oracle]",
+ "sqlalchemy[oracle-oracledb]",
+]
+
+tests-mssql = [
+ "sqlalchemy[mssql]",
+ "sqlalchemy[mssql-pymssql]",
+]
+
+tests-mssql-asyncio = [
+ {include-group = "tests-greenlet"},
+ {include-group = "tests-mssql"},
+ "aioodbc"
+]
+
+lint = [
+ {include-group = "tests-greenlet"},
+ "flake8>=7.2.0",
+ "flake8-import-order>=0.19.2",
+ "flake8-import-single==0.1.5",
+ "flake8-builtins",
+ "flake8-future-annotations>=0.0.5",
+ "flake8-docstrings",
+ "flake8-unused-arguments",
+ "flake8-rst-docstrings",
+ "pydocstyle<4.0.0",
+ "pygments",
+ "black==25.1.0",
+ "slotscheck>=0.17.0",
+ "zimports", # required by generate_tuple_map_overloads
+]
+
+mypy = [
+ {include-group = "tests-greenlet"},
+ "mypy>=1.16.0",
+ "nox", # because we check noxfile.py
+ "pytest>8,<8.4", # alembic/testing imports pytest
+ "types-greenlet",
+]
+
+
[tool.black]
line-length = 79
target-version = ['py37']
--- /dev/null
+"""Provides the tox_parameters() utility, which generates parameterized
+sections for nox tests, which include tags that indicate various combinations
+of those parameters in such a way that it's somewhat similar to how
+we were using the tox project; where individual dash-separated tags could
+be added to add more specificity to the suite configuation, or omitting them
+would fall back to defaults.
+
+
+"""
+
+from __future__ import annotations
+
+import collections
+import re
+import sys
+from typing import Any
+from typing import Callable
+from typing import Generator
+from typing import Sequence
+
+import nox
+
+OUR_PYTHON = f"{sys.version_info.major}.{sys.version_info.minor}"
+
+
+def tox_parameters(
+ names: Sequence[str],
+ token_lists: Sequence[Sequence[str]],
+ *,
+ base_tag: str | None = None,
+ filter_: Callable[..., bool] | None = None,
+ always_include_in_tag: Sequence[str] | None = None,
+) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
+ r"""Decorator to create a parameter/tagging structure for a nox session
+ function that acts to a large degree like tox's generative environments.
+
+ The output is a ``nox.parametrize()`` decorator that's built up from
+ individual ``nox.param()`` instances.
+
+ :param names: names of the parameters sent to the session function.
+ These names go straight to the first argument of ``nox.parametrize()``
+ and should all match argument names accepted by the decorated function
+ (except for ``python``, which is optional).
+ :param token_lists: a sequence of lists of values for each parameter. a
+ ``nox.param()`` will be created for the full product of these values,
+ minus those filtered out using the ``filter_`` callable. These tokens
+ are used to create the args, tags, and ids of each ``nox.param()``. The
+ list of tags will be generated out including all values for a parameter
+ joined by ``-``, as well as combinations that include a subset of those
+ values, where the omitted elements of the tag are implicitly considered to
+ match the "default" value, indicated by them being first in their
+ collection (with the exception of "python", where the current python in
+ use is the default). Additionally, values that start with an underscore
+ are omitted from all ids and tags. Values that refer to Python versions
+ wlil be expanded to the full Python executable name when passed as
+ arguments to the session function, which is currently a workaround to
+ allow free-threaded python interpreters to be located.
+ :param base_tag: optional tag that will be appended to all tags generated,
+ e.g. if the decorator yields tags like ``python314-x86-windows``, a
+ ``basetag`` value of ``all`` would yield the
+ tag as ``python314-x86-windows-all``.
+ :param filter\_: optional filtering function, must accept keyword arguments
+ matching the names in ``names``. Returns True or False indicating if
+ a certain tag combination should be included.
+ :param always_include_in_tag: list of names from ``names`` that indicate
+ parameters that should always be part of all tags, and not be omitted
+ as a "default"
+
+
+ """
+
+ PY_RE = re.compile(r"(?:python)?([234]\.\d+(t?))")
+
+ def _is_py_version(token: str) -> bool:
+ return bool(PY_RE.match(token))
+
+ def _expand_python_version(token: str) -> str:
+ """expand pyx.y(t) tags into executable names.
+
+ Works around nox issue fixed at
+ https://github.com/wntrblm/nox/pull/999 by providing full executable
+ name
+
+ """
+ if sys.platform == "win32":
+ return token
+
+ m = PY_RE.match(token)
+
+ # do this matching minimally so that it only happens for the
+ # free-threaded versions. on windows, the "pythonx.y" syntax doesn't
+ # work due to the use of the "py" tool
+ if m and m.group(2) == "t":
+ return f"python{m.group(1)}"
+ else:
+ return token
+
+ def _python_to_tag(token: str) -> str:
+ m = PY_RE.match(token)
+ if m:
+ return f"py{m.group(1).replace('.', '')}"
+ else:
+ return token
+
+ if always_include_in_tag:
+ name_to_list = dict(zip(names, token_lists))
+ must_be_present = [
+ name_to_list[name] for name in always_include_in_tag
+ ]
+ else:
+ must_be_present = None
+
+ def _recur_param(
+ prevtokens: list[str],
+ prevtags: list[str],
+ token_lists: Sequence[Sequence[str]],
+ ) -> Generator[tuple[list[str], list[str], str], None, None]:
+
+ if not token_lists:
+ return
+
+ tokens = token_lists[0]
+ remainder = token_lists[1:]
+
+ for i, token in enumerate(tokens):
+
+ if _is_py_version(token):
+ is_our_python = token == OUR_PYTHON
+ tokentag = _python_to_tag(token)
+ is_default_token = is_our_python
+ else:
+ is_our_python = False
+ tokentag = token
+ is_default_token = i == 0
+
+ if is_our_python:
+ our_python_tags = ["py"]
+ else:
+ our_python_tags = []
+
+ if not tokentag.startswith("_"):
+ tags = (
+ prevtags
+ + [tokentag]
+ + [tag + "-" + tokentag for tag in prevtags]
+ + our_python_tags
+ )
+ else:
+ tags = prevtags + our_python_tags
+
+ if remainder:
+ for args, newtags, ids in _recur_param(
+ prevtokens + [token], tags, remainder
+ ):
+ if not is_default_token:
+ newtags = [
+ t
+ for t in newtags
+ if tokentag in t or t in our_python_tags
+ ]
+
+ yield args, newtags, ids
+ else:
+ if not is_default_token:
+ newtags = [
+ t
+ for t in tags
+ if tokentag in t or t in our_python_tags
+ ]
+ else:
+ newtags = tags
+
+ if base_tag:
+ newtags = [t + f"-{base_tag}" for t in newtags]
+ if must_be_present:
+ for t in list(newtags):
+ for required_tokens in must_be_present:
+ if not any(r in t for r in required_tokens):
+ newtags.remove(t)
+ break
+
+ yield prevtokens + [token], newtags, "-".join(
+ _python_to_tag(t)
+ for t in prevtokens + [token]
+ if not t.startswith("_")
+ )
+
+ params = [
+ nox.param(
+ *[_expand_python_version(a) for a in args], tags=tags, id=ids
+ )
+ for args, tags, ids in _recur_param([], [], token_lists)
+ if filter_ is None or filter_(**dict(zip(names, args)))
+ ]
+
+ # for p in params:
+ # print(f"PARAM {'-'.join(p.args)} TAGS {p.tags}")
+
+ return nox.parametrize(names, params)
+
+
+def extract_opts(posargs: list[str], *args: str) -> tuple[list[str], Any]:
+ """Pop individual flag options from session.posargs.
+
+ Returns a named tuple with the individual flag options indicated,
+ as well the new posargs with those flags removed from the string list
+ so that the posargs can be forwarded onto pytest.
+
+ Basically if nox had an option for additional environmental flags that
+ didn't require putting them after ``--``, we wouldn't need this, but this
+ is probably more flexible.
+
+ """
+ underscore_args = [arg.replace("-", "_") for arg in args]
+ return_tuple = collections.namedtuple("options", underscore_args) # type: ignore # noqa: E501
+
+ look_for_args = {f"--{arg}": idx for idx, arg in enumerate(args)}
+ return_args = [False for arg in args]
+
+ def extract(arg: str) -> bool:
+ if arg in look_for_args:
+ return_args[look_for_args[arg]] = True
+ return True
+ else:
+ return False
+
+ return [arg for arg in posargs if not extract(arg)], return_tuple(
+ *return_args
+ )
--- /dev/null
+def warn_tox():
+ print(
+ "\n"
+ + "=" * 80
+ + "\n\033[1;31m ⚠️ NOTE: TOX IS DEPRECATED IN THIS PROJECT! ⚠️"
+ "\033[0m\n\033[1;33m "
+ "Please use nox instead for running tests.\033[0m\n" + "=" * 80 + "\n"
+ )
+
+
+if __name__ == "__main__":
+ warn_tox()
# PYTHONPATH - erased so that we use the build that's present
# in .tox as the SQLAlchemy library to be imported
#
-# PYTHONUSERSITE - this *MUST* be set so that the ./lib/ import
+# PYTHONNOUSERSITE - this *MUST* be set so that the ./lib/ import
# set up explicitly in test/conftest.py is *disabled*, again so that
# when SQLAlchemy is built into the .tox area, we use that and not the
# local checkout, at least when usedevelop=False
types-greenlet
commands =
mypy {env:MYPY_COLOR} ./lib/sqlalchemy
- # pyright changes too often with not-exactly-correct errors
- # suddently appearing for it to be stable enough for CI
- # pyright
extras =
{[greenletextras]extras}