Skip to content

Commit d7498cf

Browse files
committed
- 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 sqlalchemy#3196 - upgrade tox.ini and start using a .coveragerc file
1 parent 7fd3f05 commit d7498cf

File tree

11 files changed

+137
-97
lines changed

11 files changed

+137
-97
lines changed

.coveragerc

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[run]
2+
include=lib/sqlalchemy/*
3+
4+
[report]
5+
omit=lib/sqlalchemy/testing/*

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
/doc/build/output/
77
/dogpile_data/
88
*.orig
9+
*,cover
910
/.tox
1011
.venv
1112
*.egg-info
1213
.coverage
14+
coverage.xml
1315
.*,cover
1416
*.class
1517
*.so
+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""
2+
Bootstrapper for nose/pytest plugins.
3+
4+
The entire rationale for this system is to get the modules in plugin/
5+
imported without importing all of the supporting library, so that we can
6+
set up things for testing before coverage starts.
7+
8+
The rationale for all of plugin/ being *in* the supporting library in the
9+
first place is so that the testing and plugin suite is available to other
10+
libraries, mainly external SQLAlchemy and Alembic dialects, to make use
11+
of the same test environment and standard suites available to
12+
SQLAlchemy/Alembic themselves without the need to ship/install a separate
13+
package outside of SQLAlchemy.
14+
15+
NOTE: copied/adapted from SQLAlchemy master for backwards compatibility;
16+
this should be removable when Alembic targets SQLAlchemy 1.0.0.
17+
18+
"""
19+
20+
import os
21+
import sys
22+
23+
bootstrap_file = locals()['bootstrap_file']
24+
to_bootstrap = locals()['to_bootstrap']
25+
26+
27+
def load_file_as_module(name):
28+
path = os.path.join(os.path.dirname(bootstrap_file), "%s.py" % name)
29+
if sys.version_info >= (3, 3):
30+
from importlib import machinery
31+
mod = machinery.SourceFileLoader(name, path).load_module()
32+
else:
33+
import imp
34+
mod = imp.load_source(name, path)
35+
return mod
36+
37+
if to_bootstrap == "pytest":
38+
sys.modules["sqla_plugin_base"] = load_file_as_module("plugin_base")
39+
sys.modules["sqla_pytestplugin"] = load_file_as_module("pytestplugin")
40+
elif to_bootstrap == "nose":
41+
sys.modules["sqla_plugin_base"] = load_file_as_module("plugin_base")
42+
sys.modules["sqla_noseplugin"] = load_file_as_module("noseplugin")
43+
else:
44+
raise Exception("unknown bootstrap: %s" % to_bootstrap) # noqa

lib/sqlalchemy/testing/plugin/noseplugin.py

+16-16
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,21 @@
1212
1313
"""
1414

15+
try:
16+
# installed by bootstrap.py
17+
import sqla_plugin_base as plugin_base
18+
except ImportError:
19+
# assume we're a package, use traditional import
20+
from . import plugin_base
21+
22+
1523
import os
1624
import sys
1725

1826
from nose.plugins import Plugin
1927
fixtures = None
2028

2129
py3k = sys.version_info >= (3, 0)
22-
# no package imports yet! this prevents us from tripping coverage
23-
# too soon.
24-
path = os.path.join(os.path.dirname(__file__), "plugin_base.py")
25-
if sys.version_info >= (3, 3):
26-
from importlib import machinery
27-
plugin_base = machinery.SourceFileLoader(
28-
"plugin_base", path).load_module()
29-
else:
30-
import imp
31-
plugin_base = imp.load_source("plugin_base", path)
3230

3331

3432
class NoseSQLAlchemy(Plugin):
@@ -58,10 +56,10 @@ def configure(self, options, conf):
5856

5957
plugin_base.set_coverage_flag(options.enable_plugin_coverage)
6058

59+
def begin(self):
6160
global fixtures
62-
from sqlalchemy.testing import fixtures
61+
from sqlalchemy.testing import fixtures # noqa
6362

64-
def begin(self):
6563
plugin_base.post_begin()
6664

6765
def describeTest(self, test):
@@ -72,19 +70,21 @@ def wantFunction(self, fn):
7270

7371
def wantMethod(self, fn):
7472
if py3k:
73+
if not hasattr(fn.__self__, 'cls'):
74+
return False
7575
cls = fn.__self__.cls
7676
else:
7777
cls = fn.im_class
78-
print "METH:", fn, "CLS:", cls
7978
return plugin_base.want_method(cls, fn)
8079

8180
def wantClass(self, cls):
8281
return plugin_base.want_class(cls)
8382

8483
def beforeTest(self, test):
85-
plugin_base.before_test(test,
86-
test.test.cls.__module__,
87-
test.test.cls, test.test.method.__name__)
84+
plugin_base.before_test(
85+
test,
86+
test.test.cls.__module__,
87+
test.test.cls, test.test.method.__name__)
8888

8989
def afterTest(self, test):
9090
plugin_base.after_test(test)

lib/sqlalchemy/testing/plugin/plugin_base.py

+10-46
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@
3131
else:
3232
import ConfigParser as configparser
3333

34-
FOLLOWER_IDENT = None
35-
3634
# late imports
3735
fixtures = None
3836
engines = None
@@ -72,8 +70,6 @@ def setup_options(make_option):
7270
help="Drop all tables in the target database first")
7371
make_option("--backend-only", action="store_true", dest="backend_only",
7472
help="Run only tests marked with __backend__")
75-
make_option("--mockpool", action="store_true", dest="mockpool",
76-
help="Use mock pool (asserts only one connection used)")
7773
make_option("--low-connections", action="store_true",
7874
dest="low_connections",
7975
help="Use a low number of distinct connections - "
@@ -95,14 +91,6 @@ def setup_options(make_option):
9591
make_option("--exclude-tag", action="callback", callback=_exclude_tag,
9692
type="string",
9793
help="Exclude tests with tag <tag>")
98-
make_option("--serverside", action="store_true",
99-
help="Turn on server side cursors for PG")
100-
make_option("--mysql-engine", action="store",
101-
dest="mysql_engine", default=None,
102-
help="Use the specified MySQL storage engine for all tables, "
103-
"default is a db-default/InnoDB combo.")
104-
make_option("--tableopts", action="append", dest="tableopts", default=[],
105-
help="Add a dialect-specific table option, key=value")
10694
make_option("--write-profiles", action="store_true",
10795
dest="write_profiles", default=False,
10896
help="Write/update profiling data.")
@@ -115,8 +103,8 @@ def configure_follower(follower_ident):
115103
database creation.
116104
117105
"""
118-
global FOLLOWER_IDENT
119-
FOLLOWER_IDENT = follower_ident
106+
from sqlalchemy.testing import provision
107+
provision.FOLLOWER_IDENT = follower_ident
120108

121109

122110
def memoize_important_follower_config(dict_):
@@ -177,12 +165,14 @@ def post_begin():
177165
global util, fixtures, engines, exclusions, \
178166
assertions, warnings, profiling,\
179167
config, testing
180-
from sqlalchemy import testing
181-
from sqlalchemy.testing import fixtures, engines, exclusions, \
182-
assertions, warnings, profiling, config
183-
from sqlalchemy import util
168+
from sqlalchemy import testing # noqa
169+
from sqlalchemy.testing import fixtures, engines, exclusions # noqa
170+
from sqlalchemy.testing import assertions, warnings, profiling # noqa
171+
from sqlalchemy.testing import config # noqa
172+
from sqlalchemy import util # noqa
184173
warnings.setup_filters()
185174

175+
186176
def _log(opt_str, value, parser):
187177
global logging
188178
if not logging:
@@ -233,12 +223,6 @@ def _setup_options(opt, file_config):
233223
options = opt
234224

235225

236-
@pre
237-
def _server_side_cursors(options, file_config):
238-
if options.serverside:
239-
db_opts['server_side_cursors'] = True
240-
241-
242226
@pre
243227
def _monkeypatch_cdecimal(options, file_config):
244228
if options.cdecimal:
@@ -250,7 +234,7 @@ def _monkeypatch_cdecimal(options, file_config):
250234
def _engine_uri(options, file_config):
251235
from sqlalchemy.testing import config
252236
from sqlalchemy import testing
253-
from sqlalchemy.testing.plugin import provision
237+
from sqlalchemy.testing import provision
254238

255239
if options.dburi:
256240
db_urls = list(options.dburi)
@@ -273,19 +257,12 @@ def _engine_uri(options, file_config):
273257

274258
for db_url in db_urls:
275259
cfg = provision.setup_config(
276-
db_url, db_opts, options, file_config, FOLLOWER_IDENT)
260+
db_url, db_opts, options, file_config, provision.FOLLOWER_IDENT)
277261

278262
if not config._current:
279263
cfg.set_as_current(cfg, testing)
280264

281265

282-
@post
283-
def _engine_pool(options, file_config):
284-
if options.mockpool:
285-
from sqlalchemy import pool
286-
db_opts['poolclass'] = pool.AssertionPool
287-
288-
289266
@post
290267
def _requirements(options, file_config):
291268

@@ -368,19 +345,6 @@ def _prep_testing_database(options, file_config):
368345
schema=enum['schema'])))
369346

370347

371-
@post
372-
def _set_table_options(options, file_config):
373-
from sqlalchemy.testing import schema
374-
375-
table_options = schema.table_options
376-
for spec in options.tableopts:
377-
key, value = spec.split('=')
378-
table_options[key] = value
379-
380-
if options.mysql_engine:
381-
table_options['mysql_engine'] = options.mysql_engine
382-
383-
384348
@post
385349
def _reverse_topological(options, file_config):
386350
if options.reversetop:

lib/sqlalchemy/testing/plugin/pytestplugin.py

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1+
try:
2+
# installed by bootstrap.py
3+
import sqla_plugin_base as plugin_base
4+
except ImportError:
5+
# assume we're a package, use traditional import
6+
from . import plugin_base
7+
18
import pytest
29
import argparse
310
import inspect
4-
from . import plugin_base
511
import collections
612
import itertools
713

@@ -42,6 +48,8 @@ def pytest_configure(config):
4248
plugin_base.set_coverage_flag(bool(getattr(config.option,
4349
"cov_source", False)))
4450

51+
52+
def pytest_sessionstart(session):
4553
plugin_base.post_begin()
4654

4755
if has_xdist:
@@ -54,11 +62,11 @@ def pytest_configure_node(node):
5462
plugin_base.memoize_important_follower_config(node.slaveinput)
5563

5664
node.slaveinput["follower_ident"] = "test_%s" % next(_follower_count)
57-
from . import provision
65+
from sqlalchemy.testing import provision
5866
provision.create_follower_db(node.slaveinput["follower_ident"])
5967

6068
def pytest_testnodedown(node, error):
61-
from . import provision
69+
from sqlalchemy.testing import provision
6270
provision.drop_follower_db(node.slaveinput["follower_ident"])
6371

6472

lib/sqlalchemy/testing/plugin/provision.py renamed to lib/sqlalchemy/testing/provision.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
from sqlalchemy.engine import url as sa_url
22
from sqlalchemy import text
33
from sqlalchemy.util import compat
4-
from .. import config, engines
5-
import os
4+
from . import config, engines
5+
6+
7+
FOLLOWER_IDENT = None
68

79

810
class register(object):

lib/sqlalchemy/testing/runner.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
3131
"""
3232

33-
from sqlalchemy.testing.plugin.noseplugin import NoseSQLAlchemy
33+
from .plugin.noseplugin import NoseSQLAlchemy
3434

3535
import nose
3636

sqla_nose.py

+16-13
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,25 @@
88
"""
99
import sys
1010
import nose
11-
import warnings
11+
import os
1212

1313

14-
from os import path
1514
for pth in ['./lib']:
16-
sys.path.insert(0, path.join(path.dirname(path.abspath(__file__)), pth))
15+
sys.path.insert(
16+
0, os.path.join(os.path.dirname(os.path.abspath(__file__)), pth))
1717

18-
# installing without importing SQLAlchemy, so that coverage includes
19-
# SQLAlchemy itself.
20-
path = "lib/sqlalchemy/testing/plugin/noseplugin.py"
21-
if sys.version_info >= (3, 3):
22-
from importlib import machinery
23-
noseplugin = machinery.SourceFileLoader("noseplugin", path).load_module()
24-
else:
25-
import imp
26-
noseplugin = imp.load_source("noseplugin", path)
18+
# use bootstrapping so that test plugins are loaded
19+
# without touching the main library before coverage starts
20+
bootstrap_file = os.path.join(
21+
os.path.dirname(__file__), "lib", "sqlalchemy",
22+
"testing", "plugin", "bootstrap.py"
23+
)
2724

25+
with open(bootstrap_file) as f:
26+
code = compile(f.read(), "bootstrap.py", 'exec')
27+
to_bootstrap = "nose"
28+
exec(code, globals(), locals())
2829

29-
nose.main(addplugins=[noseplugin.NoseSQLAlchemy()])
30+
31+
from noseplugin import NoseSQLAlchemy
32+
nose.main(addplugins=[NoseSQLAlchemy()])

test/conftest.py

+17-3
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,23 @@
77
88
"""
99
import sys
10+
import os
1011

11-
from os import path
1212
for pth in ['../lib']:
13-
sys.path.insert(0, path.join(path.dirname(path.abspath(__file__)), pth))
13+
sys.path.insert(
14+
0,
15+
os.path.join(os.path.dirname(os.path.abspath(__file__)), pth))
1416

15-
from sqlalchemy.testing.plugin.pytestplugin import *
17+
18+
# use bootstrapping so that test plugins are loaded
19+
# without touching the main library before coverage starts
20+
bootstrap_file = os.path.join(
21+
os.path.dirname(__file__), "..", "lib", "sqlalchemy",
22+
"testing", "plugin", "bootstrap.py"
23+
)
24+
25+
with open(bootstrap_file) as f:
26+
code = compile(f.read(), "bootstrap.py", 'exec')
27+
to_bootstrap = "pytest"
28+
exec(code, globals(), locals())
29+
from pytestplugin import * # noqa

0 commit comments

Comments
 (0)