From: Mike Bayer Date: Thu, 7 Sep 2023 21:37:13 +0000 (-0400) Subject: ensure all modules are importable without pytest harnesses X-Git-Tag: rel_2_0_21~20^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d46dfd2e456f5b896d2d6f5677c806429873b456;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git ensure all modules are importable without pytest harnesses Fixed very old issue where the full extent of SQLAlchemy modules, including ``sqlalchemy.testing.fixtures``, could not be imported outside of a pytest run. This suits inspection utilities such as ``pkgutil`` that attempt to import all installed modules in all packages. Fixes: #10321 Change-Id: Ic2247c59b98f462036ad0d734aef9a96f290d778 --- diff --git a/doc/build/changelog/unreleased_20/10321.rst b/doc/build/changelog/unreleased_20/10321.rst new file mode 100644 index 0000000000..6186133af7 --- /dev/null +++ b/doc/build/changelog/unreleased_20/10321.rst @@ -0,0 +1,8 @@ +.. change:: + :tags: bug, setup + :tickets: 10321 + + Fixed very old issue where the full extent of SQLAlchemy modules, including + ``sqlalchemy.testing.fixtures``, could not be imported outside of a pytest + run. This suits inspection utilities such as ``pkgutil`` that attempt to + import all installed modules in all packages. diff --git a/lib/sqlalchemy/testing/config.py b/lib/sqlalchemy/testing/config.py index b8f03362f9..8430203dee 100644 --- a/lib/sqlalchemy/testing/config.py +++ b/lib/sqlalchemy/testing/config.py @@ -22,10 +22,15 @@ from typing import Tuple from typing import TypeVar from typing import Union +from . import mock +from . import requirements as _requirements from .util import fail from .. import util -requirements = None +# default requirements; this is replaced by plugin_base when pytest +# is run +requirements = _requirements.SuiteRequirements() + db = None db_url = None db_opts = None @@ -42,7 +47,42 @@ if typing.TYPE_CHECKING: _fixture_functions: FixtureFunctions else: - _fixture_functions = None # installed by plugin_base + + class _NullFixtureFunctions: + def _null_decorator(self): + def go(fn): + return fn + + return go + + def skip_test_exception(self, *arg, **kw): + return Exception() + + @property + def add_to_marker(self): + return mock.Mock() + + def mark_base_test_class(self): + return self._null_decorator() + + def combinations(self, *arg_sets, **kw): + return self._null_decorator() + + def param_ident(self, *parameters): + return self._null_decorator() + + def fixture(self, *arg, **kw): + return self._null_decorator() + + def get_current_test_name(self): + return None + + def async_test(self, fn): + return fn + + # default fixture functions; these are replaced by plugin_base when + # pytest runs + _fixture_functions = _NullFixtureFunctions() _FN = TypeVar("_FN", bound=Callable[..., Any]) @@ -121,10 +161,7 @@ def combinations( ) -def combinations_list( - arg_iterable: Iterable[Tuple[Any,]], - **kw, -): +def combinations_list(arg_iterable: Iterable[Tuple[Any, ...]], **kw): "As combination, but takes a single iterable" return combinations(*arg_iterable, **kw) diff --git a/lib/sqlalchemy/testing/plugin/plugin_base.py b/lib/sqlalchemy/testing/plugin/plugin_base.py index 393070d08c..563d8f68c2 100644 --- a/lib/sqlalchemy/testing/plugin/plugin_base.py +++ b/lib/sqlalchemy/testing/plugin/plugin_base.py @@ -473,9 +473,6 @@ def _setup_requirements(argument): from sqlalchemy.testing import config from sqlalchemy import testing - if config.requirements is not None: - return - modname, clsname = argument.split(":") # importlib.import_module() only introduced in 2.7, a little diff --git a/lib/sqlalchemy/testing/requirements.py b/lib/sqlalchemy/testing/requirements.py index 479e1beb6f..d13c548baf 100644 --- a/lib/sqlalchemy/testing/requirements.py +++ b/lib/sqlalchemy/testing/requirements.py @@ -22,9 +22,8 @@ from __future__ import annotations import platform from . import asyncio as _test_asyncio -from . import config from . import exclusions -from . import only_on +from .exclusions import only_on from .. import create_engine from .. import util from ..pool import QueuePool @@ -59,6 +58,12 @@ class SuiteRequirements(Requirements): return exclusions.closed() + @property + def uuid_data_type(self): + """Return databases that support the UUID datatype.""" + + return exclusions.closed() + @property def foreign_keys(self): """Target database must support foreign keys.""" @@ -1448,10 +1453,14 @@ class SuiteRequirements(Requirements): @property def timing_intensive(self): + from . import config + return config.add_to_marker.timing_intensive @property def memory_intensive(self): + from . import config + return config.add_to_marker.memory_intensive @property diff --git a/tools/walk_packages.py b/tools/walk_packages.py new file mode 100644 index 0000000000..efafbe991b --- /dev/null +++ b/tools/walk_packages.py @@ -0,0 +1,5 @@ +import pkgutil + +import sqlalchemy + +list(pkgutil.walk_packages(sqlalchemy.__path__, sqlalchemy.__name__ + ".")) diff --git a/tox.ini b/tox.ini index db1b15ccca..ec31a0bff0 100644 --- a/tox.ini +++ b/tox.ini @@ -240,6 +240,7 @@ commands = python ./tools/generate_proxy_methods.py --check python ./tools/sync_test_files.py --check python ./tools/generate_sql_functions.py --check + python ./tools/walk_packages.py # "pep8" env was renamed to "lint".