Skip to content

Commit c3bdc09

Browse files
committed
ENH: add pd.test to enable nose test runnning from the imported session, #4327
1 parent ab1eb2a commit c3bdc09

File tree

5 files changed

+230
-0
lines changed

5 files changed

+230
-0
lines changed

Diff for: doc/source/api.rst

+8
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,14 @@ Top-level evaluation
194194

195195
eval
196196

197+
Testing
198+
~~~~~~~
199+
200+
.. autosummary::
201+
:toctree: generated/
202+
203+
test
204+
197205
.. _api.series:
198206

199207
Series

Diff for: doc/source/contributing.rst

+9
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,15 @@ entire suite. This is done using one of the following constructs::
509509
nosetests pandas/tests/[test-module].py:[TestClass]
510510
nosetests pandas/tests/[test-module].py:[TestClass].[test_method]
511511

512+
.. versionadded:: 0.18.0
513+
514+
Furthermore one can run
515+
516+
.. code-block:: python
517+
518+
pd.test()
519+
520+
with an imported pandas to run tests similarly.
512521

513522
Running the performance test suite
514523
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Diff for: doc/source/whatsnew/v0.18.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ users upgrade to this version.
1414
Highlights include:
1515

1616
- Window functions are now methods on ``.groupby`` like objects, see :ref:`here <whatsnew_0180.enhancements.moments>`.
17+
- ``pd.test()`` top-level nose test runner is available (:issue:`4327`)
1718

1819
Check the :ref:`API Changes <whatsnew_0180.api>` and :ref:`deprecations <whatsnew_0180.deprecations>` before updating.
1920

Diff for: pandas/__init__.py

+5
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,12 @@
5555
from pandas.tools.util import to_numeric
5656
from pandas.core.reshape import melt
5757
from pandas.util.print_versions import show_versions
58+
59+
# define the testing framework
5860
import pandas.util.testing
61+
from pandas.util.nosetester import NoseTester
62+
test = NoseTester().test
63+
del NoseTester
5964

6065
# use the closest tagged version if possible
6166
from ._version import get_versions

Diff for: pandas/util/nosetester.py

+207
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
"""
2+
Nose test running.
3+
4+
This module implements ``test()`` function for pandas modules.
5+
6+
"""
7+
from __future__ import division, absolute_import, print_function
8+
9+
import os
10+
import sys
11+
import warnings
12+
from pandas.compat import string_types
13+
from numpy.testing import nosetester
14+
15+
16+
def get_package_name(filepath):
17+
"""
18+
Given a path where a package is installed, determine its name.
19+
20+
Parameters
21+
----------
22+
filepath : str
23+
Path to a file. If the determination fails, "pandas" is returned.
24+
25+
Examples
26+
--------
27+
>>> pandas.util.nosetester.get_package_name('nonsense')
28+
'pandas'
29+
30+
"""
31+
32+
pkg_name = []
33+
while 'site-packages' in filepath or 'dist-packages' in filepath:
34+
filepath, p2 = os.path.split(filepath)
35+
if p2 in ('site-packages', 'dist-packages'):
36+
break
37+
pkg_name.append(p2)
38+
39+
# if package name determination failed, just default to pandas
40+
if not pkg_name:
41+
return "pandas"
42+
43+
# otherwise, reverse to get correct order and return
44+
pkg_name.reverse()
45+
46+
# don't include the outer egg directory
47+
if pkg_name[0].endswith('.egg'):
48+
pkg_name.pop(0)
49+
50+
return '.'.join(pkg_name)
51+
52+
import_nose = nosetester.import_nose
53+
run_module_suite = nosetester.run_module_suite
54+
55+
56+
class NoseTester(nosetester.NoseTester):
57+
"""
58+
Nose test runner.
59+
60+
This class is made available as pandas.util.nosetester.NoseTester, and
61+
a test function is typically added to a package's __init__.py like so::
62+
63+
from numpy.testing import Tester
64+
test = Tester().test
65+
66+
Calling this test function finds and runs all tests associated with the
67+
package and all its sub-packages.
68+
69+
Attributes
70+
----------
71+
package_path : str
72+
Full path to the package to test.
73+
package_name : str
74+
Name of the package to test.
75+
76+
Parameters
77+
----------
78+
package : module, str or None, optional
79+
The package to test. If a string, this should be the full path to
80+
the package. If None (default), `package` is set to the module from
81+
which `NoseTester` is initialized.
82+
raise_warnings : None, str or sequence of warnings, optional
83+
This specifies which warnings to configure as 'raise' instead
84+
of 'warn' during the test execution. Valid strings are:
85+
86+
- "develop" : equals ``(DeprecationWarning, RuntimeWarning)``
87+
- "release" : equals ``()``, don't raise on any warnings.
88+
89+
See Notes for more details.
90+
91+
Notes
92+
-----
93+
The default for `raise_warnings` is
94+
``(DeprecationWarning, RuntimeWarning)`` for development versions of
95+
pandas, and ``()`` for released versions. The purpose of this switching
96+
behavior is to catch as many warnings as possible during development, but
97+
not give problems for packaging of released versions.
98+
99+
"""
100+
excludes = []
101+
102+
def _show_system_info(self):
103+
nose = import_nose()
104+
105+
import pandas
106+
print("pandas version %s" % pandas.__version__)
107+
import numpy
108+
print("numpy version %s" % numpy.__version__)
109+
pddir = os.path.dirname(pandas.__file__)
110+
print("pandas is installed in %s" % pddir)
111+
112+
pyversion = sys.version.replace('\n', '')
113+
print("Python version %s" % pyversion)
114+
print("nose version %d.%d.%d" % nose.__versioninfo__)
115+
116+
def _get_custom_doctester(self):
117+
""" Return instantiated plugin for doctests
118+
119+
Allows subclassing of this class to override doctester
120+
121+
A return value of None means use the nose builtin doctest plugin
122+
"""
123+
return None
124+
125+
def test(self, label='fast', verbose=1, extra_argv=None,
126+
doctests=False, coverage=False, raise_warnings=None):
127+
"""
128+
Run tests for module using nose.
129+
130+
Parameters
131+
----------
132+
label : {'fast', 'full', '', attribute identifier}, optional
133+
Identifies the tests to run. This can be a string to pass to
134+
the nosetests executable with the '-A' option, or one of several
135+
special values. Special values are:
136+
* 'fast' - the default - which corresponds to the ``nosetests -A``
137+
option of 'not slow'.
138+
* 'full' - fast (as above) and slow tests as in the
139+
'no -A' option to nosetests - this is the same as ''.
140+
* None or '' - run all tests.
141+
attribute_identifier - string passed directly to nosetests as '-A'.
142+
verbose : int, optional
143+
Verbosity value for test outputs, in the range 1-10. Default is 1.
144+
extra_argv : list, optional
145+
List with any extra arguments to pass to nosetests.
146+
doctests : bool, optional
147+
If True, run doctests in module. Default is False.
148+
coverage : bool, optional
149+
If True, report coverage of NumPy code. Default is False.
150+
(This requires the `coverage module:
151+
<http://nedbatchelder.com/code/modules/coverage.html>`_).
152+
raise_warnings : str or sequence of warnings, optional
153+
This specifies which warnings to configure as 'raise' instead
154+
of 'warn' during the test execution. Valid strings are:
155+
156+
- "develop" : equals ``(DeprecationWarning, RuntimeWarning)``
157+
- "release" : equals ``()``, don't raise on any warnings.
158+
159+
Returns
160+
-------
161+
result : object
162+
Returns the result of running the tests as a
163+
``nose.result.TextTestResult`` object.
164+
"""
165+
166+
# cap verbosity at 3 because nose becomes *very* verbose beyond that
167+
verbose = min(verbose, 3)
168+
169+
if doctests:
170+
print("Running unit tests and doctests for %s" % self.package_name)
171+
else:
172+
print("Running unit tests for %s" % self.package_name)
173+
174+
self._show_system_info()
175+
176+
# reset doctest state on every run
177+
import doctest
178+
doctest.master = None
179+
180+
if raise_warnings is None:
181+
raise_warnings = 'release'
182+
183+
_warn_opts = dict(develop=(DeprecationWarning, RuntimeWarning),
184+
release=())
185+
if isinstance(raise_warnings, string_types):
186+
raise_warnings = _warn_opts[raise_warnings]
187+
188+
with warnings.catch_warnings():
189+
# Reset the warning filters to the default state,
190+
# so that running the tests is more repeatable.
191+
warnings.resetwarnings()
192+
# Set all warnings to 'warn', this is because the default 'once'
193+
# has the bad property of possibly shadowing later warnings.
194+
warnings.filterwarnings('always')
195+
# Force the requested warnings to raise
196+
for warningtype in raise_warnings:
197+
warnings.filterwarnings('error', category=warningtype)
198+
# Filter out annoying import messages.
199+
warnings.filterwarnings("ignore", category=FutureWarning)
200+
201+
from numpy.testing.noseclasses import NumpyTestProgram
202+
203+
argv, plugins = self.prepare_test_args(
204+
label, verbose, extra_argv, doctests, coverage)
205+
t = NumpyTestProgram(argv=argv, exit=False, plugins=plugins)
206+
207+
return t.result

0 commit comments

Comments
 (0)