diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..1b33a56 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,5 @@ +[run] +include=alembic/* + +[report] +omit=alembic/testing/* \ No newline at end of file diff --git a/alembic/ddl/__init__.py b/alembic/ddl/__init__.py index bfc8ab4..d225c45 100644 --- a/alembic/ddl/__init__.py +++ b/alembic/ddl/__init__.py @@ -1,2 +1,2 @@ -from . import postgresql, mysql, sqlite, mssql, oracle -from .impl import DefaultImpl +from . import postgresql, mysql, sqlite, mssql, oracle # pragma: no cover +from .impl import DefaultImpl # pragma: no cover diff --git a/alembic/testing/config.py b/alembic/testing/config.py index 98006f2..474fde4 100644 --- a/alembic/testing/config.py +++ b/alembic/testing/config.py @@ -5,7 +5,7 @@ # This module is part of SQLAlchemy and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php """NOTE: copied/adapted from SQLAlchemy master for backwards compatibility; - this should be removable when Alembic targets SQLAlchemy 0.9.4. + this should be removable when Alembic targets SQLAlchemy 1.0.0 """ import collections diff --git a/alembic/testing/engines.py b/alembic/testing/engines.py index 22d04f2..35aa8b5 100644 --- a/alembic/testing/engines.py +++ b/alembic/testing/engines.py @@ -5,7 +5,7 @@ # This module is part of SQLAlchemy and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php """NOTE: copied/adapted from SQLAlchemy master for backwards compatibility; - this should be removable when Alembic targets SQLAlchemy 0.9.4. + this should be removable when Alembic targets SQLAlchemy 1.0.0. """ from __future__ import absolute_import diff --git a/alembic/testing/env.py b/alembic/testing/env.py index bf04378..6c449a5 100644 --- a/alembic/testing/env.py +++ b/alembic/testing/env.py @@ -8,12 +8,12 @@ from alembic.compat import u from alembic.script import Script, ScriptDirectory from alembic import util from . import engines -from alembic.testing.plugin import plugin_base +from . import provision def _get_staging_directory(): - if plugin_base.FOLLOWER_IDENT: - return "scratch_%s" % plugin_base.FOLLOWER_IDENT + if provision.FOLLOWER_IDENT: + return "scratch_%s" % provision.FOLLOWER_IDENT else: return 'scratch' diff --git a/alembic/testing/exclusions.py b/alembic/testing/exclusions.py index 1b572e5..a64fe35 100644 --- a/alembic/testing/exclusions.py +++ b/alembic/testing/exclusions.py @@ -5,7 +5,7 @@ # This module is part of SQLAlchemy and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php """NOTE: copied/adapted from SQLAlchemy master for backwards compatibility; - this should be removable when Alembic targets SQLAlchemy 0.9.4. + this should be removable when Alembic targets SQLAlchemy 1.0.0 """ diff --git a/alembic/testing/mock.py b/alembic/testing/mock.py index 8d0c051..cdfcb88 100644 --- a/alembic/testing/mock.py +++ b/alembic/testing/mock.py @@ -8,7 +8,7 @@ """Import stub for mock library. NOTE: copied/adapted from SQLAlchemy master for backwards compatibility; - this should be removable when Alembic targets SQLAlchemy 0.9.4. + this should be removable when Alembic targets SQLAlchemy 1.0.0 """ from __future__ import absolute_import @@ -18,7 +18,7 @@ if py33: from unittest.mock import MagicMock, Mock, call, patch else: try: - from mock import MagicMock, Mock, call, patch + from mock import MagicMock, Mock, call, patch # noqa except ImportError: raise ImportError( "SQLAlchemy's test suite requires the " diff --git a/alembic/testing/plugin/__init__.py b/alembic/testing/plugin/__init__.py index 3e00e40..98616f4 100644 --- a/alembic/testing/plugin/__init__.py +++ b/alembic/testing/plugin/__init__.py @@ -1,3 +1,3 @@ """NOTE: copied/adapted from SQLAlchemy master for backwards compatibility; - this should be removable when Alembic targets SQLAlchemy 0.9.4. + this should be removable when Alembic targets SQLAlchemy 1.0.0 """ diff --git a/alembic/testing/plugin/bootstrap.py b/alembic/testing/plugin/bootstrap.py new file mode 100644 index 0000000..1560b03 --- /dev/null +++ b/alembic/testing/plugin/bootstrap.py @@ -0,0 +1,44 @@ +""" +Bootstrapper for nose/pytest 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 +set up things for testing before coverage starts. + +The rationale for all of plugin/ being *in* the supporting library in the +first place is so that the testing and plugin suite is available to other +libraries, mainly external SQLAlchemy and Alembic dialects, to make use +of the same test environment and standard suites available to +SQLAlchemy/Alembic themselves without the need to ship/install a separate +package outside of SQLAlchemy. + +NOTE: copied/adapted from SQLAlchemy master for backwards compatibility; +this should be removable when Alembic targets SQLAlchemy 1.0.0. + +""" + +import os +import sys + +bootstrap_file = locals()['bootstrap_file'] +to_bootstrap = locals()['to_bootstrap'] + + +def load_file_as_module(name): + path = os.path.join(os.path.dirname(bootstrap_file), "%s.py" % name) + if sys.version_info >= (3, 3): + from importlib import machinery + mod = machinery.SourceFileLoader(name, path).load_module() + else: + import imp + mod = imp.load_source(name, path) + return mod + +if to_bootstrap == "pytest": + sys.modules["alembic_plugin_base"] = load_file_as_module("plugin_base") + sys.modules["alembic_pytestplugin"] = load_file_as_module("pytestplugin") +elif to_bootstrap == "nose": + sys.modules["alembic_plugin_base"] = load_file_as_module("plugin_base") + sys.modules["alembic_noseplugin"] = load_file_as_module("noseplugin") +else: + raise Exception("unknown bootstrap: %s" % to_bootstrap) # noqa diff --git a/alembic/testing/plugin/noseplugin.py b/alembic/testing/plugin/noseplugin.py index ec3eebe..3235d4f 100644 --- a/alembic/testing/plugin/noseplugin.py +++ b/alembic/testing/plugin/noseplugin.py @@ -5,14 +5,21 @@ # This module is part of SQLAlchemy and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php -"""NOTE: copied/adapted from SQLAlchemy master for backwards compatibility; - this should be removable when Alembic targets SQLAlchemy 0.9.4. +""" +Enhance nose with extra options and behaviors for running SQLAlchemy tests. + + +NOTE: copied/adapted from SQLAlchemy master for backwards compatibility; +this should be removable when Alembic targets SQLAlchemy 1.0.0. + """ -"""Enhance nose with extra options and behaviors for running SQLAlchemy tests. - - -""" +try: + # installed by bootstrap.py + import alembic_plugin_base as plugin_base +except ImportError: + # assume we're a package, use traditional import + from . import plugin_base import os import sys @@ -21,16 +28,6 @@ from nose.plugins import Plugin fixtures = None py3k = sys.version_info >= (3, 0) -# no package imports yet! this prevents us from tripping coverage -# too soon. -path = os.path.join(os.path.dirname(__file__), "plugin_base.py") -if sys.version_info >= (3, 3): - from importlib import machinery - plugin_base = machinery.SourceFileLoader( - "plugin_base", path).load_module() -else: - import imp - plugin_base = imp.load_source("plugin_base", path) class NoseSQLAlchemy(Plugin): @@ -60,10 +57,10 @@ class NoseSQLAlchemy(Plugin): plugin_base.set_coverage_flag(options.enable_plugin_coverage) + def begin(self): global fixtures from alembic.testing import fixtures # noqa - def begin(self): plugin_base.post_begin() def describeTest(self, test): @@ -77,7 +74,6 @@ class NoseSQLAlchemy(Plugin): cls = fn.__self__.cls else: cls = fn.im_class - print "METH:", fn, "CLS:", cls return plugin_base.want_method(cls, fn) def wantClass(self, cls): diff --git a/alembic/testing/plugin/plugin_base.py b/alembic/testing/plugin/plugin_base.py index c4d1f83..c4c600d 100644 --- a/alembic/testing/plugin/plugin_base.py +++ b/alembic/testing/plugin/plugin_base.py @@ -11,7 +11,7 @@ so that we can continue to support nose and also begin adding new functionality via py.test. NOTE: copied/adapted from SQLAlchemy master for backwards compatibility; - this should be removable when Alembic targets SQLAlchemy 0.9.4. +this should be removable when Alembic targets SQLAlchemy 1.0.0 """ @@ -34,11 +34,10 @@ if py3k: else: import ConfigParser as configparser -FOLLOWER_IDENT = None - # late imports fixtures = None engines = None +provision = None exclusions = None warnings = None assertions = None @@ -111,8 +110,8 @@ def configure_follower(follower_ident): database creation. """ - global FOLLOWER_IDENT - FOLLOWER_IDENT = follower_ident + from alembic.testing import provision + provision.FOLLOWER_IDENT = follower_ident def memoize_important_follower_config(dict_): @@ -164,6 +163,7 @@ def set_coverage_flag(value): def post_begin(): """things to set up later, once we know coverage is running.""" + # Lazy setup of other options (post coverage) for fn in post_configure: fn(options, file_config) @@ -245,7 +245,7 @@ def _monkeypatch_cdecimal(options, file_config): @post def _engine_uri(options, file_config): from alembic.testing import config - from alembic.testing.plugin import provision + from alembic.testing import provision if options.dburi: db_urls = list(options.dburi) @@ -268,7 +268,7 @@ def _engine_uri(options, file_config): for db_url in db_urls: cfg = provision.setup_config( - db_url, db_opts, options, file_config, FOLLOWER_IDENT) + db_url, db_opts, options, file_config, provision.FOLLOWER_IDENT) if not config._current: cfg.set_as_current(cfg) diff --git a/alembic/testing/plugin/pytestplugin.py b/alembic/testing/plugin/pytestplugin.py index 26f188f..78194a3 100644 --- a/alembic/testing/plugin/pytestplugin.py +++ b/alembic/testing/plugin/pytestplugin.py @@ -1,10 +1,21 @@ """NOTE: copied/adapted from SQLAlchemy master for backwards compatibility; - this should be removable when Alembic targets SQLAlchemy 0.9.4. + this should be removable when Alembic targets SQLAlchemy 1.0.0. """ + +try: + # installed by bootstrap.py + import alembic_plugin_base as plugin_base +except ImportError: + # assume we're a package, use traditional import + from . import plugin_base + +import sys + +py3k = sys.version_info >= (3, 0) + import pytest import argparse import inspect -from . import plugin_base import collections import itertools @@ -42,9 +53,11 @@ def pytest_configure(config): plugin_base.pre_begin(config.option) - plugin_base.set_coverage_flag(bool(getattr(config.option, - "cov_source", False))) + coverage = bool(getattr(config.option, "cov_source", False)) + plugin_base.set_coverage_flag(coverage) + +def pytest_sessionstart(session): plugin_base.post_begin() if has_xdist: @@ -57,11 +70,11 @@ if has_xdist: plugin_base.memoize_important_follower_config(node.slaveinput) node.slaveinput["follower_ident"] = "test_%s" % next(_follower_count) - from . import provision + from alembic.testing import provision provision.create_follower_db(node.slaveinput["follower_ident"]) def pytest_testnodedown(node, error): - from . import provision + from alembic.testing import provision provision.drop_follower_db(node.slaveinput["follower_ident"]) @@ -121,6 +134,7 @@ def pytest_pycollect_makeitem(collector, name, obj): _current_class = None + def pytest_runtest_setup(item): # here we seem to get called only based on what we collected # in pytest_collection_modifyitems. So to do class-based stuff diff --git a/alembic/testing/plugin/provision.py b/alembic/testing/provision.py similarity index 98% rename from alembic/testing/plugin/provision.py rename to alembic/testing/provision.py index d0edbef..801d36b 100644 --- a/alembic/testing/plugin/provision.py +++ b/alembic/testing/provision.py @@ -1,5 +1,5 @@ """NOTE: copied/adapted from SQLAlchemy master for backwards compatibility; - this should be removable when Alembic targets SQLAlchemy 0.9.4. + this should be removable when Alembic targets SQLAlchemy 1.0.0 """ from sqlalchemy.engine import url as sa_url from sqlalchemy import text @@ -7,6 +7,8 @@ from alembic import compat from alembic.testing import config, engines from alembic.testing.compat import get_url_backend_name +FOLLOWER_IDENT = None + class register(object): def __init__(self): diff --git a/alembic/testing/runner.py b/alembic/testing/runner.py index 2810b88..2ad9387 100644 --- a/alembic/testing/runner.py +++ b/alembic/testing/runner.py @@ -29,9 +29,7 @@ plugin in a special (somewhat hacky) way so that coverage against SQLAlchemy itself is possible. """ - -from alembic.testing.plugin.noseplugin import NoseSQLAlchemy - +from .plugin.noseplugin import NoseSQLAlchemy import nose diff --git a/run_tests.py b/run_tests.py index cab0502..71770fc 100755 --- a/run_tests.py +++ b/run_tests.py @@ -1,3 +1,17 @@ -from alembic.testing import runner +import os +# 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__), "alembic", + "testing", "plugin", "bootstrap.py" +) -runner.main() +with open(bootstrap_file) as f: + code = compile(f.read(), "bootstrap.py", 'exec') + to_bootstrap = "nose" + exec(code, globals(), locals()) + + +from noseplugin import NoseSQLAlchemy +import nose +nose.main(addplugins=[NoseSQLAlchemy()]) diff --git a/tests/conftest.py b/tests/conftest.py index bdb361a..608d903 100755 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,10 +6,17 @@ This script is an extension to py.test which installs SQLAlchemy's testing plugin into the local environment. """ -import sys +import os -from os import path -for pth in ['../lib']: - sys.path.insert(0, path.join(path.dirname(path.abspath(__file__)), pth)) +# 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__), "..", "alembic", + "testing", "plugin", "bootstrap.py" +) -from alembic.testing.plugin.pytestplugin import * +with open(bootstrap_file) as f: + code = compile(f.read(), "bootstrap.py", 'exec') + to_bootstrap = "pytest" + exec(code, globals(), locals()) + from pytestplugin import * # noqa diff --git a/tox.ini b/tox.ini index 00cd075..c27041a 100644 --- a/tox.ini +++ b/tox.ini @@ -32,10 +32,10 @@ recreate=True recreate=True [testenv:coverage] +# see also .coveragerc deps=coverage commands= - python -m pytest --cov=alembic {posargs} - python -m coverage xml --include=alembic/* + python -m pytest --cov=alembic --cov-report term --cov-report xml {posargs} [testenv:pep8] deps=flake8