summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2014-09-14 21:41:13 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2014-09-14 21:41:13 -0400
commitd7498cf4adb8ef9f35d1efa6dc747ac9eb489e60 (patch)
treede69b7ec88fef69c36704d2a7b06b1a0ffe0f17b
parent7fd3f05805a7cf1ff1fa29ffcf375c4eea6627ba (diff)
downloadsqlalchemy-d7498cf4adb8ef9f35d1efa6dc747ac9eb489e60.tar.gz
- remove some crufty old testing options
- reestablish the "bootstrap" system of loading the test runners in testing/plugin; using the updated approach we just came up with for alembic. Coverage should be fixed now when running either py.test or nose. fixes #3196 - upgrade tox.ini and start using a .coveragerc file
-rw-r--r--.coveragerc5
-rw-r--r--.gitignore2
-rw-r--r--lib/sqlalchemy/testing/plugin/bootstrap.py44
-rw-r--r--lib/sqlalchemy/testing/plugin/noseplugin.py32
-rw-r--r--lib/sqlalchemy/testing/plugin/plugin_base.py56
-rw-r--r--lib/sqlalchemy/testing/plugin/pytestplugin.py14
-rw-r--r--lib/sqlalchemy/testing/provision.py (renamed from lib/sqlalchemy/testing/plugin/provision.py)6
-rw-r--r--lib/sqlalchemy/testing/runner.py2
-rwxr-xr-xsqla_nose.py29
-rwxr-xr-xtest/conftest.py20
-rw-r--r--tox.ini24
11 files changed, 137 insertions, 97 deletions
diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 000000000..5d6c2bdc4
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,5 @@
+[run]
+include=lib/sqlalchemy/*
+
+[report]
+omit=lib/sqlalchemy/testing/* \ No newline at end of file
diff --git a/.gitignore b/.gitignore
index c22e53c39..55066f843 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,10 +6,12 @@
/doc/build/output/
/dogpile_data/
*.orig
+*,cover
/.tox
.venv
*.egg-info
.coverage
+coverage.xml
.*,cover
*.class
*.so
diff --git a/lib/sqlalchemy/testing/plugin/bootstrap.py b/lib/sqlalchemy/testing/plugin/bootstrap.py
new file mode 100644
index 000000000..497fcb7e5
--- /dev/null
+++ b/lib/sqlalchemy/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["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
index 6ef539142..eb4a3b258 100644
--- a/lib/sqlalchemy/testing/plugin/noseplugin.py
+++ b/lib/sqlalchemy/testing/plugin/noseplugin.py
@@ -12,6 +12,14 @@ 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
@@ -19,16 +27,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):
@@ -58,10 +56,10 @@ class NoseSQLAlchemy(Plugin):
plugin_base.set_coverage_flag(options.enable_plugin_coverage)
+ def begin(self):
global fixtures
- from sqlalchemy.testing import fixtures
+ from sqlalchemy.testing import fixtures # noqa
- def begin(self):
plugin_base.post_begin()
def describeTest(self, test):
@@ -72,19 +70,21 @@ class NoseSQLAlchemy(Plugin):
def wantMethod(self, fn):
if py3k:
+ if not hasattr(fn.__self__, 'cls'):
+ return False
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):
return plugin_base.want_class(cls)
def beforeTest(self, test):
- plugin_base.before_test(test,
- test.test.cls.__module__,
- test.test.cls, test.test.method.__name__)
+ 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)
diff --git a/lib/sqlalchemy/testing/plugin/plugin_base.py b/lib/sqlalchemy/testing/plugin/plugin_base.py
index 7ba31d3e3..6696427dc 100644
--- a/lib/sqlalchemy/testing/plugin/plugin_base.py
+++ b/lib/sqlalchemy/testing/plugin/plugin_base.py
@@ -31,8 +31,6 @@ if py3k:
else:
import ConfigParser as configparser
-FOLLOWER_IDENT = None
-
# late imports
fixtures = None
engines = None
@@ -72,8 +70,6 @@ def setup_options(make_option):
help="Drop all tables in the target database first")
make_option("--backend-only", action="store_true", dest="backend_only",
help="Run only tests marked with __backend__")
- make_option("--mockpool", action="store_true", dest="mockpool",
- help="Use mock pool (asserts only one connection used)")
make_option("--low-connections", action="store_true",
dest="low_connections",
help="Use a low number of distinct connections - "
@@ -95,14 +91,6 @@ def setup_options(make_option):
make_option("--exclude-tag", action="callback", callback=_exclude_tag,
type="string",
help="Exclude tests with tag <tag>")
- make_option("--serverside", action="store_true",
- help="Turn on server side cursors for PG")
- make_option("--mysql-engine", action="store",
- dest="mysql_engine", default=None,
- help="Use the specified MySQL storage engine for all tables, "
- "default is a db-default/InnoDB combo.")
- make_option("--tableopts", action="append", dest="tableopts", default=[],
- help="Add a dialect-specific table option, key=value")
make_option("--write-profiles", action="store_true",
dest="write_profiles", default=False,
help="Write/update profiling data.")
@@ -115,8 +103,8 @@ def configure_follower(follower_ident):
database creation.
"""
- global FOLLOWER_IDENT
- FOLLOWER_IDENT = follower_ident
+ from sqlalchemy.testing import provision
+ provision.FOLLOWER_IDENT = follower_ident
def memoize_important_follower_config(dict_):
@@ -177,12 +165,14 @@ def post_begin():
global util, fixtures, engines, exclusions, \
assertions, warnings, profiling,\
config, testing
- from sqlalchemy import testing
- from sqlalchemy.testing import fixtures, engines, exclusions, \
- assertions, warnings, profiling, config
- from sqlalchemy import util
+ from sqlalchemy import testing # noqa
+ from sqlalchemy.testing import fixtures, engines, exclusions # noqa
+ from sqlalchemy.testing import assertions, warnings, profiling # noqa
+ from sqlalchemy.testing import config # noqa
+ from sqlalchemy import util # noqa
warnings.setup_filters()
+
def _log(opt_str, value, parser):
global logging
if not logging:
@@ -234,12 +224,6 @@ def _setup_options(opt, file_config):
@pre
-def _server_side_cursors(options, file_config):
- if options.serverside:
- db_opts['server_side_cursors'] = True
-
-
-@pre
def _monkeypatch_cdecimal(options, file_config):
if options.cdecimal:
import cdecimal
@@ -250,7 +234,7 @@ def _monkeypatch_cdecimal(options, file_config):
def _engine_uri(options, file_config):
from sqlalchemy.testing import config
from sqlalchemy import testing
- from sqlalchemy.testing.plugin import provision
+ from sqlalchemy.testing import provision
if options.dburi:
db_urls = list(options.dburi)
@@ -273,20 +257,13 @@ 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, testing)
@post
-def _engine_pool(options, file_config):
- if options.mockpool:
- from sqlalchemy import pool
- db_opts['poolclass'] = pool.AssertionPool
-
-
-@post
def _requirements(options, file_config):
requirement_cls = file_config.get('sqla_testing', "requirement_cls")
@@ -369,19 +346,6 @@ def _prep_testing_database(options, file_config):
@post
-def _set_table_options(options, file_config):
- from sqlalchemy.testing import schema
-
- table_options = schema.table_options
- for spec in options.tableopts:
- key, value = spec.split('=')
- table_options[key] = value
-
- if options.mysql_engine:
- table_options['mysql_engine'] = options.mysql_engine
-
-
-@post
def _reverse_topological(options, file_config):
if options.reversetop:
from sqlalchemy.orm.util import randomize_unitofwork
diff --git a/lib/sqlalchemy/testing/plugin/pytestplugin.py b/lib/sqlalchemy/testing/plugin/pytestplugin.py
index 005942913..4bbc8ed9a 100644
--- a/lib/sqlalchemy/testing/plugin/pytestplugin.py
+++ b/lib/sqlalchemy/testing/plugin/pytestplugin.py
@@ -1,7 +1,13 @@
+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 pytest
import argparse
import inspect
-from . import plugin_base
import collections
import itertools
@@ -42,6 +48,8 @@ def pytest_configure(config):
plugin_base.set_coverage_flag(bool(getattr(config.option,
"cov_source", False)))
+
+def pytest_sessionstart(session):
plugin_base.post_begin()
if has_xdist:
@@ -54,11 +62,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 sqlalchemy.testing import provision
provision.create_follower_db(node.slaveinput["follower_ident"])
def pytest_testnodedown(node, error):
- from . import provision
+ from sqlalchemy.testing import provision
provision.drop_follower_db(node.slaveinput["follower_ident"])
diff --git a/lib/sqlalchemy/testing/plugin/provision.py b/lib/sqlalchemy/testing/provision.py
index c6b9030f5..0bcdad959 100644
--- a/lib/sqlalchemy/testing/plugin/provision.py
+++ b/lib/sqlalchemy/testing/provision.py
@@ -1,8 +1,10 @@
from sqlalchemy.engine import url as sa_url
from sqlalchemy import text
from sqlalchemy.util import compat
-from .. import config, engines
-import os
+from . import config, engines
+
+
+FOLLOWER_IDENT = None
class register(object):
diff --git a/lib/sqlalchemy/testing/runner.py b/lib/sqlalchemy/testing/runner.py
index df254520b..23d7a0a91 100644
--- a/lib/sqlalchemy/testing/runner.py
+++ b/lib/sqlalchemy/testing/runner.py
@@ -30,7 +30,7 @@ SQLAlchemy itself is possible.
"""
-from sqlalchemy.testing.plugin.noseplugin import NoseSQLAlchemy
+from .plugin.noseplugin import NoseSQLAlchemy
import nose
diff --git a/sqla_nose.py b/sqla_nose.py
index f89a1dce0..b977f4bf5 100755
--- a/sqla_nose.py
+++ b/sqla_nose.py
@@ -8,22 +8,25 @@ installs SQLAlchemy's testing plugin into the local environment.
"""
import sys
import nose
-import warnings
+import os
-from os import path
for pth in ['./lib']:
- sys.path.insert(0, path.join(path.dirname(path.abspath(__file__)), pth))
+ sys.path.insert(
+ 0, os.path.join(os.path.dirname(os.path.abspath(__file__)), pth))
-# installing without importing SQLAlchemy, so that coverage includes
-# SQLAlchemy itself.
-path = "lib/sqlalchemy/testing/plugin/noseplugin.py"
-if sys.version_info >= (3, 3):
- from importlib import machinery
- noseplugin = machinery.SourceFileLoader("noseplugin", path).load_module()
-else:
- import imp
- noseplugin = imp.load_source("noseplugin", path)
+# 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())
-nose.main(addplugins=[noseplugin.NoseSQLAlchemy()])
+
+from noseplugin import NoseSQLAlchemy
+nose.main(addplugins=[NoseSQLAlchemy()])
diff --git a/test/conftest.py b/test/conftest.py
index 1dd442309..c697085ee 100755
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -7,9 +7,23 @@ 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))
+ sys.path.insert(
+ 0,
+ os.path.join(os.path.dirname(os.path.abspath(__file__)), pth))
-from sqlalchemy.testing.plugin.pytestplugin import *
+
+# 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 = "pytest"
+ exec(code, globals(), locals())
+ from pytestplugin import * # noqa
diff --git a/tox.ini b/tox.ini
index 304bfd632..46ac122be 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,18 +1,15 @@
[tox]
-envlist = full
+envlist = full,py26,py27,py33,py34
[testenv]
deps=pytest
- flake8
- coverage
mock
sitepackages=True
usedevelop=True
commands=
- python -m pytest {posargs}
-envdir=pytest
+ python -m pytest -n4 {posargs}
[testenv:full]
@@ -21,21 +18,22 @@ envdir=pytest
setenv=
DISABLE_SQLALCHEMY_CEXT=1
+# see also .coveragerc
+deps=coverage
commands=
- python -m pytest \
- --cov=lib/sqlalchemy \
- --exclude-tag memory-intensive \
- --exclude-tag timing-intensive \
- -k "not aaa_profiling" \
- {posargs}
- python -m coverage xml --include=lib/sqlalchemy/*
+ python -m pytest --cov=sqlalchemy --cov-report term --cov-report xml \
+ --exclude-tag memory-intensive \
+ --exclude-tag timing-intensive \
+ -k "not aaa_profiling" \
+ {posargs}
+
[testenv:pep8]
commands = python -m flake8 {posargs}
[flake8]
-
+deps=flake8
show-source = True
ignore = E711,E712,E721,F841,F811
exclude=.venv,.git,.tox,dist,doc,*egg,build