- coverage was not working (and needs to be fixed in SQLAlchemy too),

go back to using a "bootstrap" system where we load in pytestplugin/
noseplugin via file importing, plugin/ is not used as a package.
- identify the pytest_sessionstart hook as the best place to do
plugin_base.post_begin() and actually begin importing main modules;
this is where coverage has actually started.
- we're now targeting 1.0.0 as this has to be ported to SQLAlchemy.
- start using .coveragerc
This commit is contained in:
Mike Bayer 2014-09-14 15:49:04 -04:00
parent 58149cf4d0
commit adc081312e
17 changed files with 135 additions and 55 deletions

5
.coveragerc Normal file
View File

@ -0,0 +1,5 @@
[run]
include=alembic/*
[report]
omit=alembic/testing/*

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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
"""

View File

@ -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 "

View File

@ -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
"""

View File

@ -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

View File

@ -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):

View File

@ -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)

View File

@ -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

View File

@ -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):

View File

@ -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

View File

@ -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()])

View File

@ -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

View File

@ -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