diff --git a/doc/build/changelog/unreleased_13/4460.rst b/doc/build/changelog/unreleased_13/4460.rst new file mode 100644 index 00000000000..52ae7430449 --- /dev/null +++ b/doc/build/changelog/unreleased_13/4460.rst @@ -0,0 +1,7 @@ +.. change:: + :tags: change, tests + :tickets: 4460 + + The test system has removed support for Nose, which is unmaintained for + several years and is producing warnings under Python 3. The test suite is + currently standardized on Pytest. Pull request courtesy Parth Shandilya. diff --git a/lib/sqlalchemy/testing/plugin/bootstrap.py b/lib/sqlalchemy/testing/plugin/bootstrap.py index 2230ae2a860..a95c947e200 100644 --- a/lib/sqlalchemy/testing/plugin/bootstrap.py +++ b/lib/sqlalchemy/testing/plugin/bootstrap.py @@ -1,5 +1,5 @@ """ -Bootstrapper for nose/pytest plugins. +Bootstrapper for test framework plugins. The entire rationale for this system is to get the modules in plugin/ imported without importing all of the supporting library, so that we can @@ -41,8 +41,5 @@ def load_file_as_module(name): if to_bootstrap == "pytest": sys.modules["sqla_plugin_base"] = load_file_as_module("plugin_base") sys.modules["sqla_pytestplugin"] = load_file_as_module("pytestplugin") -elif to_bootstrap == "nose": - sys.modules["sqla_plugin_base"] = load_file_as_module("plugin_base") - sys.modules["sqla_noseplugin"] = load_file_as_module("noseplugin") else: raise Exception("unknown bootstrap: %s" % to_bootstrap) # noqa diff --git a/lib/sqlalchemy/testing/plugin/noseplugin.py b/lib/sqlalchemy/testing/plugin/noseplugin.py deleted file mode 100644 index 05c62f79140..00000000000 --- a/lib/sqlalchemy/testing/plugin/noseplugin.py +++ /dev/null @@ -1,113 +0,0 @@ -# plugin/noseplugin.py -# Copyright (C) 2005-2019 the SQLAlchemy authors and contributors -# -# -# This module is part of SQLAlchemy and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - -"""Enhance nose with extra options and behaviors for running SQLAlchemy tests. - -Must be run via ./sqla_nose.py so that it is imported in the expected -way (e.g. as a package-less import). - -""" - -try: - # installed by bootstrap.py - import sqla_plugin_base as plugin_base -except ImportError: - # assume we're a package, use traditional import - from . import plugin_base - - -import os -import sys - -import nose -from nose.plugins import Plugin - - -fixtures = None - -py3k = sys.version_info >= (3, 0) - - -class NoseSQLAlchemy(Plugin): - enabled = True - - name = "sqla_testing" - score = 100 - - def options(self, parser, env=os.environ): - Plugin.options(self, parser, env) - opt = parser.add_option - - def make_option(name, **kw): - callback_ = kw.pop("callback", None) or kw.pop( - "zeroarg_callback", None - ) - if callback_: - - def wrap_(option, opt_str, value, parser): - callback_(opt_str, value, parser) - - kw["callback"] = wrap_ - opt(name, **kw) - - plugin_base.setup_options(make_option) - plugin_base.read_config() - - def configure(self, options, conf): - super(NoseSQLAlchemy, self).configure(options, conf) - plugin_base.pre_begin(options) - - plugin_base.set_coverage_flag(options.enable_plugin_coverage) - - plugin_base.set_skip_test(nose.SkipTest) - - def begin(self): - global fixtures - from sqlalchemy.testing import fixtures # noqa - - plugin_base.post_begin() - - def describeTest(self, test): - return "" - - def wantFunction(self, fn): - return False - - def wantMethod(self, fn): - if py3k: - if not hasattr(fn.__self__, "cls"): - return False - cls = fn.__self__.cls - else: - cls = fn.im_class - return plugin_base.want_method(cls, fn) - - def wantClass(self, cls): - return plugin_base.want_class(cls) - - def beforeTest(self, test): - if not hasattr(test.test, "cls"): - return - plugin_base.before_test( - test, - test.test.cls.__module__, - test.test.cls, - test.test.method.__name__, - ) - - def afterTest(self, test): - plugin_base.after_test(test) - - def startContext(self, ctx): - if not isinstance(ctx, type) or not issubclass(ctx, fixtures.TestBase): - return - plugin_base.start_test_class(ctx) - - def stopContext(self, ctx): - if not isinstance(ctx, type) or not issubclass(ctx, fixtures.TestBase): - return - plugin_base.stop_test_class(ctx) diff --git a/lib/sqlalchemy/testing/plugin/plugin_base.py b/lib/sqlalchemy/testing/plugin/plugin_base.py index 9c9e311428d..ef44a5906bc 100644 --- a/lib/sqlalchemy/testing/plugin/plugin_base.py +++ b/lib/sqlalchemy/testing/plugin/plugin_base.py @@ -8,8 +8,9 @@ """Testing extensions. this module is designed to work as a testing-framework-agnostic library, -so that we can continue to support nose and also begin adding new -functionality via py.test. +created so that multiple test frameworks can be supported at once +(mostly so that we can migrate to new ones). The current target +is py.test. """ @@ -244,8 +245,7 @@ def post_begin(): for fn in post_configure: fn(options, file_config) - # late imports, has to happen after config as well - # as nose plugins like coverage + # late imports, has to happen after config. global util, fixtures, engines, exclusions, assertions global warnings, profiling, config, testing from sqlalchemy import testing # noqa @@ -575,7 +575,7 @@ def _setup_engine(cls): def before_test(test, test_module_name, test_class, test_name): - # like a nose id, e.g.: + # format looks like: # "test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause" name = getattr(test_class, "_sa_orig_cls_name", test_class.__name__) diff --git a/lib/sqlalchemy/testing/runner.py b/lib/sqlalchemy/testing/runner.py deleted file mode 100644 index ac5ff40c4bc..00000000000 --- a/lib/sqlalchemy/testing/runner.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python -# testing/runner.py -# Copyright (C) 2005-2019 the SQLAlchemy authors and contributors -# -# -# This module is part of SQLAlchemy and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php -""" -Nose test runner module. - -This script is a front-end to "nosetests" which -installs SQLAlchemy's testing plugin into the local environment. - -The script is intended to be used by third-party dialects and extensions -that run within SQLAlchemy's testing framework. The runner can -be invoked via:: - - python -m sqlalchemy.testing.runner - -The script is then essentially the same as the "nosetests" script, including -all of the usual Nose options. The test environment requires that a -setup.cfg is locally present including various required options. - -Note that when using this runner, Nose's "coverage" plugin will not be -able to provide coverage for SQLAlchemy itself, since SQLAlchemy is -imported into sys.modules before coverage is started. The special -script sqla_nose.py is provided as a top-level script which loads the -plugin in a special (somewhat hacky) way so that coverage against -SQLAlchemy itself is possible. - -""" - -import nose - -from .plugin.noseplugin import NoseSQLAlchemy - - -def main(): - nose.main(addplugins=[NoseSQLAlchemy()]) - - -def setup_py_test(): - """Runner to use for the 'test_suite' entry of your setup.py. - - Prevents any name clash shenanigans from the command line - argument "test" that the "setup.py test" command sends - to nose. - - """ - nose.main(addplugins=[NoseSQLAlchemy()], argv=["runner"]) diff --git a/setup.cfg b/setup.cfg index 6d75087b858..580f3b357b3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,12 +4,6 @@ tag_build = dev [metadata] license_file = LICENSE -[nosetests] -with-sqla_testing = true -exclude = ^examples -first-package-wins = true -where = test - [tool:pytest] addopts= --tb native -v -r sfxX --maxfail=25 -p no:warnings -p no:logging python_files=test/*test_*.py diff --git a/sqla_nose.py b/sqla_nose.py deleted file mode 100755 index f5d548ad29e..00000000000 --- a/sqla_nose.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python -""" -nose runner script. - -This script is a front-end to "nosetests" which -installs SQLAlchemy's testing plugin into the local environment. - -""" -import os -import sys - -import nose - - -if not sys.flags.no_user_site: - sys.path.insert( - 0, - os.path.join(os.path.dirname(os.path.abspath(__file__)), 'lib') - ) - - -# use bootstrapping so that test plugins are loaded -# without touching the main library before coverage starts -bootstrap_file = os.path.join( - os.path.dirname(__file__), "lib", "sqlalchemy", - "testing", "plugin", "bootstrap.py" -) - -with open(bootstrap_file) as f: - code = compile(f.read(), "bootstrap.py", 'exec') - to_bootstrap = "nose" - exec(code, globals(), locals()) - - -from noseplugin import NoseSQLAlchemy # noqa -nose.main(addplugins=[NoseSQLAlchemy()])