Skip to content

ENH: add pd.test to enable nose test runnning from the imported session, #4327 #11913

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 8, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions doc/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,14 @@ Top-level evaluation

eval

Testing
~~~~~~~

.. autosummary::
:toctree: generated/

test

.. _api.series:

Series
Expand Down
9 changes: 9 additions & 0 deletions doc/source/contributing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,15 @@ entire suite. This is done using one of the following constructs::
nosetests pandas/tests/[test-module].py:[TestClass]
nosetests pandas/tests/[test-module].py:[TestClass].[test_method]

.. versionadded:: 0.18.0

Furthermore one can run

.. code-block:: python

pd.test()

with an imported pandas to run tests similarly.

Running the performance test suite
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.18.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ users upgrade to this version.
Highlights include:

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

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

Expand Down
5 changes: 5 additions & 0 deletions pandas/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,12 @@
from pandas.tools.util import to_numeric
from pandas.core.reshape import melt
from pandas.util.print_versions import show_versions

# define the testing framework
import pandas.util.testing
from pandas.util.nosetester import NoseTester
test = NoseTester().test
del NoseTester

# use the closest tagged version if possible
from ._version import get_versions
Expand Down
207 changes: 207 additions & 0 deletions pandas/util/nosetester.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
"""
Nose test running.

This module implements ``test()`` function for pandas modules.

"""
from __future__ import division, absolute_import, print_function

import os
import sys
import warnings
from pandas.compat import string_types
from numpy.testing import nosetester


def get_package_name(filepath):
"""
Given a path where a package is installed, determine its name.

Parameters
----------
filepath : str
Path to a file. If the determination fails, "pandas" is returned.

Examples
--------
>>> pandas.util.nosetester.get_package_name('nonsense')
'pandas'

"""

pkg_name = []
while 'site-packages' in filepath or 'dist-packages' in filepath:
filepath, p2 = os.path.split(filepath)
if p2 in ('site-packages', 'dist-packages'):
break
pkg_name.append(p2)

# if package name determination failed, just default to pandas
if not pkg_name:
return "pandas"

# otherwise, reverse to get correct order and return
pkg_name.reverse()

# don't include the outer egg directory
if pkg_name[0].endswith('.egg'):
pkg_name.pop(0)

return '.'.join(pkg_name)

import_nose = nosetester.import_nose
run_module_suite = nosetester.run_module_suite


class NoseTester(nosetester.NoseTester):
"""
Nose test runner.

This class is made available as pandas.util.nosetester.NoseTester, and
a test function is typically added to a package's __init__.py like so::

from numpy.testing import Tester
test = Tester().test

Calling this test function finds and runs all tests associated with the
package and all its sub-packages.

Attributes
----------
package_path : str
Full path to the package to test.
package_name : str
Name of the package to test.

Parameters
----------
package : module, str or None, optional
The package to test. If a string, this should be the full path to
the package. If None (default), `package` is set to the module from
which `NoseTester` is initialized.
raise_warnings : None, str or sequence of warnings, optional
This specifies which warnings to configure as 'raise' instead
of 'warn' during the test execution. Valid strings are:

- "develop" : equals ``(DeprecationWarning, RuntimeWarning)``
- "release" : equals ``()``, don't raise on any warnings.

See Notes for more details.

Notes
-----
The default for `raise_warnings` is
``(DeprecationWarning, RuntimeWarning)`` for development versions of
pandas, and ``()`` for released versions. The purpose of this switching
behavior is to catch as many warnings as possible during development, but
not give problems for packaging of released versions.

"""
excludes = []

def _show_system_info(self):
nose = import_nose()

import pandas
print("pandas version %s" % pandas.__version__)
import numpy
print("numpy version %s" % numpy.__version__)
pddir = os.path.dirname(pandas.__file__)
print("pandas is installed in %s" % pddir)

pyversion = sys.version.replace('\n', '')
print("Python version %s" % pyversion)
print("nose version %d.%d.%d" % nose.__versioninfo__)

def _get_custom_doctester(self):
""" Return instantiated plugin for doctests

Allows subclassing of this class to override doctester

A return value of None means use the nose builtin doctest plugin
"""
return None

def test(self, label='fast', verbose=1, extra_argv=None,
doctests=False, coverage=False, raise_warnings=None):
"""
Run tests for module using nose.

Parameters
----------
label : {'fast', 'full', '', attribute identifier}, optional
Identifies the tests to run. This can be a string to pass to
the nosetests executable with the '-A' option, or one of several
special values. Special values are:
* 'fast' - the default - which corresponds to the ``nosetests -A``
option of 'not slow'.
* 'full' - fast (as above) and slow tests as in the
'no -A' option to nosetests - this is the same as ''.
* None or '' - run all tests.
attribute_identifier - string passed directly to nosetests as '-A'.
verbose : int, optional
Verbosity value for test outputs, in the range 1-10. Default is 1.
extra_argv : list, optional
List with any extra arguments to pass to nosetests.
doctests : bool, optional
If True, run doctests in module. Default is False.
coverage : bool, optional
If True, report coverage of NumPy code. Default is False.
(This requires the `coverage module:
<http://nedbatchelder.com/code/modules/coverage.html>`_).
raise_warnings : str or sequence of warnings, optional
This specifies which warnings to configure as 'raise' instead
of 'warn' during the test execution. Valid strings are:

- "develop" : equals ``(DeprecationWarning, RuntimeWarning)``
- "release" : equals ``()``, don't raise on any warnings.

Returns
-------
result : object
Returns the result of running the tests as a
``nose.result.TextTestResult`` object.
"""

# cap verbosity at 3 because nose becomes *very* verbose beyond that
verbose = min(verbose, 3)

if doctests:
print("Running unit tests and doctests for %s" % self.package_name)
else:
print("Running unit tests for %s" % self.package_name)

self._show_system_info()

# reset doctest state on every run
import doctest
doctest.master = None

if raise_warnings is None:
raise_warnings = 'release'

_warn_opts = dict(develop=(DeprecationWarning, RuntimeWarning),
release=())
if isinstance(raise_warnings, string_types):
raise_warnings = _warn_opts[raise_warnings]

with warnings.catch_warnings():
# Reset the warning filters to the default state,
# so that running the tests is more repeatable.
warnings.resetwarnings()
# Set all warnings to 'warn', this is because the default 'once'
# has the bad property of possibly shadowing later warnings.
warnings.filterwarnings('always')
# Force the requested warnings to raise
for warningtype in raise_warnings:
warnings.filterwarnings('error', category=warningtype)
# Filter out annoying import messages.
warnings.filterwarnings("ignore", category=FutureWarning)

from numpy.testing.noseclasses import NumpyTestProgram

argv, plugins = self.prepare_test_args(
label, verbose, extra_argv, doctests, coverage)
t = NumpyTestProgram(argv=argv, exit=False, plugins=plugins)

return t.result