Uploaded Test files
This commit is contained in:
parent
f584ad9d97
commit
2e81cb7d99
16627 changed files with 2065359 additions and 102444 deletions
49
venv/Lib/site-packages/IPython/testing/__init__.py
Normal file
49
venv/Lib/site-packages/IPython/testing/__init__.py
Normal file
|
@ -0,0 +1,49 @@
|
|||
"""Testing support (tools to test IPython itself).
|
||||
"""
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (C) 2009-2011 The IPython Development Team
|
||||
#
|
||||
# Distributed under the terms of the BSD License. The full license is in
|
||||
# the file COPYING, distributed as part of this software.
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
import os
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Functions
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
# User-level entry point for testing
|
||||
def test(**kwargs):
|
||||
"""Run the entire IPython test suite.
|
||||
|
||||
Any of the options for run_iptestall() may be passed as keyword arguments.
|
||||
|
||||
For example::
|
||||
|
||||
IPython.test(testgroups=['lib', 'config', 'utils'], fast=2)
|
||||
|
||||
will run those three sections of the test suite, using two processes.
|
||||
"""
|
||||
|
||||
# Do the import internally, so that this function doesn't increase total
|
||||
# import time
|
||||
from .iptestcontroller import run_iptestall, default_options
|
||||
options = default_options()
|
||||
for name, val in kwargs.items():
|
||||
setattr(options, name, val)
|
||||
run_iptestall(options)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Constants
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
# We scale all timeouts via this factor, slow machines can increase it
|
||||
IPYTHON_TESTING_TIMEOUT_SCALE = float(os.getenv(
|
||||
'IPYTHON_TESTING_TIMEOUT_SCALE', 1))
|
||||
|
||||
# So nose doesn't try to run this as a test itself and we end up with an
|
||||
# infinite test loop
|
||||
test.__test__ = False
|
3
venv/Lib/site-packages/IPython/testing/__main__.py
Normal file
3
venv/Lib/site-packages/IPython/testing/__main__.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
if __name__ == '__main__':
|
||||
from IPython.testing import iptestcontroller
|
||||
iptestcontroller.main()
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
383
venv/Lib/site-packages/IPython/testing/decorators.py
Normal file
383
venv/Lib/site-packages/IPython/testing/decorators.py
Normal file
|
@ -0,0 +1,383 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Decorators for labeling test objects.
|
||||
|
||||
Decorators that merely return a modified version of the original function
|
||||
object are straightforward. Decorators that return a new function object need
|
||||
to use nose.tools.make_decorator(original_function)(decorator) in returning the
|
||||
decorator, in order to preserve metadata such as function name, setup and
|
||||
teardown functions and so on - see nose.tools for more information.
|
||||
|
||||
This module provides a set of useful decorators meant to be ready to use in
|
||||
your own tests. See the bottom of the file for the ready-made ones, and if you
|
||||
find yourself writing a new one that may be of generic use, add it here.
|
||||
|
||||
Included decorators:
|
||||
|
||||
|
||||
Lightweight testing that remains unittest-compatible.
|
||||
|
||||
- An @as_unittest decorator can be used to tag any normal parameter-less
|
||||
function as a unittest TestCase. Then, both nose and normal unittest will
|
||||
recognize it as such. This will make it easier to migrate away from Nose if
|
||||
we ever need/want to while maintaining very lightweight tests.
|
||||
|
||||
NOTE: This file contains IPython-specific decorators. Using the machinery in
|
||||
IPython.external.decorators, we import either numpy.testing.decorators if numpy is
|
||||
available, OR use equivalent code in IPython.external._decorators, which
|
||||
we've copied verbatim from numpy.
|
||||
|
||||
"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
import warnings
|
||||
from importlib import import_module
|
||||
|
||||
from decorator import decorator
|
||||
|
||||
# Expose the unittest-driven decorators
|
||||
from .ipunittest import ipdoctest, ipdocstring
|
||||
|
||||
# Grab the numpy-specific decorators which we keep in a file that we
|
||||
# occasionally update from upstream: decorators.py is a copy of
|
||||
# numpy.testing.decorators, we expose all of it here.
|
||||
from IPython.external.decorators import knownfailureif
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Classes and functions
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
# Simple example of the basic idea
|
||||
def as_unittest(func):
|
||||
"""Decorator to make a simple function into a normal test via unittest."""
|
||||
class Tester(unittest.TestCase):
|
||||
def test(self):
|
||||
func()
|
||||
|
||||
Tester.__name__ = func.__name__
|
||||
|
||||
return Tester
|
||||
|
||||
# Utility functions
|
||||
|
||||
def apply_wrapper(wrapper, func):
|
||||
"""Apply a wrapper to a function for decoration.
|
||||
|
||||
This mixes Michele Simionato's decorator tool with nose's make_decorator,
|
||||
to apply a wrapper in a decorator so that all nose attributes, as well as
|
||||
function signature and other properties, survive the decoration cleanly.
|
||||
This will ensure that wrapped functions can still be well introspected via
|
||||
IPython, for example.
|
||||
"""
|
||||
warnings.warn("The function `apply_wrapper` is deprecated since IPython 4.0",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
import nose.tools
|
||||
|
||||
return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
|
||||
|
||||
|
||||
def make_label_dec(label, ds=None):
|
||||
"""Factory function to create a decorator that applies one or more labels.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
label : string or sequence
|
||||
One or more labels that will be applied by the decorator to the functions
|
||||
it decorates. Labels are attributes of the decorated function with their
|
||||
value set to True.
|
||||
|
||||
ds : string
|
||||
An optional docstring for the resulting decorator. If not given, a
|
||||
default docstring is auto-generated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
A decorator.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
A simple labeling decorator:
|
||||
|
||||
>>> slow = make_label_dec('slow')
|
||||
>>> slow.__doc__
|
||||
"Labels a test as 'slow'."
|
||||
|
||||
And one that uses multiple labels and a custom docstring:
|
||||
|
||||
>>> rare = make_label_dec(['slow','hard'],
|
||||
... "Mix labels 'slow' and 'hard' for rare tests.")
|
||||
>>> rare.__doc__
|
||||
"Mix labels 'slow' and 'hard' for rare tests."
|
||||
|
||||
Now, let's test using this one:
|
||||
>>> @rare
|
||||
... def f(): pass
|
||||
...
|
||||
>>>
|
||||
>>> f.slow
|
||||
True
|
||||
>>> f.hard
|
||||
True
|
||||
"""
|
||||
|
||||
warnings.warn("The function `make_label_dec` is deprecated since IPython 4.0",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
if isinstance(label, str):
|
||||
labels = [label]
|
||||
else:
|
||||
labels = label
|
||||
|
||||
# Validate that the given label(s) are OK for use in setattr() by doing a
|
||||
# dry run on a dummy function.
|
||||
tmp = lambda : None
|
||||
for label in labels:
|
||||
setattr(tmp,label,True)
|
||||
|
||||
# This is the actual decorator we'll return
|
||||
def decor(f):
|
||||
for label in labels:
|
||||
setattr(f,label,True)
|
||||
return f
|
||||
|
||||
# Apply the user's docstring, or autogenerate a basic one
|
||||
if ds is None:
|
||||
ds = "Labels a test as %r." % label
|
||||
decor.__doc__ = ds
|
||||
|
||||
return decor
|
||||
|
||||
|
||||
# Inspired by numpy's skipif, but uses the full apply_wrapper utility to
|
||||
# preserve function metadata better and allows the skip condition to be a
|
||||
# callable.
|
||||
def skipif(skip_condition, msg=None):
|
||||
''' Make function raise SkipTest exception if skip_condition is true
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
skip_condition : bool or callable
|
||||
Flag to determine whether to skip test. If the condition is a
|
||||
callable, it is used at runtime to dynamically make the decision. This
|
||||
is useful for tests that may require costly imports, to delay the cost
|
||||
until the test suite is actually executed.
|
||||
msg : string
|
||||
Message to give on raising a SkipTest exception.
|
||||
|
||||
Returns
|
||||
-------
|
||||
decorator : function
|
||||
Decorator, which, when applied to a function, causes SkipTest
|
||||
to be raised when the skip_condition was True, and the function
|
||||
to be called normally otherwise.
|
||||
|
||||
Notes
|
||||
-----
|
||||
You will see from the code that we had to further decorate the
|
||||
decorator with the nose.tools.make_decorator function in order to
|
||||
transmit function name, and various other metadata.
|
||||
'''
|
||||
|
||||
def skip_decorator(f):
|
||||
# Local import to avoid a hard nose dependency and only incur the
|
||||
# import time overhead at actual test-time.
|
||||
import nose
|
||||
|
||||
# Allow for both boolean or callable skip conditions.
|
||||
if callable(skip_condition):
|
||||
skip_val = skip_condition
|
||||
else:
|
||||
skip_val = lambda : skip_condition
|
||||
|
||||
def get_msg(func,msg=None):
|
||||
"""Skip message with information about function being skipped."""
|
||||
if msg is None: out = 'Test skipped due to test condition.'
|
||||
else: out = msg
|
||||
return "Skipping test: %s. %s" % (func.__name__,out)
|
||||
|
||||
# We need to define *two* skippers because Python doesn't allow both
|
||||
# return with value and yield inside the same function.
|
||||
def skipper_func(*args, **kwargs):
|
||||
"""Skipper for normal test functions."""
|
||||
if skip_val():
|
||||
raise nose.SkipTest(get_msg(f,msg))
|
||||
else:
|
||||
return f(*args, **kwargs)
|
||||
|
||||
def skipper_gen(*args, **kwargs):
|
||||
"""Skipper for test generators."""
|
||||
if skip_val():
|
||||
raise nose.SkipTest(get_msg(f,msg))
|
||||
else:
|
||||
for x in f(*args, **kwargs):
|
||||
yield x
|
||||
|
||||
# Choose the right skipper to use when building the actual generator.
|
||||
if nose.util.isgenerator(f):
|
||||
skipper = skipper_gen
|
||||
else:
|
||||
skipper = skipper_func
|
||||
|
||||
return nose.tools.make_decorator(f)(skipper)
|
||||
|
||||
return skip_decorator
|
||||
|
||||
# A version with the condition set to true, common case just to attach a message
|
||||
# to a skip decorator
|
||||
def skip(msg=None):
|
||||
"""Decorator factory - mark a test function for skipping from test suite.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
msg : string
|
||||
Optional message to be added.
|
||||
|
||||
Returns
|
||||
-------
|
||||
decorator : function
|
||||
Decorator, which, when applied to a function, causes SkipTest
|
||||
to be raised, with the optional message added.
|
||||
"""
|
||||
if msg and not isinstance(msg, str):
|
||||
raise ValueError('invalid object passed to `@skip` decorator, did you '
|
||||
'meant `@skip()` with brackets ?')
|
||||
return skipif(True, msg)
|
||||
|
||||
|
||||
def onlyif(condition, msg):
|
||||
"""The reverse from skipif, see skipif for details."""
|
||||
|
||||
if callable(condition):
|
||||
skip_condition = lambda : not condition()
|
||||
else:
|
||||
skip_condition = lambda : not condition
|
||||
|
||||
return skipif(skip_condition, msg)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Utility functions for decorators
|
||||
def module_not_available(module):
|
||||
"""Can module be imported? Returns true if module does NOT import.
|
||||
|
||||
This is used to make a decorator to skip tests that require module to be
|
||||
available, but delay the 'import numpy' to test execution time.
|
||||
"""
|
||||
try:
|
||||
mod = import_module(module)
|
||||
mod_not_avail = False
|
||||
except ImportError:
|
||||
mod_not_avail = True
|
||||
|
||||
return mod_not_avail
|
||||
|
||||
|
||||
def decorated_dummy(dec, name):
|
||||
"""Return a dummy function decorated with dec, with the given name.
|
||||
|
||||
Examples
|
||||
--------
|
||||
import IPython.testing.decorators as dec
|
||||
setup = dec.decorated_dummy(dec.skip_if_no_x11, __name__)
|
||||
"""
|
||||
warnings.warn("The function `decorated_dummy` is deprecated since IPython 4.0",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
dummy = lambda: None
|
||||
dummy.__name__ = name
|
||||
return dec(dummy)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Decorators for public use
|
||||
|
||||
# Decorators to skip certain tests on specific platforms.
|
||||
skip_win32 = skipif(sys.platform == 'win32',
|
||||
"This test does not run under Windows")
|
||||
skip_linux = skipif(sys.platform.startswith('linux'),
|
||||
"This test does not run under Linux")
|
||||
skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
|
||||
|
||||
|
||||
# Decorators to skip tests if not on specific platforms.
|
||||
skip_if_not_win32 = skipif(sys.platform != 'win32',
|
||||
"This test only runs under Windows")
|
||||
skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
|
||||
"This test only runs under Linux")
|
||||
skip_if_not_osx = skipif(sys.platform != 'darwin',
|
||||
"This test only runs under OSX")
|
||||
|
||||
|
||||
_x11_skip_cond = (sys.platform not in ('darwin', 'win32') and
|
||||
os.environ.get('DISPLAY', '') == '')
|
||||
_x11_skip_msg = "Skipped under *nix when X11/XOrg not available"
|
||||
|
||||
skip_if_no_x11 = skipif(_x11_skip_cond, _x11_skip_msg)
|
||||
|
||||
|
||||
# Decorators to skip certain tests on specific platform/python combinations
|
||||
skip_win32_py38 = skipif(sys.version_info > (3,8) and os.name == 'nt')
|
||||
|
||||
|
||||
# not a decorator itself, returns a dummy function to be used as setup
|
||||
def skip_file_no_x11(name):
|
||||
warnings.warn("The function `skip_file_no_x11` is deprecated since IPython 4.0",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
return decorated_dummy(skip_if_no_x11, name) if _x11_skip_cond else None
|
||||
|
||||
# Other skip decorators
|
||||
|
||||
# generic skip without module
|
||||
skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod)
|
||||
|
||||
skipif_not_numpy = skip_without('numpy')
|
||||
|
||||
skipif_not_matplotlib = skip_without('matplotlib')
|
||||
|
||||
skipif_not_sympy = skip_without('sympy')
|
||||
|
||||
skip_known_failure = knownfailureif(True,'This test is known to fail')
|
||||
|
||||
# A null 'decorator', useful to make more readable code that needs to pick
|
||||
# between different decorators based on OS or other conditions
|
||||
null_deco = lambda f: f
|
||||
|
||||
# Some tests only run where we can use unicode paths. Note that we can't just
|
||||
# check os.path.supports_unicode_filenames, which is always False on Linux.
|
||||
try:
|
||||
f = tempfile.NamedTemporaryFile(prefix=u"tmp€")
|
||||
except UnicodeEncodeError:
|
||||
unicode_paths = False
|
||||
else:
|
||||
unicode_paths = True
|
||||
f.close()
|
||||
|
||||
onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable "
|
||||
"where we can use unicode in filenames."))
|
||||
|
||||
|
||||
def onlyif_cmds_exist(*commands):
|
||||
"""
|
||||
Decorator to skip test when at least one of `commands` is not found.
|
||||
"""
|
||||
for cmd in commands:
|
||||
if not shutil.which(cmd):
|
||||
return skip("This test runs only if command '{0}' "
|
||||
"is installed".format(cmd))
|
||||
return null_deco
|
||||
|
||||
def onlyif_any_cmd_exists(*commands):
|
||||
"""
|
||||
Decorator to skip test unless at least one of `commands` is found.
|
||||
"""
|
||||
warnings.warn("The function `onlyif_any_cmd_exists` is deprecated since IPython 4.0",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
for cmd in commands:
|
||||
if shutil.which(cmd):
|
||||
return null_deco
|
||||
return skip("This test runs only if one of the commands {0} "
|
||||
"is installed".format(commands))
|
137
venv/Lib/site-packages/IPython/testing/globalipapp.py
Normal file
137
venv/Lib/site-packages/IPython/testing/globalipapp.py
Normal file
|
@ -0,0 +1,137 @@
|
|||
"""Global IPython app to support test running.
|
||||
|
||||
We must start our own ipython object and heavily muck with it so that all the
|
||||
modifications IPython makes to system behavior don't send the doctest machinery
|
||||
into a fit. This code should be considered a gross hack, but it gets the job
|
||||
done.
|
||||
"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import builtins as builtin_mod
|
||||
import sys
|
||||
import types
|
||||
import warnings
|
||||
|
||||
from . import tools
|
||||
|
||||
from IPython.core import page
|
||||
from IPython.utils import io
|
||||
from IPython.terminal.interactiveshell import TerminalInteractiveShell
|
||||
|
||||
|
||||
class StreamProxy(io.IOStream):
|
||||
"""Proxy for sys.stdout/err. This will request the stream *at call time*
|
||||
allowing for nose's Capture plugin's redirection of sys.stdout/err.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
The name of the stream. This will be requested anew at every call
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
warnings.warn("StreamProxy is deprecated and unused as of IPython 5", DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
self.name=name
|
||||
|
||||
@property
|
||||
def stream(self):
|
||||
return getattr(sys, self.name)
|
||||
|
||||
def flush(self):
|
||||
self.stream.flush()
|
||||
|
||||
|
||||
def get_ipython():
|
||||
# This will get replaced by the real thing once we start IPython below
|
||||
return start_ipython()
|
||||
|
||||
|
||||
# A couple of methods to override those in the running IPython to interact
|
||||
# better with doctest (doctest captures on raw stdout, so we need to direct
|
||||
# various types of output there otherwise it will miss them).
|
||||
|
||||
def xsys(self, cmd):
|
||||
"""Replace the default system call with a capturing one for doctest.
|
||||
"""
|
||||
# We use getoutput, but we need to strip it because pexpect captures
|
||||
# the trailing newline differently from commands.getoutput
|
||||
print(self.getoutput(cmd, split=False, depth=1).rstrip(), end='', file=sys.stdout)
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
def _showtraceback(self, etype, evalue, stb):
|
||||
"""Print the traceback purely on stdout for doctest to capture it.
|
||||
"""
|
||||
print(self.InteractiveTB.stb2text(stb), file=sys.stdout)
|
||||
|
||||
|
||||
def start_ipython():
|
||||
"""Start a global IPython shell, which we need for IPython-specific syntax.
|
||||
"""
|
||||
global get_ipython
|
||||
|
||||
# This function should only ever run once!
|
||||
if hasattr(start_ipython, 'already_called'):
|
||||
return
|
||||
start_ipython.already_called = True
|
||||
|
||||
# Store certain global objects that IPython modifies
|
||||
_displayhook = sys.displayhook
|
||||
_excepthook = sys.excepthook
|
||||
_main = sys.modules.get('__main__')
|
||||
|
||||
# Create custom argv and namespaces for our IPython to be test-friendly
|
||||
config = tools.default_config()
|
||||
config.TerminalInteractiveShell.simple_prompt = True
|
||||
|
||||
# Create and initialize our test-friendly IPython instance.
|
||||
shell = TerminalInteractiveShell.instance(config=config,
|
||||
)
|
||||
|
||||
# A few more tweaks needed for playing nicely with doctests...
|
||||
|
||||
# remove history file
|
||||
shell.tempfiles.append(config.HistoryManager.hist_file)
|
||||
|
||||
# These traps are normally only active for interactive use, set them
|
||||
# permanently since we'll be mocking interactive sessions.
|
||||
shell.builtin_trap.activate()
|
||||
|
||||
# Modify the IPython system call with one that uses getoutput, so that we
|
||||
# can capture subcommands and print them to Python's stdout, otherwise the
|
||||
# doctest machinery would miss them.
|
||||
shell.system = types.MethodType(xsys, shell)
|
||||
|
||||
shell._showtraceback = types.MethodType(_showtraceback, shell)
|
||||
|
||||
# IPython is ready, now clean up some global state...
|
||||
|
||||
# Deactivate the various python system hooks added by ipython for
|
||||
# interactive convenience so we don't confuse the doctest system
|
||||
sys.modules['__main__'] = _main
|
||||
sys.displayhook = _displayhook
|
||||
sys.excepthook = _excepthook
|
||||
|
||||
# So that ipython magics and aliases can be doctested (they work by making
|
||||
# a call into a global _ip object). Also make the top-level get_ipython
|
||||
# now return this without recursively calling here again.
|
||||
_ip = shell
|
||||
get_ipython = _ip.get_ipython
|
||||
builtin_mod._ip = _ip
|
||||
builtin_mod.ip = _ip
|
||||
builtin_mod.get_ipython = get_ipython
|
||||
|
||||
# Override paging, so we don't require user interaction during the tests.
|
||||
def nopage(strng, start=0, screen_lines=0, pager_cmd=None):
|
||||
if isinstance(strng, dict):
|
||||
strng = strng.get('text/plain', '')
|
||||
print(strng)
|
||||
|
||||
page.orig_page = page.pager_page
|
||||
page.pager_page = nopage
|
||||
|
||||
return _ip
|
460
venv/Lib/site-packages/IPython/testing/iptest.py
Normal file
460
venv/Lib/site-packages/IPython/testing/iptest.py
Normal file
|
@ -0,0 +1,460 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""IPython Test Suite Runner.
|
||||
|
||||
This module provides a main entry point to a user script to test IPython
|
||||
itself from the command line. There are two ways of running this script:
|
||||
|
||||
1. With the syntax `iptest all`. This runs our entire test suite by
|
||||
calling this script (with different arguments) recursively. This
|
||||
causes modules and package to be tested in different processes, using nose
|
||||
or trial where appropriate.
|
||||
2. With the regular nose syntax, like `iptest IPython -- -vvs`. In this form
|
||||
the script simply calls nose, but with special command line flags and
|
||||
plugins loaded. Options after `--` are passed to nose.
|
||||
|
||||
"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
|
||||
import glob
|
||||
from io import BytesIO
|
||||
import os
|
||||
import os.path as path
|
||||
import sys
|
||||
from threading import Thread, Lock, Event
|
||||
import warnings
|
||||
|
||||
import nose.plugins.builtin
|
||||
from nose.plugins.xunit import Xunit
|
||||
from nose import SkipTest
|
||||
from nose.core import TestProgram
|
||||
from nose.plugins import Plugin
|
||||
from nose.util import safe_str
|
||||
|
||||
from IPython import version_info
|
||||
from IPython.utils.py3compat import decode
|
||||
from IPython.utils.importstring import import_item
|
||||
from IPython.testing.plugin.ipdoctest import IPythonDoctest
|
||||
from IPython.external.decorators import KnownFailure, knownfailureif
|
||||
|
||||
pjoin = path.join
|
||||
|
||||
|
||||
# Enable printing all warnings raise by IPython's modules
|
||||
warnings.filterwarnings('ignore', message='.*Matplotlib is building the font cache.*', category=UserWarning, module='.*')
|
||||
warnings.filterwarnings('error', message='.*', category=ResourceWarning, module='.*')
|
||||
warnings.filterwarnings('error', message=".*{'config': True}.*", category=DeprecationWarning, module='IPy.*')
|
||||
warnings.filterwarnings('default', message='.*', category=Warning, module='IPy.*')
|
||||
|
||||
warnings.filterwarnings('error', message='.*apply_wrapper.*', category=DeprecationWarning, module='.*')
|
||||
warnings.filterwarnings('error', message='.*make_label_dec', category=DeprecationWarning, module='.*')
|
||||
warnings.filterwarnings('error', message='.*decorated_dummy.*', category=DeprecationWarning, module='.*')
|
||||
warnings.filterwarnings('error', message='.*skip_file_no_x11.*', category=DeprecationWarning, module='.*')
|
||||
warnings.filterwarnings('error', message='.*onlyif_any_cmd_exists.*', category=DeprecationWarning, module='.*')
|
||||
|
||||
warnings.filterwarnings('error', message='.*disable_gui.*', category=DeprecationWarning, module='.*')
|
||||
|
||||
warnings.filterwarnings('error', message='.*ExceptionColors global is deprecated.*', category=DeprecationWarning, module='.*')
|
||||
|
||||
# Jedi older versions
|
||||
warnings.filterwarnings(
|
||||
'error', message='.*elementwise != comparison failed and.*', category=FutureWarning, module='.*')
|
||||
|
||||
if version_info < (6,):
|
||||
# nose.tools renames all things from `camelCase` to `snake_case` which raise an
|
||||
# warning with the runner they also import from standard import library. (as of Dec 2015)
|
||||
# Ignore, let's revisit that in a couple of years for IPython 6.
|
||||
warnings.filterwarnings(
|
||||
'ignore', message='.*Please use assertEqual instead', category=Warning, module='IPython.*')
|
||||
|
||||
if version_info < (8,):
|
||||
warnings.filterwarnings('ignore', message='.*Completer.complete.*',
|
||||
category=PendingDeprecationWarning, module='.*')
|
||||
else:
|
||||
warnings.warn(
|
||||
'Completer.complete was pending deprecation and should be changed to Deprecated', FutureWarning)
|
||||
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Monkeypatch Xunit to count known failures as skipped.
|
||||
# ------------------------------------------------------------------------------
|
||||
def monkeypatch_xunit():
|
||||
try:
|
||||
dec.knownfailureif(True)(lambda: None)()
|
||||
except Exception as e:
|
||||
KnownFailureTest = type(e)
|
||||
|
||||
def addError(self, test, err, capt=None):
|
||||
if issubclass(err[0], KnownFailureTest):
|
||||
err = (SkipTest,) + err[1:]
|
||||
return self.orig_addError(test, err, capt)
|
||||
|
||||
Xunit.orig_addError = Xunit.addError
|
||||
Xunit.addError = addError
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Check which dependencies are installed and greater than minimum version.
|
||||
#-----------------------------------------------------------------------------
|
||||
def extract_version(mod):
|
||||
return mod.__version__
|
||||
|
||||
def test_for(item, min_version=None, callback=extract_version):
|
||||
"""Test to see if item is importable, and optionally check against a minimum
|
||||
version.
|
||||
|
||||
If min_version is given, the default behavior is to check against the
|
||||
`__version__` attribute of the item, but specifying `callback` allows you to
|
||||
extract the value you are interested in. e.g::
|
||||
|
||||
In [1]: import sys
|
||||
|
||||
In [2]: from IPython.testing.iptest import test_for
|
||||
|
||||
In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
|
||||
Out[3]: True
|
||||
|
||||
"""
|
||||
try:
|
||||
check = import_item(item)
|
||||
except (ImportError, RuntimeError):
|
||||
# GTK reports Runtime error if it can't be initialized even if it's
|
||||
# importable.
|
||||
return False
|
||||
else:
|
||||
if min_version:
|
||||
if callback:
|
||||
# extra processing step to get version to compare
|
||||
check = callback(check)
|
||||
|
||||
return check >= min_version
|
||||
else:
|
||||
return True
|
||||
|
||||
# Global dict where we can store information on what we have and what we don't
|
||||
# have available at test run time
|
||||
have = {'matplotlib': test_for('matplotlib'),
|
||||
'pygments': test_for('pygments'),
|
||||
'sqlite3': test_for('sqlite3')}
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Test suite definitions
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
test_group_names = ['core',
|
||||
'extensions', 'lib', 'terminal', 'testing', 'utils',
|
||||
]
|
||||
|
||||
class TestSection(object):
|
||||
def __init__(self, name, includes):
|
||||
self.name = name
|
||||
self.includes = includes
|
||||
self.excludes = []
|
||||
self.dependencies = []
|
||||
self.enabled = True
|
||||
|
||||
def exclude(self, module):
|
||||
if not module.startswith('IPython'):
|
||||
module = self.includes[0] + "." + module
|
||||
self.excludes.append(module.replace('.', os.sep))
|
||||
|
||||
def requires(self, *packages):
|
||||
self.dependencies.extend(packages)
|
||||
|
||||
@property
|
||||
def will_run(self):
|
||||
return self.enabled and all(have[p] for p in self.dependencies)
|
||||
|
||||
# Name -> (include, exclude, dependencies_met)
|
||||
test_sections = {n:TestSection(n, ['IPython.%s' % n]) for n in test_group_names}
|
||||
|
||||
|
||||
# Exclusions and dependencies
|
||||
# ---------------------------
|
||||
|
||||
# core:
|
||||
sec = test_sections['core']
|
||||
if not have['sqlite3']:
|
||||
sec.exclude('tests.test_history')
|
||||
sec.exclude('history')
|
||||
if not have['matplotlib']:
|
||||
sec.exclude('pylabtools'),
|
||||
sec.exclude('tests.test_pylabtools')
|
||||
|
||||
# lib:
|
||||
sec = test_sections['lib']
|
||||
sec.exclude('kernel')
|
||||
if not have['pygments']:
|
||||
sec.exclude('tests.test_lexers')
|
||||
# We do this unconditionally, so that the test suite doesn't import
|
||||
# gtk, changing the default encoding and masking some unicode bugs.
|
||||
sec.exclude('inputhookgtk')
|
||||
# We also do this unconditionally, because wx can interfere with Unix signals.
|
||||
# There are currently no tests for it anyway.
|
||||
sec.exclude('inputhookwx')
|
||||
# Testing inputhook will need a lot of thought, to figure out
|
||||
# how to have tests that don't lock up with the gui event
|
||||
# loops in the picture
|
||||
sec.exclude('inputhook')
|
||||
|
||||
# testing:
|
||||
sec = test_sections['testing']
|
||||
# These have to be skipped on win32 because they use echo, rm, cd, etc.
|
||||
# See ticket https://github.com/ipython/ipython/issues/87
|
||||
if sys.platform == 'win32':
|
||||
sec.exclude('plugin.test_exampleip')
|
||||
sec.exclude('plugin.dtexample')
|
||||
|
||||
# don't run jupyter_console tests found via shim
|
||||
test_sections['terminal'].exclude('console')
|
||||
|
||||
# extensions:
|
||||
sec = test_sections['extensions']
|
||||
# This is deprecated in favour of rpy2
|
||||
sec.exclude('rmagic')
|
||||
# autoreload does some strange stuff, so move it to its own test section
|
||||
sec.exclude('autoreload')
|
||||
sec.exclude('tests.test_autoreload')
|
||||
test_sections['autoreload'] = TestSection('autoreload',
|
||||
['IPython.extensions.autoreload', 'IPython.extensions.tests.test_autoreload'])
|
||||
test_group_names.append('autoreload')
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Functions and classes
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
def check_exclusions_exist():
|
||||
from IPython.paths import get_ipython_package_dir
|
||||
from warnings import warn
|
||||
parent = os.path.dirname(get_ipython_package_dir())
|
||||
for sec in test_sections:
|
||||
for pattern in sec.exclusions:
|
||||
fullpath = pjoin(parent, pattern)
|
||||
if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
|
||||
warn("Excluding nonexistent file: %r" % pattern)
|
||||
|
||||
|
||||
class ExclusionPlugin(Plugin):
|
||||
"""A nose plugin to effect our exclusions of files and directories.
|
||||
"""
|
||||
name = 'exclusions'
|
||||
score = 3000 # Should come before any other plugins
|
||||
|
||||
def __init__(self, exclude_patterns=None):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
|
||||
exclude_patterns : sequence of strings, optional
|
||||
Filenames containing these patterns (as raw strings, not as regular
|
||||
expressions) are excluded from the tests.
|
||||
"""
|
||||
self.exclude_patterns = exclude_patterns or []
|
||||
super(ExclusionPlugin, self).__init__()
|
||||
|
||||
def options(self, parser, env=os.environ):
|
||||
Plugin.options(self, parser, env)
|
||||
|
||||
def configure(self, options, config):
|
||||
Plugin.configure(self, options, config)
|
||||
# Override nose trying to disable plugin.
|
||||
self.enabled = True
|
||||
|
||||
def wantFile(self, filename):
|
||||
"""Return whether the given filename should be scanned for tests.
|
||||
"""
|
||||
if any(pat in filename for pat in self.exclude_patterns):
|
||||
return False
|
||||
return None
|
||||
|
||||
def wantDirectory(self, directory):
|
||||
"""Return whether the given directory should be scanned for tests.
|
||||
"""
|
||||
if any(pat in directory for pat in self.exclude_patterns):
|
||||
return False
|
||||
return None
|
||||
|
||||
|
||||
class StreamCapturer(Thread):
|
||||
daemon = True # Don't hang if main thread crashes
|
||||
started = False
|
||||
def __init__(self, echo=False):
|
||||
super(StreamCapturer, self).__init__()
|
||||
self.echo = echo
|
||||
self.streams = []
|
||||
self.buffer = BytesIO()
|
||||
self.readfd, self.writefd = os.pipe()
|
||||
self.buffer_lock = Lock()
|
||||
self.stop = Event()
|
||||
|
||||
def run(self):
|
||||
self.started = True
|
||||
|
||||
while not self.stop.is_set():
|
||||
chunk = os.read(self.readfd, 1024)
|
||||
|
||||
with self.buffer_lock:
|
||||
self.buffer.write(chunk)
|
||||
if self.echo:
|
||||
sys.stdout.write(decode(chunk))
|
||||
|
||||
os.close(self.readfd)
|
||||
os.close(self.writefd)
|
||||
|
||||
def reset_buffer(self):
|
||||
with self.buffer_lock:
|
||||
self.buffer.truncate(0)
|
||||
self.buffer.seek(0)
|
||||
|
||||
def get_buffer(self):
|
||||
with self.buffer_lock:
|
||||
return self.buffer.getvalue()
|
||||
|
||||
def ensure_started(self):
|
||||
if not self.started:
|
||||
self.start()
|
||||
|
||||
def halt(self):
|
||||
"""Safely stop the thread."""
|
||||
if not self.started:
|
||||
return
|
||||
|
||||
self.stop.set()
|
||||
os.write(self.writefd, b'\0') # Ensure we're not locked in a read()
|
||||
self.join()
|
||||
|
||||
class SubprocessStreamCapturePlugin(Plugin):
|
||||
name='subprocstreams'
|
||||
def __init__(self):
|
||||
Plugin.__init__(self)
|
||||
self.stream_capturer = StreamCapturer()
|
||||
self.destination = os.environ.get('IPTEST_SUBPROC_STREAMS', 'capture')
|
||||
# This is ugly, but distant parts of the test machinery need to be able
|
||||
# to redirect streams, so we make the object globally accessible.
|
||||
nose.iptest_stdstreams_fileno = self.get_write_fileno
|
||||
|
||||
def get_write_fileno(self):
|
||||
if self.destination == 'capture':
|
||||
self.stream_capturer.ensure_started()
|
||||
return self.stream_capturer.writefd
|
||||
elif self.destination == 'discard':
|
||||
return os.open(os.devnull, os.O_WRONLY)
|
||||
else:
|
||||
return sys.__stdout__.fileno()
|
||||
|
||||
def configure(self, options, config):
|
||||
Plugin.configure(self, options, config)
|
||||
# Override nose trying to disable plugin.
|
||||
if self.destination == 'capture':
|
||||
self.enabled = True
|
||||
|
||||
def startTest(self, test):
|
||||
# Reset log capture
|
||||
self.stream_capturer.reset_buffer()
|
||||
|
||||
def formatFailure(self, test, err):
|
||||
# Show output
|
||||
ec, ev, tb = err
|
||||
captured = self.stream_capturer.get_buffer().decode('utf-8', 'replace')
|
||||
if captured.strip():
|
||||
ev = safe_str(ev)
|
||||
out = [ev, '>> begin captured subprocess output <<',
|
||||
captured,
|
||||
'>> end captured subprocess output <<']
|
||||
return ec, '\n'.join(out), tb
|
||||
|
||||
return err
|
||||
|
||||
formatError = formatFailure
|
||||
|
||||
def finalize(self, result):
|
||||
self.stream_capturer.halt()
|
||||
|
||||
|
||||
def run_iptest():
|
||||
"""Run the IPython test suite using nose.
|
||||
|
||||
This function is called when this script is **not** called with the form
|
||||
`iptest all`. It simply calls nose with appropriate command line flags
|
||||
and accepts all of the standard nose arguments.
|
||||
"""
|
||||
# Apply our monkeypatch to Xunit
|
||||
if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
|
||||
monkeypatch_xunit()
|
||||
|
||||
arg1 = sys.argv[1]
|
||||
if arg1.startswith('IPython/'):
|
||||
if arg1.endswith('.py'):
|
||||
arg1 = arg1[:-3]
|
||||
sys.argv[1] = arg1.replace('/', '.')
|
||||
|
||||
arg1 = sys.argv[1]
|
||||
if arg1 in test_sections:
|
||||
section = test_sections[arg1]
|
||||
sys.argv[1:2] = section.includes
|
||||
elif arg1.startswith('IPython.') and arg1[8:] in test_sections:
|
||||
section = test_sections[arg1[8:]]
|
||||
sys.argv[1:2] = section.includes
|
||||
else:
|
||||
section = TestSection(arg1, includes=[arg1])
|
||||
|
||||
|
||||
argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
|
||||
# We add --exe because of setuptools' imbecility (it
|
||||
# blindly does chmod +x on ALL files). Nose does the
|
||||
# right thing and it tries to avoid executables,
|
||||
# setuptools unfortunately forces our hand here. This
|
||||
# has been discussed on the distutils list and the
|
||||
# setuptools devs refuse to fix this problem!
|
||||
'--exe',
|
||||
]
|
||||
if '-a' not in argv and '-A' not in argv:
|
||||
argv = argv + ['-a', '!crash']
|
||||
|
||||
if nose.__version__ >= '0.11':
|
||||
# I don't fully understand why we need this one, but depending on what
|
||||
# directory the test suite is run from, if we don't give it, 0 tests
|
||||
# get run. Specifically, if the test suite is run from the source dir
|
||||
# with an argument (like 'iptest.py IPython.core', 0 tests are run,
|
||||
# even if the same call done in this directory works fine). It appears
|
||||
# that if the requested package is in the current dir, nose bails early
|
||||
# by default. Since it's otherwise harmless, leave it in by default
|
||||
# for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
|
||||
argv.append('--traverse-namespace')
|
||||
|
||||
plugins = [ ExclusionPlugin(section.excludes), KnownFailure(),
|
||||
SubprocessStreamCapturePlugin() ]
|
||||
|
||||
# we still have some vestigial doctests in core
|
||||
if (section.name.startswith(('core', 'IPython.core', 'IPython.utils'))):
|
||||
plugins.append(IPythonDoctest())
|
||||
argv.extend([
|
||||
'--with-ipdoctest',
|
||||
'--ipdoctest-tests',
|
||||
'--ipdoctest-extension=txt',
|
||||
])
|
||||
|
||||
|
||||
# Use working directory set by parent process (see iptestcontroller)
|
||||
if 'IPTEST_WORKING_DIR' in os.environ:
|
||||
os.chdir(os.environ['IPTEST_WORKING_DIR'])
|
||||
|
||||
# We need a global ipython running in this process, but the special
|
||||
# in-process group spawns its own IPython kernels, so for *that* group we
|
||||
# must avoid also opening the global one (otherwise there's a conflict of
|
||||
# singletons). Ultimately the solution to this problem is to refactor our
|
||||
# assumptions about what needs to be a singleton and what doesn't (app
|
||||
# objects should, individual shells shouldn't). But for now, this
|
||||
# workaround allows the test suite for the inprocess module to complete.
|
||||
if 'kernel.inprocess' not in section.name:
|
||||
from IPython.testing import globalipapp
|
||||
globalipapp.start_ipython()
|
||||
|
||||
# Now nose can run
|
||||
TestProgram(argv=argv, addplugins=plugins)
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_iptest()
|
491
venv/Lib/site-packages/IPython/testing/iptestcontroller.py
Normal file
491
venv/Lib/site-packages/IPython/testing/iptestcontroller.py
Normal file
|
@ -0,0 +1,491 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""IPython Test Process Controller
|
||||
|
||||
This module runs one or more subprocesses which will actually run the IPython
|
||||
test suite.
|
||||
|
||||
"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
|
||||
import argparse
|
||||
import multiprocessing.pool
|
||||
import os
|
||||
import stat
|
||||
import shutil
|
||||
import signal
|
||||
import sys
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
from .iptest import (
|
||||
have, test_group_names as py_test_group_names, test_sections, StreamCapturer,
|
||||
)
|
||||
from IPython.utils.path import compress_user
|
||||
from IPython.utils.py3compat import decode
|
||||
from IPython.utils.sysinfo import get_sys_info
|
||||
from IPython.utils.tempdir import TemporaryDirectory
|
||||
|
||||
class TestController:
|
||||
"""Run tests in a subprocess
|
||||
"""
|
||||
#: str, IPython test suite to be executed.
|
||||
section = None
|
||||
#: list, command line arguments to be executed
|
||||
cmd = None
|
||||
#: dict, extra environment variables to set for the subprocess
|
||||
env = None
|
||||
#: list, TemporaryDirectory instances to clear up when the process finishes
|
||||
dirs = None
|
||||
#: subprocess.Popen instance
|
||||
process = None
|
||||
#: str, process stdout+stderr
|
||||
stdout = None
|
||||
|
||||
def __init__(self):
|
||||
self.cmd = []
|
||||
self.env = {}
|
||||
self.dirs = []
|
||||
|
||||
def setUp(self):
|
||||
"""Create temporary directories etc.
|
||||
|
||||
This is only called when we know the test group will be run. Things
|
||||
created here may be cleaned up by self.cleanup().
|
||||
"""
|
||||
pass
|
||||
|
||||
def launch(self, buffer_output=False, capture_output=False):
|
||||
# print('*** ENV:', self.env) # dbg
|
||||
# print('*** CMD:', self.cmd) # dbg
|
||||
env = os.environ.copy()
|
||||
env.update(self.env)
|
||||
if buffer_output:
|
||||
capture_output = True
|
||||
self.stdout_capturer = c = StreamCapturer(echo=not buffer_output)
|
||||
c.start()
|
||||
stdout = c.writefd if capture_output else None
|
||||
stderr = subprocess.STDOUT if capture_output else None
|
||||
self.process = subprocess.Popen(self.cmd, stdout=stdout,
|
||||
stderr=stderr, env=env)
|
||||
|
||||
def wait(self):
|
||||
self.process.wait()
|
||||
self.stdout_capturer.halt()
|
||||
self.stdout = self.stdout_capturer.get_buffer()
|
||||
return self.process.returncode
|
||||
|
||||
def cleanup_process(self):
|
||||
"""Cleanup on exit by killing any leftover processes."""
|
||||
subp = self.process
|
||||
if subp is None or (subp.poll() is not None):
|
||||
return # Process doesn't exist, or is already dead.
|
||||
|
||||
try:
|
||||
print('Cleaning up stale PID: %d' % subp.pid)
|
||||
subp.kill()
|
||||
except: # (OSError, WindowsError) ?
|
||||
# This is just a best effort, if we fail or the process was
|
||||
# really gone, ignore it.
|
||||
pass
|
||||
else:
|
||||
for i in range(10):
|
||||
if subp.poll() is None:
|
||||
time.sleep(0.1)
|
||||
else:
|
||||
break
|
||||
|
||||
if subp.poll() is None:
|
||||
# The process did not die...
|
||||
print('... failed. Manual cleanup may be required.')
|
||||
|
||||
def cleanup(self):
|
||||
"Kill process if it's still alive, and clean up temporary directories"
|
||||
self.cleanup_process()
|
||||
for td in self.dirs:
|
||||
td.cleanup()
|
||||
|
||||
__del__ = cleanup
|
||||
|
||||
|
||||
class PyTestController(TestController):
|
||||
"""Run Python tests using IPython.testing.iptest"""
|
||||
#: str, Python command to execute in subprocess
|
||||
pycmd = None
|
||||
|
||||
def __init__(self, section, options):
|
||||
"""Create new test runner."""
|
||||
TestController.__init__(self)
|
||||
self.section = section
|
||||
# pycmd is put into cmd[2] in PyTestController.launch()
|
||||
self.cmd = [sys.executable, '-c', None, section]
|
||||
self.pycmd = "from IPython.testing.iptest import run_iptest; run_iptest()"
|
||||
self.options = options
|
||||
|
||||
def setup(self):
|
||||
ipydir = TemporaryDirectory()
|
||||
self.dirs.append(ipydir)
|
||||
self.env['IPYTHONDIR'] = ipydir.name
|
||||
self.workingdir = workingdir = TemporaryDirectory()
|
||||
self.dirs.append(workingdir)
|
||||
self.env['IPTEST_WORKING_DIR'] = workingdir.name
|
||||
# This means we won't get odd effects from our own matplotlib config
|
||||
self.env['MPLCONFIGDIR'] = workingdir.name
|
||||
# For security reasons (http://bugs.python.org/issue16202), use
|
||||
# a temporary directory to which other users have no access.
|
||||
self.env['TMPDIR'] = workingdir.name
|
||||
|
||||
# Add a non-accessible directory to PATH (see gh-7053)
|
||||
noaccess = os.path.join(self.workingdir.name, "_no_access_")
|
||||
self.noaccess = noaccess
|
||||
os.mkdir(noaccess, 0)
|
||||
|
||||
PATH = os.environ.get('PATH', '')
|
||||
if PATH:
|
||||
PATH = noaccess + os.pathsep + PATH
|
||||
else:
|
||||
PATH = noaccess
|
||||
self.env['PATH'] = PATH
|
||||
|
||||
# From options:
|
||||
if self.options.xunit:
|
||||
self.add_xunit()
|
||||
if self.options.coverage:
|
||||
self.add_coverage()
|
||||
self.env['IPTEST_SUBPROC_STREAMS'] = self.options.subproc_streams
|
||||
self.cmd.extend(self.options.extra_args)
|
||||
|
||||
def cleanup(self):
|
||||
"""
|
||||
Make the non-accessible directory created in setup() accessible
|
||||
again, otherwise deleting the workingdir will fail.
|
||||
"""
|
||||
os.chmod(self.noaccess, stat.S_IRWXU)
|
||||
TestController.cleanup(self)
|
||||
|
||||
@property
|
||||
def will_run(self):
|
||||
try:
|
||||
return test_sections[self.section].will_run
|
||||
except KeyError:
|
||||
return True
|
||||
|
||||
def add_xunit(self):
|
||||
xunit_file = os.path.abspath(self.section + '.xunit.xml')
|
||||
self.cmd.extend(['--with-xunit', '--xunit-file', xunit_file])
|
||||
|
||||
def add_coverage(self):
|
||||
try:
|
||||
sources = test_sections[self.section].includes
|
||||
except KeyError:
|
||||
sources = ['IPython']
|
||||
|
||||
coverage_rc = ("[run]\n"
|
||||
"data_file = {data_file}\n"
|
||||
"source =\n"
|
||||
" {source}\n"
|
||||
).format(data_file=os.path.abspath('.coverage.'+self.section),
|
||||
source="\n ".join(sources))
|
||||
config_file = os.path.join(self.workingdir.name, '.coveragerc')
|
||||
with open(config_file, 'w') as f:
|
||||
f.write(coverage_rc)
|
||||
|
||||
self.env['COVERAGE_PROCESS_START'] = config_file
|
||||
self.pycmd = "import coverage; coverage.process_startup(); " + self.pycmd
|
||||
|
||||
def launch(self, buffer_output=False):
|
||||
self.cmd[2] = self.pycmd
|
||||
super(PyTestController, self).launch(buffer_output=buffer_output)
|
||||
|
||||
|
||||
def prepare_controllers(options):
|
||||
"""Returns two lists of TestController instances, those to run, and those
|
||||
not to run."""
|
||||
testgroups = options.testgroups
|
||||
if not testgroups:
|
||||
testgroups = py_test_group_names
|
||||
|
||||
controllers = [PyTestController(name, options) for name in testgroups]
|
||||
|
||||
to_run = [c for c in controllers if c.will_run]
|
||||
not_run = [c for c in controllers if not c.will_run]
|
||||
return to_run, not_run
|
||||
|
||||
def do_run(controller, buffer_output=True):
|
||||
"""Setup and run a test controller.
|
||||
|
||||
If buffer_output is True, no output is displayed, to avoid it appearing
|
||||
interleaved. In this case, the caller is responsible for displaying test
|
||||
output on failure.
|
||||
|
||||
Returns
|
||||
-------
|
||||
controller : TestController
|
||||
The same controller as passed in, as a convenience for using map() type
|
||||
APIs.
|
||||
exitcode : int
|
||||
The exit code of the test subprocess. Non-zero indicates failure.
|
||||
"""
|
||||
try:
|
||||
try:
|
||||
controller.setup()
|
||||
controller.launch(buffer_output=buffer_output)
|
||||
except Exception:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return controller, 1 # signal failure
|
||||
|
||||
exitcode = controller.wait()
|
||||
return controller, exitcode
|
||||
|
||||
except KeyboardInterrupt:
|
||||
return controller, -signal.SIGINT
|
||||
finally:
|
||||
controller.cleanup()
|
||||
|
||||
def report():
|
||||
"""Return a string with a summary report of test-related variables."""
|
||||
inf = get_sys_info()
|
||||
out = []
|
||||
def _add(name, value):
|
||||
out.append((name, value))
|
||||
|
||||
_add('IPython version', inf['ipython_version'])
|
||||
_add('IPython commit', "{} ({})".format(inf['commit_hash'], inf['commit_source']))
|
||||
_add('IPython package', compress_user(inf['ipython_path']))
|
||||
_add('Python version', inf['sys_version'].replace('\n',''))
|
||||
_add('sys.executable', compress_user(inf['sys_executable']))
|
||||
_add('Platform', inf['platform'])
|
||||
|
||||
width = max(len(n) for (n,v) in out)
|
||||
out = ["{:<{width}}: {}\n".format(n, v, width=width) for (n,v) in out]
|
||||
|
||||
avail = []
|
||||
not_avail = []
|
||||
|
||||
for k, is_avail in have.items():
|
||||
if is_avail:
|
||||
avail.append(k)
|
||||
else:
|
||||
not_avail.append(k)
|
||||
|
||||
if avail:
|
||||
out.append('\nTools and libraries available at test time:\n')
|
||||
avail.sort()
|
||||
out.append(' ' + ' '.join(avail)+'\n')
|
||||
|
||||
if not_avail:
|
||||
out.append('\nTools and libraries NOT available at test time:\n')
|
||||
not_avail.sort()
|
||||
out.append(' ' + ' '.join(not_avail)+'\n')
|
||||
|
||||
return ''.join(out)
|
||||
|
||||
def run_iptestall(options):
|
||||
"""Run the entire IPython test suite by calling nose and trial.
|
||||
|
||||
This function constructs :class:`IPTester` instances for all IPython
|
||||
modules and package and then runs each of them. This causes the modules
|
||||
and packages of IPython to be tested each in their own subprocess using
|
||||
nose.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
All parameters are passed as attributes of the options object.
|
||||
|
||||
testgroups : list of str
|
||||
Run only these sections of the test suite. If empty, run all the available
|
||||
sections.
|
||||
|
||||
fast : int or None
|
||||
Run the test suite in parallel, using n simultaneous processes. If None
|
||||
is passed, one process is used per CPU core. Default 1 (i.e. sequential)
|
||||
|
||||
inc_slow : bool
|
||||
Include slow tests. By default, these tests aren't run.
|
||||
|
||||
url : unicode
|
||||
Address:port to use when running the JS tests.
|
||||
|
||||
xunit : bool
|
||||
Produce Xunit XML output. This is written to multiple foo.xunit.xml files.
|
||||
|
||||
coverage : bool or str
|
||||
Measure code coverage from tests. True will store the raw coverage data,
|
||||
or pass 'html' or 'xml' to get reports.
|
||||
|
||||
extra_args : list
|
||||
Extra arguments to pass to the test subprocesses, e.g. '-v'
|
||||
"""
|
||||
to_run, not_run = prepare_controllers(options)
|
||||
|
||||
def justify(ltext, rtext, width=70, fill='-'):
|
||||
ltext += ' '
|
||||
rtext = (' ' + rtext).rjust(width - len(ltext), fill)
|
||||
return ltext + rtext
|
||||
|
||||
# Run all test runners, tracking execution time
|
||||
failed = []
|
||||
t_start = time.time()
|
||||
|
||||
print()
|
||||
if options.fast == 1:
|
||||
# This actually means sequential, i.e. with 1 job
|
||||
for controller in to_run:
|
||||
print('Test group:', controller.section)
|
||||
sys.stdout.flush() # Show in correct order when output is piped
|
||||
controller, res = do_run(controller, buffer_output=False)
|
||||
if res:
|
||||
failed.append(controller)
|
||||
if res == -signal.SIGINT:
|
||||
print("Interrupted")
|
||||
break
|
||||
print()
|
||||
|
||||
else:
|
||||
# Run tests concurrently
|
||||
try:
|
||||
pool = multiprocessing.pool.ThreadPool(options.fast)
|
||||
for (controller, res) in pool.imap_unordered(do_run, to_run):
|
||||
res_string = 'OK' if res == 0 else 'FAILED'
|
||||
print(justify('Test group: ' + controller.section, res_string))
|
||||
if res:
|
||||
print(decode(controller.stdout))
|
||||
failed.append(controller)
|
||||
if res == -signal.SIGINT:
|
||||
print("Interrupted")
|
||||
break
|
||||
except KeyboardInterrupt:
|
||||
return
|
||||
|
||||
for controller in not_run:
|
||||
print(justify('Test group: ' + controller.section, 'NOT RUN'))
|
||||
|
||||
t_end = time.time()
|
||||
t_tests = t_end - t_start
|
||||
nrunners = len(to_run)
|
||||
nfail = len(failed)
|
||||
# summarize results
|
||||
print('_'*70)
|
||||
print('Test suite completed for system with the following information:')
|
||||
print(report())
|
||||
took = "Took %.3fs." % t_tests
|
||||
print('Status: ', end='')
|
||||
if not failed:
|
||||
print('OK (%d test groups).' % nrunners, took)
|
||||
else:
|
||||
# If anything went wrong, point out what command to rerun manually to
|
||||
# see the actual errors and individual summary
|
||||
failed_sections = [c.section for c in failed]
|
||||
print('ERROR - {} out of {} test groups failed ({}).'.format(nfail,
|
||||
nrunners, ', '.join(failed_sections)), took)
|
||||
print()
|
||||
print('You may wish to rerun these, with:')
|
||||
print(' iptest', *failed_sections)
|
||||
print()
|
||||
|
||||
if options.coverage:
|
||||
from coverage import coverage, CoverageException
|
||||
cov = coverage(data_file='.coverage')
|
||||
cov.combine()
|
||||
cov.save()
|
||||
|
||||
# Coverage HTML report
|
||||
if options.coverage == 'html':
|
||||
html_dir = 'ipy_htmlcov'
|
||||
shutil.rmtree(html_dir, ignore_errors=True)
|
||||
print("Writing HTML coverage report to %s/ ... " % html_dir, end="")
|
||||
sys.stdout.flush()
|
||||
|
||||
# Custom HTML reporter to clean up module names.
|
||||
from coverage.html import HtmlReporter
|
||||
class CustomHtmlReporter(HtmlReporter):
|
||||
def find_code_units(self, morfs):
|
||||
super(CustomHtmlReporter, self).find_code_units(morfs)
|
||||
for cu in self.code_units:
|
||||
nameparts = cu.name.split(os.sep)
|
||||
if 'IPython' not in nameparts:
|
||||
continue
|
||||
ix = nameparts.index('IPython')
|
||||
cu.name = '.'.join(nameparts[ix:])
|
||||
|
||||
# Reimplement the html_report method with our custom reporter
|
||||
cov.get_data()
|
||||
cov.config.from_args(omit='*{0}tests{0}*'.format(os.sep), html_dir=html_dir,
|
||||
html_title='IPython test coverage',
|
||||
)
|
||||
reporter = CustomHtmlReporter(cov, cov.config)
|
||||
reporter.report(None)
|
||||
print('done.')
|
||||
|
||||
# Coverage XML report
|
||||
elif options.coverage == 'xml':
|
||||
try:
|
||||
cov.xml_report(outfile='ipy_coverage.xml')
|
||||
except CoverageException as e:
|
||||
print('Generating coverage report failed. Are you running javascript tests only?')
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
if failed:
|
||||
# Ensure that our exit code indicates failure
|
||||
sys.exit(1)
|
||||
|
||||
argparser = argparse.ArgumentParser(description='Run IPython test suite')
|
||||
argparser.add_argument('testgroups', nargs='*',
|
||||
help='Run specified groups of tests. If omitted, run '
|
||||
'all tests.')
|
||||
argparser.add_argument('--all', action='store_true',
|
||||
help='Include slow tests not run by default.')
|
||||
argparser.add_argument('-j', '--fast', nargs='?', const=None, default=1, type=int,
|
||||
help='Run test sections in parallel. This starts as many '
|
||||
'processes as you have cores, or you can specify a number.')
|
||||
argparser.add_argument('--xunit', action='store_true',
|
||||
help='Produce Xunit XML results')
|
||||
argparser.add_argument('--coverage', nargs='?', const=True, default=False,
|
||||
help="Measure test coverage. Specify 'html' or "
|
||||
"'xml' to get reports.")
|
||||
argparser.add_argument('--subproc-streams', default='capture',
|
||||
help="What to do with stdout/stderr from subprocesses. "
|
||||
"'capture' (default), 'show' and 'discard' are the options.")
|
||||
|
||||
def default_options():
|
||||
"""Get an argparse Namespace object with the default arguments, to pass to
|
||||
:func:`run_iptestall`.
|
||||
"""
|
||||
options = argparser.parse_args([])
|
||||
options.extra_args = []
|
||||
return options
|
||||
|
||||
def main():
|
||||
# iptest doesn't work correctly if the working directory is the
|
||||
# root of the IPython source tree. Tell the user to avoid
|
||||
# frustration.
|
||||
if os.path.exists(os.path.join(os.getcwd(),
|
||||
'IPython', 'testing', '__main__.py')):
|
||||
print("Don't run iptest from the IPython source directory",
|
||||
file=sys.stderr)
|
||||
sys.exit(1)
|
||||
# Arguments after -- should be passed through to nose. Argparse treats
|
||||
# everything after -- as regular positional arguments, so we separate them
|
||||
# first.
|
||||
try:
|
||||
ix = sys.argv.index('--')
|
||||
except ValueError:
|
||||
to_parse = sys.argv[1:]
|
||||
extra_args = []
|
||||
else:
|
||||
to_parse = sys.argv[1:ix]
|
||||
extra_args = sys.argv[ix+1:]
|
||||
|
||||
options = argparser.parse_args(to_parse)
|
||||
options.extra_args = extra_args
|
||||
|
||||
run_iptestall(options)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
178
venv/Lib/site-packages/IPython/testing/ipunittest.py
Normal file
178
venv/Lib/site-packages/IPython/testing/ipunittest.py
Normal file
|
@ -0,0 +1,178 @@
|
|||
"""Experimental code for cleaner support of IPython syntax with unittest.
|
||||
|
||||
In IPython up until 0.10, we've used very hacked up nose machinery for running
|
||||
tests with IPython special syntax, and this has proved to be extremely slow.
|
||||
This module provides decorators to try a different approach, stemming from a
|
||||
conversation Brian and I (FP) had about this problem Sept/09.
|
||||
|
||||
The goal is to be able to easily write simple functions that can be seen by
|
||||
unittest as tests, and ultimately for these to support doctests with full
|
||||
IPython syntax. Nose already offers this based on naming conventions and our
|
||||
hackish plugins, but we are seeking to move away from nose dependencies if
|
||||
possible.
|
||||
|
||||
This module follows a different approach, based on decorators.
|
||||
|
||||
- A decorator called @ipdoctest can mark any function as having a docstring
|
||||
that should be viewed as a doctest, but after syntax conversion.
|
||||
|
||||
Authors
|
||||
-------
|
||||
|
||||
- Fernando Perez <Fernando.Perez@berkeley.edu>
|
||||
"""
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (C) 2009-2011 The IPython Development Team
|
||||
#
|
||||
# Distributed under the terms of the BSD License. The full license is in
|
||||
# the file COPYING, distributed as part of this software.
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Imports
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
# Stdlib
|
||||
import re
|
||||
import unittest
|
||||
from doctest import DocTestFinder, DocTestRunner, TestResults
|
||||
from IPython.terminal.interactiveshell import InteractiveShell
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Classes and functions
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
def count_failures(runner):
|
||||
"""Count number of failures in a doctest runner.
|
||||
|
||||
Code modeled after the summarize() method in doctest.
|
||||
"""
|
||||
return [TestResults(f, t) for f, t in runner._name2ft.values() if f > 0 ]
|
||||
|
||||
|
||||
class IPython2PythonConverter(object):
|
||||
"""Convert IPython 'syntax' to valid Python.
|
||||
|
||||
Eventually this code may grow to be the full IPython syntax conversion
|
||||
implementation, but for now it only does prompt conversion."""
|
||||
|
||||
def __init__(self):
|
||||
self.rps1 = re.compile(r'In\ \[\d+\]: ')
|
||||
self.rps2 = re.compile(r'\ \ \ \.\.\.+: ')
|
||||
self.rout = re.compile(r'Out\[\d+\]: \s*?\n?')
|
||||
self.pyps1 = '>>> '
|
||||
self.pyps2 = '... '
|
||||
self.rpyps1 = re.compile (r'(\s*%s)(.*)$' % self.pyps1)
|
||||
self.rpyps2 = re.compile (r'(\s*%s)(.*)$' % self.pyps2)
|
||||
|
||||
def __call__(self, ds):
|
||||
"""Convert IPython prompts to python ones in a string."""
|
||||
from . import globalipapp
|
||||
|
||||
pyps1 = '>>> '
|
||||
pyps2 = '... '
|
||||
pyout = ''
|
||||
|
||||
dnew = ds
|
||||
dnew = self.rps1.sub(pyps1, dnew)
|
||||
dnew = self.rps2.sub(pyps2, dnew)
|
||||
dnew = self.rout.sub(pyout, dnew)
|
||||
ip = InteractiveShell.instance()
|
||||
|
||||
# Convert input IPython source into valid Python.
|
||||
out = []
|
||||
newline = out.append
|
||||
for line in dnew.splitlines():
|
||||
|
||||
mps1 = self.rpyps1.match(line)
|
||||
if mps1 is not None:
|
||||
prompt, text = mps1.groups()
|
||||
newline(prompt+ip.prefilter(text, False))
|
||||
continue
|
||||
|
||||
mps2 = self.rpyps2.match(line)
|
||||
if mps2 is not None:
|
||||
prompt, text = mps2.groups()
|
||||
newline(prompt+ip.prefilter(text, True))
|
||||
continue
|
||||
|
||||
newline(line)
|
||||
newline('') # ensure a closing newline, needed by doctest
|
||||
#print "PYSRC:", '\n'.join(out) # dbg
|
||||
return '\n'.join(out)
|
||||
|
||||
#return dnew
|
||||
|
||||
|
||||
class Doc2UnitTester(object):
|
||||
"""Class whose instances act as a decorator for docstring testing.
|
||||
|
||||
In practice we're only likely to need one instance ever, made below (though
|
||||
no attempt is made at turning it into a singleton, there is no need for
|
||||
that).
|
||||
"""
|
||||
def __init__(self, verbose=False):
|
||||
"""New decorator.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
verbose : boolean, optional (False)
|
||||
Passed to the doctest finder and runner to control verbosity.
|
||||
"""
|
||||
self.verbose = verbose
|
||||
# We can reuse the same finder for all instances
|
||||
self.finder = DocTestFinder(verbose=verbose, recurse=False)
|
||||
|
||||
def __call__(self, func):
|
||||
"""Use as a decorator: doctest a function's docstring as a unittest.
|
||||
|
||||
This version runs normal doctests, but the idea is to make it later run
|
||||
ipython syntax instead."""
|
||||
|
||||
# Capture the enclosing instance with a different name, so the new
|
||||
# class below can see it without confusion regarding its own 'self'
|
||||
# that will point to the test instance at runtime
|
||||
d2u = self
|
||||
|
||||
# Rewrite the function's docstring to have python syntax
|
||||
if func.__doc__ is not None:
|
||||
func.__doc__ = ip2py(func.__doc__)
|
||||
|
||||
# Now, create a tester object that is a real unittest instance, so
|
||||
# normal unittest machinery (or Nose, or Trial) can find it.
|
||||
class Tester(unittest.TestCase):
|
||||
def test(self):
|
||||
# Make a new runner per function to be tested
|
||||
runner = DocTestRunner(verbose=d2u.verbose)
|
||||
for the_test in d2u.finder.find(func, func.__name__):
|
||||
runner.run(the_test)
|
||||
failed = count_failures(runner)
|
||||
if failed:
|
||||
# Since we only looked at a single function's docstring,
|
||||
# failed should contain at most one item. More than that
|
||||
# is a case we can't handle and should error out on
|
||||
if len(failed) > 1:
|
||||
err = "Invalid number of test results: %s" % failed
|
||||
raise ValueError(err)
|
||||
# Report a normal failure.
|
||||
self.fail('failed doctests: %s' % str(failed[0]))
|
||||
|
||||
# Rename it so test reports have the original signature.
|
||||
Tester.__name__ = func.__name__
|
||||
return Tester
|
||||
|
||||
|
||||
def ipdocstring(func):
|
||||
"""Change the function docstring via ip2py.
|
||||
"""
|
||||
if func.__doc__ is not None:
|
||||
func.__doc__ = ip2py(func.__doc__)
|
||||
return func
|
||||
|
||||
|
||||
# Make an instance of the classes for public use
|
||||
ipdoctest = Doc2UnitTester()
|
||||
ip2py = IPython2PythonConverter()
|
34
venv/Lib/site-packages/IPython/testing/plugin/README.txt
Normal file
34
venv/Lib/site-packages/IPython/testing/plugin/README.txt
Normal file
|
@ -0,0 +1,34 @@
|
|||
=======================================================
|
||||
Nose plugin with IPython and extension module support
|
||||
=======================================================
|
||||
|
||||
This directory provides the key functionality for test support that IPython
|
||||
needs as a nose plugin, which can be installed for use in projects other than
|
||||
IPython.
|
||||
|
||||
The presence of a Makefile here is mostly for development and debugging
|
||||
purposes as it only provides a few shorthand commands. You can manually
|
||||
install the plugin by using standard Python procedures (``setup.py install``
|
||||
with appropriate arguments).
|
||||
|
||||
To install the plugin using the Makefile, edit its first line to reflect where
|
||||
you'd like the installation.
|
||||
|
||||
Once you've set the prefix, simply build/install the plugin with::
|
||||
|
||||
make
|
||||
|
||||
and run the tests with::
|
||||
|
||||
make test
|
||||
|
||||
You should see output similar to::
|
||||
|
||||
maqroll[plugin]> make test
|
||||
nosetests -s --with-ipdoctest --doctest-tests dtexample.py
|
||||
..
|
||||
----------------------------------------------------------------------
|
||||
Ran 2 tests in 0.016s
|
||||
|
||||
OK
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
157
venv/Lib/site-packages/IPython/testing/plugin/dtexample.py
Normal file
157
venv/Lib/site-packages/IPython/testing/plugin/dtexample.py
Normal file
|
@ -0,0 +1,157 @@
|
|||
"""Simple example using doctests.
|
||||
|
||||
This file just contains doctests both using plain python and IPython prompts.
|
||||
All tests should be loaded by nose.
|
||||
"""
|
||||
|
||||
def pyfunc():
|
||||
"""Some pure python tests...
|
||||
|
||||
>>> pyfunc()
|
||||
'pyfunc'
|
||||
|
||||
>>> import os
|
||||
|
||||
>>> 2+3
|
||||
5
|
||||
|
||||
>>> for i in range(3):
|
||||
... print(i, end=' ')
|
||||
... print(i+1, end=' ')
|
||||
...
|
||||
0 1 1 2 2 3
|
||||
"""
|
||||
return 'pyfunc'
|
||||
|
||||
def ipfunc():
|
||||
"""Some ipython tests...
|
||||
|
||||
In [1]: import os
|
||||
|
||||
In [3]: 2+3
|
||||
Out[3]: 5
|
||||
|
||||
In [26]: for i in range(3):
|
||||
....: print(i, end=' ')
|
||||
....: print(i+1, end=' ')
|
||||
....:
|
||||
0 1 1 2 2 3
|
||||
|
||||
|
||||
Examples that access the operating system work:
|
||||
|
||||
In [1]: !echo hello
|
||||
hello
|
||||
|
||||
In [2]: !echo hello > /tmp/foo_iptest
|
||||
|
||||
In [3]: !cat /tmp/foo_iptest
|
||||
hello
|
||||
|
||||
In [4]: rm -f /tmp/foo_iptest
|
||||
|
||||
It's OK to use '_' for the last result, but do NOT try to use IPython's
|
||||
numbered history of _NN outputs, since those won't exist under the
|
||||
doctest environment:
|
||||
|
||||
In [7]: 'hi'
|
||||
Out[7]: 'hi'
|
||||
|
||||
In [8]: print(repr(_))
|
||||
'hi'
|
||||
|
||||
In [7]: 3+4
|
||||
Out[7]: 7
|
||||
|
||||
In [8]: _+3
|
||||
Out[8]: 10
|
||||
|
||||
In [9]: ipfunc()
|
||||
Out[9]: 'ipfunc'
|
||||
"""
|
||||
return 'ipfunc'
|
||||
|
||||
|
||||
def ranfunc():
|
||||
"""A function with some random output.
|
||||
|
||||
Normal examples are verified as usual:
|
||||
>>> 1+3
|
||||
4
|
||||
|
||||
But if you put '# random' in the output, it is ignored:
|
||||
>>> 1+3
|
||||
junk goes here... # random
|
||||
|
||||
>>> 1+2
|
||||
again, anything goes #random
|
||||
if multiline, the random mark is only needed once.
|
||||
|
||||
>>> 1+2
|
||||
You can also put the random marker at the end:
|
||||
# random
|
||||
|
||||
>>> 1+2
|
||||
# random
|
||||
.. or at the beginning.
|
||||
|
||||
More correct input is properly verified:
|
||||
>>> ranfunc()
|
||||
'ranfunc'
|
||||
"""
|
||||
return 'ranfunc'
|
||||
|
||||
|
||||
def random_all():
|
||||
"""A function where we ignore the output of ALL examples.
|
||||
|
||||
Examples:
|
||||
|
||||
# all-random
|
||||
|
||||
This mark tells the testing machinery that all subsequent examples should
|
||||
be treated as random (ignoring their output). They are still executed,
|
||||
so if a they raise an error, it will be detected as such, but their
|
||||
output is completely ignored.
|
||||
|
||||
>>> 1+3
|
||||
junk goes here...
|
||||
|
||||
>>> 1+3
|
||||
klasdfj;
|
||||
|
||||
>>> 1+2
|
||||
again, anything goes
|
||||
blah...
|
||||
"""
|
||||
pass
|
||||
|
||||
def iprand():
|
||||
"""Some ipython tests with random output.
|
||||
|
||||
In [7]: 3+4
|
||||
Out[7]: 7
|
||||
|
||||
In [8]: print('hello')
|
||||
world # random
|
||||
|
||||
In [9]: iprand()
|
||||
Out[9]: 'iprand'
|
||||
"""
|
||||
return 'iprand'
|
||||
|
||||
def iprand_all():
|
||||
"""Some ipython tests with fully random output.
|
||||
|
||||
# all-random
|
||||
|
||||
In [7]: 1
|
||||
Out[7]: 99
|
||||
|
||||
In [8]: print('hello')
|
||||
world
|
||||
|
||||
In [9]: iprand_all()
|
||||
Out[9]: 'junk'
|
||||
"""
|
||||
return 'iprand_all'
|
761
venv/Lib/site-packages/IPython/testing/plugin/ipdoctest.py
Normal file
761
venv/Lib/site-packages/IPython/testing/plugin/ipdoctest.py
Normal file
|
@ -0,0 +1,761 @@
|
|||
"""Nose Plugin that supports IPython doctests.
|
||||
|
||||
Limitations:
|
||||
|
||||
- When generating examples for use as doctests, make sure that you have
|
||||
pretty-printing OFF. This can be done either by setting the
|
||||
``PlainTextFormatter.pprint`` option in your configuration file to False, or
|
||||
by interactively disabling it with %Pprint. This is required so that IPython
|
||||
output matches that of normal Python, which is used by doctest for internal
|
||||
execution.
|
||||
|
||||
- Do not rely on specific prompt numbers for results (such as using
|
||||
'_34==True', for example). For IPython tests run via an external process the
|
||||
prompt numbers may be different, and IPython tests run as normal python code
|
||||
won't even have these special _NN variables set at all.
|
||||
"""
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Module imports
|
||||
|
||||
# From the standard library
|
||||
import builtins as builtin_mod
|
||||
import doctest
|
||||
import inspect
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from importlib import import_module
|
||||
from io import StringIO
|
||||
|
||||
from testpath import modified_env
|
||||
|
||||
from inspect import getmodule
|
||||
|
||||
# We are overriding the default doctest runner, so we need to import a few
|
||||
# things from doctest directly
|
||||
from doctest import (REPORTING_FLAGS, REPORT_ONLY_FIRST_FAILURE,
|
||||
_unittest_reportflags, DocTestRunner,
|
||||
_extract_future_flags, pdb, _OutputRedirectingPdb,
|
||||
_exception_traceback,
|
||||
linecache)
|
||||
|
||||
# Third-party modules
|
||||
|
||||
from nose.plugins import doctests, Plugin
|
||||
from nose.util import anyp, tolist
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Module globals and other constants
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Classes and functions
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
def is_extension_module(filename):
|
||||
"""Return whether the given filename is an extension module.
|
||||
|
||||
This simply checks that the extension is either .so or .pyd.
|
||||
"""
|
||||
return os.path.splitext(filename)[1].lower() in ('.so','.pyd')
|
||||
|
||||
|
||||
class DocTestSkip(object):
|
||||
"""Object wrapper for doctests to be skipped."""
|
||||
|
||||
ds_skip = """Doctest to skip.
|
||||
>>> 1 #doctest: +SKIP
|
||||
"""
|
||||
|
||||
def __init__(self,obj):
|
||||
self.obj = obj
|
||||
|
||||
def __getattribute__(self,key):
|
||||
if key == '__doc__':
|
||||
return DocTestSkip.ds_skip
|
||||
else:
|
||||
return getattr(object.__getattribute__(self,'obj'),key)
|
||||
|
||||
# Modified version of the one in the stdlib, that fixes a python bug (doctests
|
||||
# not found in extension modules, http://bugs.python.org/issue3158)
|
||||
class DocTestFinder(doctest.DocTestFinder):
|
||||
|
||||
def _from_module(self, module, object):
|
||||
"""
|
||||
Return true if the given object is defined in the given
|
||||
module.
|
||||
"""
|
||||
if module is None:
|
||||
return True
|
||||
elif inspect.isfunction(object):
|
||||
return module.__dict__ is object.__globals__
|
||||
elif inspect.isbuiltin(object):
|
||||
return module.__name__ == object.__module__
|
||||
elif inspect.isclass(object):
|
||||
return module.__name__ == object.__module__
|
||||
elif inspect.ismethod(object):
|
||||
# This one may be a bug in cython that fails to correctly set the
|
||||
# __module__ attribute of methods, but since the same error is easy
|
||||
# to make by extension code writers, having this safety in place
|
||||
# isn't such a bad idea
|
||||
return module.__name__ == object.__self__.__class__.__module__
|
||||
elif inspect.getmodule(object) is not None:
|
||||
return module is inspect.getmodule(object)
|
||||
elif hasattr(object, '__module__'):
|
||||
return module.__name__ == object.__module__
|
||||
elif isinstance(object, property):
|
||||
return True # [XX] no way not be sure.
|
||||
elif inspect.ismethoddescriptor(object):
|
||||
# Unbound PyQt signals reach this point in Python 3.4b3, and we want
|
||||
# to avoid throwing an error. See also http://bugs.python.org/issue3158
|
||||
return False
|
||||
else:
|
||||
raise ValueError("object must be a class or function, got %r" % object)
|
||||
|
||||
def _find(self, tests, obj, name, module, source_lines, globs, seen):
|
||||
"""
|
||||
Find tests for the given object and any contained objects, and
|
||||
add them to `tests`.
|
||||
"""
|
||||
print('_find for:', obj, name, module) # dbg
|
||||
if hasattr(obj,"skip_doctest"):
|
||||
#print 'SKIPPING DOCTEST FOR:',obj # dbg
|
||||
obj = DocTestSkip(obj)
|
||||
|
||||
doctest.DocTestFinder._find(self,tests, obj, name, module,
|
||||
source_lines, globs, seen)
|
||||
|
||||
# Below we re-run pieces of the above method with manual modifications,
|
||||
# because the original code is buggy and fails to correctly identify
|
||||
# doctests in extension modules.
|
||||
|
||||
# Local shorthands
|
||||
from inspect import isroutine, isclass
|
||||
|
||||
# Look for tests in a module's contained objects.
|
||||
if inspect.ismodule(obj) and self._recurse:
|
||||
for valname, val in obj.__dict__.items():
|
||||
valname1 = '%s.%s' % (name, valname)
|
||||
if ( (isroutine(val) or isclass(val))
|
||||
and self._from_module(module, val) ):
|
||||
|
||||
self._find(tests, val, valname1, module, source_lines,
|
||||
globs, seen)
|
||||
|
||||
# Look for tests in a class's contained objects.
|
||||
if inspect.isclass(obj) and self._recurse:
|
||||
#print 'RECURSE into class:',obj # dbg
|
||||
for valname, val in obj.__dict__.items():
|
||||
# Special handling for staticmethod/classmethod.
|
||||
if isinstance(val, staticmethod):
|
||||
val = getattr(obj, valname)
|
||||
if isinstance(val, classmethod):
|
||||
val = getattr(obj, valname).__func__
|
||||
|
||||
# Recurse to methods, properties, and nested classes.
|
||||
if ((inspect.isfunction(val) or inspect.isclass(val) or
|
||||
inspect.ismethod(val) or
|
||||
isinstance(val, property)) and
|
||||
self._from_module(module, val)):
|
||||
valname = '%s.%s' % (name, valname)
|
||||
self._find(tests, val, valname, module, source_lines,
|
||||
globs, seen)
|
||||
|
||||
|
||||
class IPDoctestOutputChecker(doctest.OutputChecker):
|
||||
"""Second-chance checker with support for random tests.
|
||||
|
||||
If the default comparison doesn't pass, this checker looks in the expected
|
||||
output string for flags that tell us to ignore the output.
|
||||
"""
|
||||
|
||||
random_re = re.compile(r'#\s*random\s+')
|
||||
|
||||
def check_output(self, want, got, optionflags):
|
||||
"""Check output, accepting special markers embedded in the output.
|
||||
|
||||
If the output didn't pass the default validation but the special string
|
||||
'#random' is included, we accept it."""
|
||||
|
||||
# Let the original tester verify first, in case people have valid tests
|
||||
# that happen to have a comment saying '#random' embedded in.
|
||||
ret = doctest.OutputChecker.check_output(self, want, got,
|
||||
optionflags)
|
||||
if not ret and self.random_re.search(want):
|
||||
#print >> sys.stderr, 'RANDOM OK:',want # dbg
|
||||
return True
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
class DocTestCase(doctests.DocTestCase):
|
||||
"""Proxy for DocTestCase: provides an address() method that
|
||||
returns the correct address for the doctest case. Otherwise
|
||||
acts as a proxy to the test case. To provide hints for address(),
|
||||
an obj may also be passed -- this will be used as the test object
|
||||
for purposes of determining the test address, if it is provided.
|
||||
"""
|
||||
|
||||
# Note: this method was taken from numpy's nosetester module.
|
||||
|
||||
# Subclass nose.plugins.doctests.DocTestCase to work around a bug in
|
||||
# its constructor that blocks non-default arguments from being passed
|
||||
# down into doctest.DocTestCase
|
||||
|
||||
def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
|
||||
checker=None, obj=None, result_var='_'):
|
||||
self._result_var = result_var
|
||||
doctests.DocTestCase.__init__(self, test,
|
||||
optionflags=optionflags,
|
||||
setUp=setUp, tearDown=tearDown,
|
||||
checker=checker)
|
||||
# Now we must actually copy the original constructor from the stdlib
|
||||
# doctest class, because we can't call it directly and a bug in nose
|
||||
# means it never gets passed the right arguments.
|
||||
|
||||
self._dt_optionflags = optionflags
|
||||
self._dt_checker = checker
|
||||
self._dt_test = test
|
||||
self._dt_test_globs_ori = test.globs
|
||||
self._dt_setUp = setUp
|
||||
self._dt_tearDown = tearDown
|
||||
|
||||
# XXX - store this runner once in the object!
|
||||
runner = IPDocTestRunner(optionflags=optionflags,
|
||||
checker=checker, verbose=False)
|
||||
self._dt_runner = runner
|
||||
|
||||
|
||||
# Each doctest should remember the directory it was loaded from, so
|
||||
# things like %run work without too many contortions
|
||||
self._ori_dir = os.path.dirname(test.filename)
|
||||
|
||||
# Modified runTest from the default stdlib
|
||||
def runTest(self):
|
||||
test = self._dt_test
|
||||
runner = self._dt_runner
|
||||
|
||||
old = sys.stdout
|
||||
new = StringIO()
|
||||
optionflags = self._dt_optionflags
|
||||
|
||||
if not (optionflags & REPORTING_FLAGS):
|
||||
# The option flags don't include any reporting flags,
|
||||
# so add the default reporting flags
|
||||
optionflags |= _unittest_reportflags
|
||||
|
||||
try:
|
||||
# Save our current directory and switch out to the one where the
|
||||
# test was originally created, in case another doctest did a
|
||||
# directory change. We'll restore this in the finally clause.
|
||||
curdir = os.getcwd()
|
||||
#print 'runTest in dir:', self._ori_dir # dbg
|
||||
os.chdir(self._ori_dir)
|
||||
|
||||
runner.DIVIDER = "-"*70
|
||||
failures, tries = runner.run(test,out=new.write,
|
||||
clear_globs=False)
|
||||
finally:
|
||||
sys.stdout = old
|
||||
os.chdir(curdir)
|
||||
|
||||
if failures:
|
||||
raise self.failureException(self.format_failure(new.getvalue()))
|
||||
|
||||
def setUp(self):
|
||||
"""Modified test setup that syncs with ipython namespace"""
|
||||
#print "setUp test", self._dt_test.examples # dbg
|
||||
if isinstance(self._dt_test.examples[0], IPExample):
|
||||
# for IPython examples *only*, we swap the globals with the ipython
|
||||
# namespace, after updating it with the globals (which doctest
|
||||
# fills with the necessary info from the module being tested).
|
||||
self.user_ns_orig = {}
|
||||
self.user_ns_orig.update(_ip.user_ns)
|
||||
_ip.user_ns.update(self._dt_test.globs)
|
||||
# We must remove the _ key in the namespace, so that Python's
|
||||
# doctest code sets it naturally
|
||||
_ip.user_ns.pop('_', None)
|
||||
_ip.user_ns['__builtins__'] = builtin_mod
|
||||
self._dt_test.globs = _ip.user_ns
|
||||
|
||||
super(DocTestCase, self).setUp()
|
||||
|
||||
def tearDown(self):
|
||||
|
||||
# Undo the test.globs reassignment we made, so that the parent class
|
||||
# teardown doesn't destroy the ipython namespace
|
||||
if isinstance(self._dt_test.examples[0], IPExample):
|
||||
self._dt_test.globs = self._dt_test_globs_ori
|
||||
_ip.user_ns.clear()
|
||||
_ip.user_ns.update(self.user_ns_orig)
|
||||
|
||||
# XXX - fperez: I am not sure if this is truly a bug in nose 0.11, but
|
||||
# it does look like one to me: its tearDown method tries to run
|
||||
#
|
||||
# delattr(builtin_mod, self._result_var)
|
||||
#
|
||||
# without checking that the attribute really is there; it implicitly
|
||||
# assumes it should have been set via displayhook. But if the
|
||||
# displayhook was never called, this doesn't necessarily happen. I
|
||||
# haven't been able to find a little self-contained example outside of
|
||||
# ipython that would show the problem so I can report it to the nose
|
||||
# team, but it does happen a lot in our code.
|
||||
#
|
||||
# So here, we just protect as narrowly as possible by trapping an
|
||||
# attribute error whose message would be the name of self._result_var,
|
||||
# and letting any other error propagate.
|
||||
try:
|
||||
super(DocTestCase, self).tearDown()
|
||||
except AttributeError as exc:
|
||||
if exc.args[0] != self._result_var:
|
||||
raise
|
||||
|
||||
|
||||
# A simple subclassing of the original with a different class name, so we can
|
||||
# distinguish and treat differently IPython examples from pure python ones.
|
||||
class IPExample(doctest.Example): pass
|
||||
|
||||
|
||||
class IPExternalExample(doctest.Example):
|
||||
"""Doctest examples to be run in an external process."""
|
||||
|
||||
def __init__(self, source, want, exc_msg=None, lineno=0, indent=0,
|
||||
options=None):
|
||||
# Parent constructor
|
||||
doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
|
||||
|
||||
# An EXTRA newline is needed to prevent pexpect hangs
|
||||
self.source += '\n'
|
||||
|
||||
|
||||
class IPDocTestParser(doctest.DocTestParser):
|
||||
"""
|
||||
A class used to parse strings containing doctest examples.
|
||||
|
||||
Note: This is a version modified to properly recognize IPython input and
|
||||
convert any IPython examples into valid Python ones.
|
||||
"""
|
||||
# This regular expression is used to find doctest examples in a
|
||||
# string. It defines three groups: `source` is the source code
|
||||
# (including leading indentation and prompts); `indent` is the
|
||||
# indentation of the first (PS1) line of the source code; and
|
||||
# `want` is the expected output (including leading indentation).
|
||||
|
||||
# Classic Python prompts or default IPython ones
|
||||
_PS1_PY = r'>>>'
|
||||
_PS2_PY = r'\.\.\.'
|
||||
|
||||
_PS1_IP = r'In\ \[\d+\]:'
|
||||
_PS2_IP = r'\ \ \ \.\.\.+:'
|
||||
|
||||
_RE_TPL = r'''
|
||||
# Source consists of a PS1 line followed by zero or more PS2 lines.
|
||||
(?P<source>
|
||||
(?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
|
||||
(?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
|
||||
\n? # a newline
|
||||
# Want consists of any non-blank lines that do not start with PS1.
|
||||
(?P<want> (?:(?![ ]*$) # Not a blank line
|
||||
(?![ ]*%s) # Not a line starting with PS1
|
||||
(?![ ]*%s) # Not a line starting with PS2
|
||||
.*$\n? # But any other line
|
||||
)*)
|
||||
'''
|
||||
|
||||
_EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY),
|
||||
re.MULTILINE | re.VERBOSE)
|
||||
|
||||
_EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
|
||||
re.MULTILINE | re.VERBOSE)
|
||||
|
||||
# Mark a test as being fully random. In this case, we simply append the
|
||||
# random marker ('#random') to each individual example's output. This way
|
||||
# we don't need to modify any other code.
|
||||
_RANDOM_TEST = re.compile(r'#\s*all-random\s+')
|
||||
|
||||
# Mark tests to be executed in an external process - currently unsupported.
|
||||
_EXTERNAL_IP = re.compile(r'#\s*ipdoctest:\s*EXTERNAL')
|
||||
|
||||
def ip2py(self,source):
|
||||
"""Convert input IPython source into valid Python."""
|
||||
block = _ip.input_transformer_manager.transform_cell(source)
|
||||
if len(block.splitlines()) == 1:
|
||||
return _ip.prefilter(block)
|
||||
else:
|
||||
return block
|
||||
|
||||
def parse(self, string, name='<string>'):
|
||||
"""
|
||||
Divide the given string into examples and intervening text,
|
||||
and return them as a list of alternating Examples and strings.
|
||||
Line numbers for the Examples are 0-based. The optional
|
||||
argument `name` is a name identifying this string, and is only
|
||||
used for error messages.
|
||||
"""
|
||||
|
||||
#print 'Parse string:\n',string # dbg
|
||||
|
||||
string = string.expandtabs()
|
||||
# If all lines begin with the same indentation, then strip it.
|
||||
min_indent = self._min_indent(string)
|
||||
if min_indent > 0:
|
||||
string = '\n'.join([l[min_indent:] for l in string.split('\n')])
|
||||
|
||||
output = []
|
||||
charno, lineno = 0, 0
|
||||
|
||||
# We make 'all random' tests by adding the '# random' mark to every
|
||||
# block of output in the test.
|
||||
if self._RANDOM_TEST.search(string):
|
||||
random_marker = '\n# random'
|
||||
else:
|
||||
random_marker = ''
|
||||
|
||||
# Whether to convert the input from ipython to python syntax
|
||||
ip2py = False
|
||||
# Find all doctest examples in the string. First, try them as Python
|
||||
# examples, then as IPython ones
|
||||
terms = list(self._EXAMPLE_RE_PY.finditer(string))
|
||||
if terms:
|
||||
# Normal Python example
|
||||
#print '-'*70 # dbg
|
||||
#print 'PyExample, Source:\n',string # dbg
|
||||
#print '-'*70 # dbg
|
||||
Example = doctest.Example
|
||||
else:
|
||||
# It's an ipython example. Note that IPExamples are run
|
||||
# in-process, so their syntax must be turned into valid python.
|
||||
# IPExternalExamples are run out-of-process (via pexpect) so they
|
||||
# don't need any filtering (a real ipython will be executing them).
|
||||
terms = list(self._EXAMPLE_RE_IP.finditer(string))
|
||||
if self._EXTERNAL_IP.search(string):
|
||||
#print '-'*70 # dbg
|
||||
#print 'IPExternalExample, Source:\n',string # dbg
|
||||
#print '-'*70 # dbg
|
||||
Example = IPExternalExample
|
||||
else:
|
||||
#print '-'*70 # dbg
|
||||
#print 'IPExample, Source:\n',string # dbg
|
||||
#print '-'*70 # dbg
|
||||
Example = IPExample
|
||||
ip2py = True
|
||||
|
||||
for m in terms:
|
||||
# Add the pre-example text to `output`.
|
||||
output.append(string[charno:m.start()])
|
||||
# Update lineno (lines before this example)
|
||||
lineno += string.count('\n', charno, m.start())
|
||||
# Extract info from the regexp match.
|
||||
(source, options, want, exc_msg) = \
|
||||
self._parse_example(m, name, lineno,ip2py)
|
||||
|
||||
# Append the random-output marker (it defaults to empty in most
|
||||
# cases, it's only non-empty for 'all-random' tests):
|
||||
want += random_marker
|
||||
|
||||
if Example is IPExternalExample:
|
||||
options[doctest.NORMALIZE_WHITESPACE] = True
|
||||
want += '\n'
|
||||
|
||||
# Create an Example, and add it to the list.
|
||||
if not self._IS_BLANK_OR_COMMENT(source):
|
||||
output.append(Example(source, want, exc_msg,
|
||||
lineno=lineno,
|
||||
indent=min_indent+len(m.group('indent')),
|
||||
options=options))
|
||||
# Update lineno (lines inside this example)
|
||||
lineno += string.count('\n', m.start(), m.end())
|
||||
# Update charno.
|
||||
charno = m.end()
|
||||
# Add any remaining post-example text to `output`.
|
||||
output.append(string[charno:])
|
||||
return output
|
||||
|
||||
def _parse_example(self, m, name, lineno,ip2py=False):
|
||||
"""
|
||||
Given a regular expression match from `_EXAMPLE_RE` (`m`),
|
||||
return a pair `(source, want)`, where `source` is the matched
|
||||
example's source code (with prompts and indentation stripped);
|
||||
and `want` is the example's expected output (with indentation
|
||||
stripped).
|
||||
|
||||
`name` is the string's name, and `lineno` is the line number
|
||||
where the example starts; both are used for error messages.
|
||||
|
||||
Optional:
|
||||
`ip2py`: if true, filter the input via IPython to convert the syntax
|
||||
into valid python.
|
||||
"""
|
||||
|
||||
# Get the example's indentation level.
|
||||
indent = len(m.group('indent'))
|
||||
|
||||
# Divide source into lines; check that they're properly
|
||||
# indented; and then strip their indentation & prompts.
|
||||
source_lines = m.group('source').split('\n')
|
||||
|
||||
# We're using variable-length input prompts
|
||||
ps1 = m.group('ps1')
|
||||
ps2 = m.group('ps2')
|
||||
ps1_len = len(ps1)
|
||||
|
||||
self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
|
||||
if ps2:
|
||||
self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
|
||||
|
||||
source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines])
|
||||
|
||||
if ip2py:
|
||||
# Convert source input from IPython into valid Python syntax
|
||||
source = self.ip2py(source)
|
||||
|
||||
# Divide want into lines; check that it's properly indented; and
|
||||
# then strip the indentation. Spaces before the last newline should
|
||||
# be preserved, so plain rstrip() isn't good enough.
|
||||
want = m.group('want')
|
||||
want_lines = want.split('\n')
|
||||
if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]):
|
||||
del want_lines[-1] # forget final newline & spaces after it
|
||||
self._check_prefix(want_lines, ' '*indent, name,
|
||||
lineno + len(source_lines))
|
||||
|
||||
# Remove ipython output prompt that might be present in the first line
|
||||
want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0])
|
||||
|
||||
want = '\n'.join([wl[indent:] for wl in want_lines])
|
||||
|
||||
# If `want` contains a traceback message, then extract it.
|
||||
m = self._EXCEPTION_RE.match(want)
|
||||
if m:
|
||||
exc_msg = m.group('msg')
|
||||
else:
|
||||
exc_msg = None
|
||||
|
||||
# Extract options from the source.
|
||||
options = self._find_options(source, name, lineno)
|
||||
|
||||
return source, options, want, exc_msg
|
||||
|
||||
def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
|
||||
"""
|
||||
Given the lines of a source string (including prompts and
|
||||
leading indentation), check to make sure that every prompt is
|
||||
followed by a space character. If any line is not followed by
|
||||
a space character, then raise ValueError.
|
||||
|
||||
Note: IPython-modified version which takes the input prompt length as a
|
||||
parameter, so that prompts of variable length can be dealt with.
|
||||
"""
|
||||
space_idx = indent+ps1_len
|
||||
min_len = space_idx+1
|
||||
for i, line in enumerate(lines):
|
||||
if len(line) >= min_len and line[space_idx] != ' ':
|
||||
raise ValueError('line %r of the docstring for %s '
|
||||
'lacks blank after %s: %r' %
|
||||
(lineno+i+1, name,
|
||||
line[indent:space_idx], line))
|
||||
|
||||
|
||||
SKIP = doctest.register_optionflag('SKIP')
|
||||
|
||||
|
||||
class IPDocTestRunner(doctest.DocTestRunner,object):
|
||||
"""Test runner that synchronizes the IPython namespace with test globals.
|
||||
"""
|
||||
|
||||
def run(self, test, compileflags=None, out=None, clear_globs=True):
|
||||
|
||||
# Hack: ipython needs access to the execution context of the example,
|
||||
# so that it can propagate user variables loaded by %run into
|
||||
# test.globs. We put them here into our modified %run as a function
|
||||
# attribute. Our new %run will then only make the namespace update
|
||||
# when called (rather than unconditionally updating test.globs here
|
||||
# for all examples, most of which won't be calling %run anyway).
|
||||
#_ip._ipdoctest_test_globs = test.globs
|
||||
#_ip._ipdoctest_test_filename = test.filename
|
||||
|
||||
test.globs.update(_ip.user_ns)
|
||||
|
||||
# Override terminal size to standardise traceback format
|
||||
with modified_env({'COLUMNS': '80', 'LINES': '24'}):
|
||||
return super(IPDocTestRunner,self).run(test,
|
||||
compileflags,out,clear_globs)
|
||||
|
||||
|
||||
class DocFileCase(doctest.DocFileCase):
|
||||
"""Overrides to provide filename
|
||||
"""
|
||||
def address(self):
|
||||
return (self._dt_test.filename, None, None)
|
||||
|
||||
|
||||
class ExtensionDoctest(doctests.Doctest):
|
||||
"""Nose Plugin that supports doctests in extension modules.
|
||||
"""
|
||||
name = 'extdoctest' # call nosetests with --with-extdoctest
|
||||
enabled = True
|
||||
|
||||
def options(self, parser, env=os.environ):
|
||||
Plugin.options(self, parser, env)
|
||||
parser.add_option('--doctest-tests', action='store_true',
|
||||
dest='doctest_tests',
|
||||
default=env.get('NOSE_DOCTEST_TESTS',True),
|
||||
help="Also look for doctests in test modules. "
|
||||
"Note that classes, methods and functions should "
|
||||
"have either doctests or non-doctest tests, "
|
||||
"not both. [NOSE_DOCTEST_TESTS]")
|
||||
parser.add_option('--doctest-extension', action="append",
|
||||
dest="doctestExtension",
|
||||
help="Also look for doctests in files with "
|
||||
"this extension [NOSE_DOCTEST_EXTENSION]")
|
||||
# Set the default as a list, if given in env; otherwise
|
||||
# an additional value set on the command line will cause
|
||||
# an error.
|
||||
env_setting = env.get('NOSE_DOCTEST_EXTENSION')
|
||||
if env_setting is not None:
|
||||
parser.set_defaults(doctestExtension=tolist(env_setting))
|
||||
|
||||
|
||||
def configure(self, options, config):
|
||||
Plugin.configure(self, options, config)
|
||||
# Pull standard doctest plugin out of config; we will do doctesting
|
||||
config.plugins.plugins = [p for p in config.plugins.plugins
|
||||
if p.name != 'doctest']
|
||||
self.doctest_tests = options.doctest_tests
|
||||
self.extension = tolist(options.doctestExtension)
|
||||
|
||||
self.parser = doctest.DocTestParser()
|
||||
self.finder = DocTestFinder()
|
||||
self.checker = IPDoctestOutputChecker()
|
||||
self.globs = None
|
||||
self.extraglobs = None
|
||||
|
||||
|
||||
def loadTestsFromExtensionModule(self,filename):
|
||||
bpath,mod = os.path.split(filename)
|
||||
modname = os.path.splitext(mod)[0]
|
||||
try:
|
||||
sys.path.append(bpath)
|
||||
module = import_module(modname)
|
||||
tests = list(self.loadTestsFromModule(module))
|
||||
finally:
|
||||
sys.path.pop()
|
||||
return tests
|
||||
|
||||
# NOTE: the method below is almost a copy of the original one in nose, with
|
||||
# a few modifications to control output checking.
|
||||
|
||||
def loadTestsFromModule(self, module):
|
||||
#print '*** ipdoctest - lTM',module # dbg
|
||||
|
||||
if not self.matches(module.__name__):
|
||||
log.debug("Doctest doesn't want module %s", module)
|
||||
return
|
||||
|
||||
tests = self.finder.find(module,globs=self.globs,
|
||||
extraglobs=self.extraglobs)
|
||||
if not tests:
|
||||
return
|
||||
|
||||
# always use whitespace and ellipsis options
|
||||
optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
|
||||
|
||||
tests.sort()
|
||||
module_file = module.__file__
|
||||
if module_file[-4:] in ('.pyc', '.pyo'):
|
||||
module_file = module_file[:-1]
|
||||
for test in tests:
|
||||
if not test.examples:
|
||||
continue
|
||||
if not test.filename:
|
||||
test.filename = module_file
|
||||
|
||||
yield DocTestCase(test,
|
||||
optionflags=optionflags,
|
||||
checker=self.checker)
|
||||
|
||||
|
||||
def loadTestsFromFile(self, filename):
|
||||
#print "ipdoctest - from file", filename # dbg
|
||||
if is_extension_module(filename):
|
||||
for t in self.loadTestsFromExtensionModule(filename):
|
||||
yield t
|
||||
else:
|
||||
if self.extension and anyp(filename.endswith, self.extension):
|
||||
name = os.path.basename(filename)
|
||||
with open(filename) as dh:
|
||||
doc = dh.read()
|
||||
test = self.parser.get_doctest(
|
||||
doc, globs={'__file__': filename}, name=name,
|
||||
filename=filename, lineno=0)
|
||||
if test.examples:
|
||||
#print 'FileCase:',test.examples # dbg
|
||||
yield DocFileCase(test)
|
||||
else:
|
||||
yield False # no tests to load
|
||||
|
||||
|
||||
class IPythonDoctest(ExtensionDoctest):
|
||||
"""Nose Plugin that supports doctests in extension modules.
|
||||
"""
|
||||
name = 'ipdoctest' # call nosetests with --with-ipdoctest
|
||||
enabled = True
|
||||
|
||||
def makeTest(self, obj, parent):
|
||||
"""Look for doctests in the given object, which will be a
|
||||
function, method or class.
|
||||
"""
|
||||
#print 'Plugin analyzing:', obj, parent # dbg
|
||||
# always use whitespace and ellipsis options
|
||||
optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
|
||||
|
||||
doctests = self.finder.find(obj, module=getmodule(parent))
|
||||
if doctests:
|
||||
for test in doctests:
|
||||
if len(test.examples) == 0:
|
||||
continue
|
||||
|
||||
yield DocTestCase(test, obj=obj,
|
||||
optionflags=optionflags,
|
||||
checker=self.checker)
|
||||
|
||||
def options(self, parser, env=os.environ):
|
||||
#print "Options for nose plugin:", self.name # dbg
|
||||
Plugin.options(self, parser, env)
|
||||
parser.add_option('--ipdoctest-tests', action='store_true',
|
||||
dest='ipdoctest_tests',
|
||||
default=env.get('NOSE_IPDOCTEST_TESTS',True),
|
||||
help="Also look for doctests in test modules. "
|
||||
"Note that classes, methods and functions should "
|
||||
"have either doctests or non-doctest tests, "
|
||||
"not both. [NOSE_IPDOCTEST_TESTS]")
|
||||
parser.add_option('--ipdoctest-extension', action="append",
|
||||
dest="ipdoctest_extension",
|
||||
help="Also look for doctests in files with "
|
||||
"this extension [NOSE_IPDOCTEST_EXTENSION]")
|
||||
# Set the default as a list, if given in env; otherwise
|
||||
# an additional value set on the command line will cause
|
||||
# an error.
|
||||
env_setting = env.get('NOSE_IPDOCTEST_EXTENSION')
|
||||
if env_setting is not None:
|
||||
parser.set_defaults(ipdoctest_extension=tolist(env_setting))
|
||||
|
||||
def configure(self, options, config):
|
||||
#print "Configuring nose plugin:", self.name # dbg
|
||||
Plugin.configure(self, options, config)
|
||||
# Pull standard doctest plugin out of config; we will do doctesting
|
||||
config.plugins.plugins = [p for p in config.plugins.plugins
|
||||
if p.name != 'doctest']
|
||||
self.doctest_tests = options.ipdoctest_tests
|
||||
self.extension = tolist(options.ipdoctest_extension)
|
||||
|
||||
self.parser = IPDocTestParser()
|
||||
self.finder = DocTestFinder(parser=self.parser)
|
||||
self.checker = IPDoctestOutputChecker()
|
||||
self.globs = None
|
||||
self.extraglobs = None
|
18
venv/Lib/site-packages/IPython/testing/plugin/iptest.py
Normal file
18
venv/Lib/site-packages/IPython/testing/plugin/iptest.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python
|
||||
"""Nose-based test runner.
|
||||
"""
|
||||
|
||||
from nose.core import main
|
||||
from nose.plugins.builtin import plugins
|
||||
from nose.plugins.doctests import Doctest
|
||||
|
||||
from . import ipdoctest
|
||||
from .ipdoctest import IPDocTestRunner
|
||||
|
||||
if __name__ == '__main__':
|
||||
print('WARNING: this code is incomplete!')
|
||||
print()
|
||||
|
||||
pp = [x() for x in plugins] # activate all builtin plugins first
|
||||
main(testRunner=IPDocTestRunner(),
|
||||
plugins=pp+[ipdoctest.IPythonDoctest(),Doctest()])
|
18
venv/Lib/site-packages/IPython/testing/plugin/setup.py
Normal file
18
venv/Lib/site-packages/IPython/testing/plugin/setup.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python
|
||||
"""A Nose plugin to support IPython doctests.
|
||||
"""
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(name='IPython doctest plugin',
|
||||
version='0.1',
|
||||
author='The IPython Team',
|
||||
description = 'Nose plugin to load IPython-extended doctests',
|
||||
license = 'LGPL',
|
||||
py_modules = ['ipdoctest'],
|
||||
entry_points = {
|
||||
'nose.plugins.0.10': ['ipdoctest = ipdoctest:IPythonDoctest',
|
||||
'extdoctest = ipdoctest:ExtensionDoctest',
|
||||
],
|
||||
},
|
||||
)
|
19
venv/Lib/site-packages/IPython/testing/plugin/show_refs.py
Normal file
19
venv/Lib/site-packages/IPython/testing/plugin/show_refs.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
"""Simple script to show reference holding behavior.
|
||||
|
||||
This is used by a companion test case.
|
||||
"""
|
||||
|
||||
import gc
|
||||
|
||||
class C(object):
|
||||
def __del__(self):
|
||||
pass
|
||||
#print 'deleting object...' # dbg
|
||||
|
||||
if __name__ == '__main__':
|
||||
c = C()
|
||||
|
||||
c_refs = gc.get_referrers(c)
|
||||
ref_ids = list(map(id,c_refs))
|
||||
|
||||
print('c referrers:',list(map(type,c_refs)))
|
33
venv/Lib/site-packages/IPython/testing/plugin/simple.py
Normal file
33
venv/Lib/site-packages/IPython/testing/plugin/simple.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
"""Simple example using doctests.
|
||||
|
||||
This file just contains doctests both using plain python and IPython prompts.
|
||||
All tests should be loaded by nose.
|
||||
"""
|
||||
|
||||
def pyfunc():
|
||||
"""Some pure python tests...
|
||||
|
||||
>>> pyfunc()
|
||||
'pyfunc'
|
||||
|
||||
>>> import os
|
||||
|
||||
>>> 2+3
|
||||
5
|
||||
|
||||
>>> for i in range(3):
|
||||
... print(i, end=' ')
|
||||
... print(i+1, end=' ')
|
||||
...
|
||||
0 1 1 2 2 3
|
||||
"""
|
||||
return 'pyfunc'
|
||||
|
||||
|
||||
def ipyfunc2():
|
||||
"""Some pure python tests...
|
||||
|
||||
>>> 1+1
|
||||
2
|
||||
"""
|
||||
return 'pyfunc2'
|
|
@ -0,0 +1,2 @@
|
|||
x = 1
|
||||
print('x is:',x)
|
36
venv/Lib/site-packages/IPython/testing/plugin/test_combo.txt
Normal file
36
venv/Lib/site-packages/IPython/testing/plugin/test_combo.txt
Normal file
|
@ -0,0 +1,36 @@
|
|||
=======================
|
||||
Combo testing example
|
||||
=======================
|
||||
|
||||
This is a simple example that mixes ipython doctests::
|
||||
|
||||
In [1]: import code
|
||||
|
||||
In [2]: 2**12
|
||||
Out[2]: 4096
|
||||
|
||||
with command-line example information that does *not* get executed::
|
||||
|
||||
$ mpirun -n 4 ipengine --controller-port=10000 --controller-ip=host0
|
||||
|
||||
and with literal examples of Python source code::
|
||||
|
||||
controller = dict(host='myhost',
|
||||
engine_port=None, # default is 10105
|
||||
control_port=None,
|
||||
)
|
||||
|
||||
# keys are hostnames, values are the number of engine on that host
|
||||
engines = dict(node1=2,
|
||||
node2=2,
|
||||
node3=2,
|
||||
node3=2,
|
||||
)
|
||||
|
||||
# Force failure to detect that this test is being run.
|
||||
1/0
|
||||
|
||||
These source code examples are executed but no output is compared at all. An
|
||||
error or failure is reported only if an exception is raised.
|
||||
|
||||
NOTE: the execution of pure python blocks is not yet working!
|
|
@ -0,0 +1,24 @@
|
|||
=====================================
|
||||
Tests in example form - pure python
|
||||
=====================================
|
||||
|
||||
This file contains doctest examples embedded as code blocks, using normal
|
||||
Python prompts. See the accompanying file for similar examples using IPython
|
||||
prompts (you can't mix both types within one file). The following will be run
|
||||
as a test::
|
||||
|
||||
>>> 1+1
|
||||
2
|
||||
>>> print ("hello")
|
||||
hello
|
||||
|
||||
More than one example works::
|
||||
|
||||
>>> s="Hello World"
|
||||
|
||||
>>> s.upper()
|
||||
'HELLO WORLD'
|
||||
|
||||
but you should note that the *entire* test file is considered to be a single
|
||||
test. Individual code blocks that fail are printed separately as ``example
|
||||
failures``, but the whole file is still counted and reported as one test.
|
|
@ -0,0 +1,30 @@
|
|||
=================================
|
||||
Tests in example form - IPython
|
||||
=================================
|
||||
|
||||
You can write text files with examples that use IPython prompts (as long as you
|
||||
use the nose ipython doctest plugin), but you can not mix and match prompt
|
||||
styles in a single file. That is, you either use all ``>>>`` prompts or all
|
||||
IPython-style prompts. Your test suite *can* have both types, you just need to
|
||||
put each type of example in a separate. Using IPython prompts, you can paste
|
||||
directly from your session::
|
||||
|
||||
In [5]: s="Hello World"
|
||||
|
||||
In [6]: s.upper()
|
||||
Out[6]: 'HELLO WORLD'
|
||||
|
||||
Another example::
|
||||
|
||||
In [8]: 1+3
|
||||
Out[8]: 4
|
||||
|
||||
Just like in IPython docstrings, you can use all IPython syntax and features::
|
||||
|
||||
In [9]: !echo "hello"
|
||||
hello
|
||||
|
||||
In [10]: a='hi'
|
||||
|
||||
In [11]: !echo $a
|
||||
hi
|
|
@ -0,0 +1,76 @@
|
|||
"""Tests for the ipdoctest machinery itself.
|
||||
|
||||
Note: in a file named test_X, functions whose only test is their docstring (as
|
||||
a doctest) and which have no test functionality of their own, should be called
|
||||
'doctest_foo' instead of 'test_foo', otherwise they get double-counted (the
|
||||
empty function call is counted as a test, which just inflates tests numbers
|
||||
artificially).
|
||||
"""
|
||||
|
||||
def doctest_simple():
|
||||
"""ipdoctest must handle simple inputs
|
||||
|
||||
In [1]: 1
|
||||
Out[1]: 1
|
||||
|
||||
In [2]: print(1)
|
||||
1
|
||||
"""
|
||||
|
||||
def doctest_multiline1():
|
||||
"""The ipdoctest machinery must handle multiline examples gracefully.
|
||||
|
||||
In [2]: for i in range(4):
|
||||
...: print(i)
|
||||
...:
|
||||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
"""
|
||||
|
||||
def doctest_multiline2():
|
||||
"""Multiline examples that define functions and print output.
|
||||
|
||||
In [7]: def f(x):
|
||||
...: return x+1
|
||||
...:
|
||||
|
||||
In [8]: f(1)
|
||||
Out[8]: 2
|
||||
|
||||
In [9]: def g(x):
|
||||
...: print('x is:',x)
|
||||
...:
|
||||
|
||||
In [10]: g(1)
|
||||
x is: 1
|
||||
|
||||
In [11]: g('hello')
|
||||
x is: hello
|
||||
"""
|
||||
|
||||
|
||||
def doctest_multiline3():
|
||||
"""Multiline examples with blank lines.
|
||||
|
||||
In [12]: def h(x):
|
||||
....: if x>1:
|
||||
....: return x**2
|
||||
....: # To leave a blank line in the input, you must mark it
|
||||
....: # with a comment character:
|
||||
....: #
|
||||
....: # otherwise the doctest parser gets confused.
|
||||
....: else:
|
||||
....: return -1
|
||||
....:
|
||||
|
||||
In [13]: h(5)
|
||||
Out[13]: 25
|
||||
|
||||
In [14]: h(1)
|
||||
Out[14]: -1
|
||||
|
||||
In [15]: h(0)
|
||||
Out[15]: -1
|
||||
"""
|
46
venv/Lib/site-packages/IPython/testing/plugin/test_refs.py
Normal file
46
venv/Lib/site-packages/IPython/testing/plugin/test_refs.py
Normal file
|
@ -0,0 +1,46 @@
|
|||
"""Some simple tests for the plugin while running scripts.
|
||||
"""
|
||||
# Module imports
|
||||
# Std lib
|
||||
import inspect
|
||||
|
||||
# Our own
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Testing functions
|
||||
|
||||
def test_trivial():
|
||||
"""A trivial passing test."""
|
||||
pass
|
||||
|
||||
def doctest_run():
|
||||
"""Test running a trivial script.
|
||||
|
||||
In [13]: run simplevars.py
|
||||
x is: 1
|
||||
"""
|
||||
|
||||
def doctest_runvars():
|
||||
"""Test that variables defined in scripts get loaded correctly via %run.
|
||||
|
||||
In [13]: run simplevars.py
|
||||
x is: 1
|
||||
|
||||
In [14]: x
|
||||
Out[14]: 1
|
||||
"""
|
||||
|
||||
def doctest_ivars():
|
||||
"""Test that variables defined interactively are picked up.
|
||||
In [5]: zz=1
|
||||
|
||||
In [6]: zz
|
||||
Out[6]: 1
|
||||
"""
|
||||
|
||||
def doctest_refs():
|
||||
"""DocTest reference holding issues when running scripts.
|
||||
|
||||
In [32]: run show_refs.py
|
||||
c referrers: [<... 'dict'>]
|
||||
"""
|
19
venv/Lib/site-packages/IPython/testing/skipdoctest.py
Normal file
19
venv/Lib/site-packages/IPython/testing/skipdoctest.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
"""Decorators marks that a doctest should be skipped.
|
||||
|
||||
The IPython.testing.decorators module triggers various extra imports, including
|
||||
numpy and sympy if they're present. Since this decorator is used in core parts
|
||||
of IPython, it's in a separate module so that running IPython doesn't trigger
|
||||
those imports."""
|
||||
|
||||
# Copyright (C) IPython Development Team
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
|
||||
def skip_doctest(f):
|
||||
"""Decorator - mark a function or method for skipping its doctest.
|
||||
|
||||
This decorator allows you to mark a function whose docstring you wish to
|
||||
omit from testing, while preserving the docstring for introspection, help,
|
||||
etc."""
|
||||
f.skip_doctest = True
|
||||
return f
|
10
venv/Lib/site-packages/IPython/testing/tests/__init__.py
Normal file
10
venv/Lib/site-packages/IPython/testing/tests/__init__.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
# encoding: utf-8
|
||||
__docformat__ = "restructuredtext en"
|
||||
#-------------------------------------------------------------------------------
|
||||
# Copyright (C) 2005 Fernando Perez <fperez@colorado.edu>
|
||||
# Brian E Granger <ellisonbg@gmail.com>
|
||||
# Benjamin Ragan-Kelley <benjaminrk@gmail.com>
|
||||
#
|
||||
# Distributed under the terms of the BSD License. The full license is in
|
||||
# the file COPYING, distributed as part of this software.
|
||||
#-------------------------------------------------------------------------------
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
164
venv/Lib/site-packages/IPython/testing/tests/test_decorators.py
Normal file
164
venv/Lib/site-packages/IPython/testing/tests/test_decorators.py
Normal file
|
@ -0,0 +1,164 @@
|
|||
"""Tests for the decorators we've created for IPython.
|
||||
"""
|
||||
|
||||
# Module imports
|
||||
# Std lib
|
||||
import inspect
|
||||
import sys
|
||||
|
||||
# Third party
|
||||
import nose.tools as nt
|
||||
|
||||
# Our own
|
||||
from IPython.testing import decorators as dec
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Utilities
|
||||
|
||||
# Note: copied from OInspect, kept here so the testing stuff doesn't create
|
||||
# circular dependencies and is easier to reuse.
|
||||
def getargspec(obj):
|
||||
"""Get the names and default values of a function's arguments.
|
||||
|
||||
A tuple of four things is returned: (args, varargs, varkw, defaults).
|
||||
'args' is a list of the argument names (it may contain nested lists).
|
||||
'varargs' and 'varkw' are the names of the * and ** arguments or None.
|
||||
'defaults' is an n-tuple of the default values of the last n arguments.
|
||||
|
||||
Modified version of inspect.getargspec from the Python Standard
|
||||
Library."""
|
||||
|
||||
if inspect.isfunction(obj):
|
||||
func_obj = obj
|
||||
elif inspect.ismethod(obj):
|
||||
func_obj = obj.__func__
|
||||
else:
|
||||
raise TypeError('arg is not a Python function')
|
||||
args, varargs, varkw = inspect.getargs(func_obj.__code__)
|
||||
return args, varargs, varkw, func_obj.__defaults__
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Testing functions
|
||||
|
||||
@dec.as_unittest
|
||||
def trivial():
|
||||
"""A trivial test"""
|
||||
pass
|
||||
|
||||
|
||||
@dec.skip()
|
||||
def test_deliberately_broken():
|
||||
"""A deliberately broken test - we want to skip this one."""
|
||||
1/0
|
||||
|
||||
@dec.skip('Testing the skip decorator')
|
||||
def test_deliberately_broken2():
|
||||
"""Another deliberately broken test - we want to skip this one."""
|
||||
1/0
|
||||
|
||||
|
||||
# Verify that we can correctly skip the doctest for a function at will, but
|
||||
# that the docstring itself is NOT destroyed by the decorator.
|
||||
def doctest_bad(x,y=1,**k):
|
||||
"""A function whose doctest we need to skip.
|
||||
|
||||
>>> 1+1
|
||||
3
|
||||
"""
|
||||
print('x:',x)
|
||||
print('y:',y)
|
||||
print('k:',k)
|
||||
|
||||
|
||||
def call_doctest_bad():
|
||||
"""Check that we can still call the decorated functions.
|
||||
|
||||
>>> doctest_bad(3,y=4)
|
||||
x: 3
|
||||
y: 4
|
||||
k: {}
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def test_skip_dt_decorator():
|
||||
"""Doctest-skipping decorator should preserve the docstring.
|
||||
"""
|
||||
# Careful: 'check' must be a *verbatim* copy of the doctest_bad docstring!
|
||||
check = """A function whose doctest we need to skip.
|
||||
|
||||
>>> 1+1
|
||||
3
|
||||
"""
|
||||
# Fetch the docstring from doctest_bad after decoration.
|
||||
val = doctest_bad.__doc__
|
||||
|
||||
nt.assert_equal(check,val,"doctest_bad docstrings don't match")
|
||||
|
||||
|
||||
# Doctest skipping should work for class methods too
|
||||
class FooClass(object):
|
||||
"""FooClass
|
||||
|
||||
Example:
|
||||
|
||||
>>> 1+1
|
||||
2
|
||||
"""
|
||||
|
||||
def __init__(self,x):
|
||||
"""Make a FooClass.
|
||||
|
||||
Example:
|
||||
|
||||
>>> f = FooClass(3)
|
||||
junk
|
||||
"""
|
||||
print('Making a FooClass.')
|
||||
self.x = x
|
||||
|
||||
def bar(self,y):
|
||||
"""Example:
|
||||
|
||||
>>> ff = FooClass(3)
|
||||
>>> ff.bar(0)
|
||||
boom!
|
||||
>>> 1/0
|
||||
bam!
|
||||
"""
|
||||
return 1/y
|
||||
|
||||
def baz(self,y):
|
||||
"""Example:
|
||||
|
||||
>>> ff2 = FooClass(3)
|
||||
Making a FooClass.
|
||||
>>> ff2.baz(3)
|
||||
True
|
||||
"""
|
||||
return self.x==y
|
||||
|
||||
|
||||
def test_skip_dt_decorator2():
|
||||
"""Doctest-skipping decorator should preserve function signature.
|
||||
"""
|
||||
# Hardcoded correct answer
|
||||
dtargs = (['x', 'y'], None, 'k', (1,))
|
||||
# Introspect out the value
|
||||
dtargsr = getargspec(doctest_bad)
|
||||
assert dtargsr==dtargs, \
|
||||
"Incorrectly reconstructed args for doctest_bad: %s" % (dtargsr,)
|
||||
|
||||
|
||||
@dec.skip_linux
|
||||
def test_linux():
|
||||
nt.assert_false(sys.platform.startswith('linux'),"This test can't run under linux")
|
||||
|
||||
@dec.skip_win32
|
||||
def test_win32():
|
||||
nt.assert_not_equal(sys.platform,'win32',"This test can't run under windows")
|
||||
|
||||
@dec.skip_osx
|
||||
def test_osx():
|
||||
nt.assert_not_equal(sys.platform,'darwin',"This test can't run under osx")
|
||||
|
131
venv/Lib/site-packages/IPython/testing/tests/test_ipunittest.py
Normal file
131
venv/Lib/site-packages/IPython/testing/tests/test_ipunittest.py
Normal file
|
@ -0,0 +1,131 @@
|
|||
"""Tests for IPython's test support utilities.
|
||||
|
||||
These are decorators that allow standalone functions and docstrings to be seen
|
||||
as tests by unittest, replicating some of nose's functionality. Additionally,
|
||||
IPython-syntax docstrings can be auto-converted to '>>>' so that ipython
|
||||
sessions can be copy-pasted as tests.
|
||||
|
||||
This file can be run as a script, and it will call unittest.main(). We must
|
||||
check that it works with unittest as well as with nose...
|
||||
|
||||
|
||||
Notes:
|
||||
|
||||
- Using nosetests --with-doctest --doctest-tests testfile.py
|
||||
will find docstrings as tests wherever they are, even in methods. But
|
||||
if we use ipython syntax in the docstrings, they must be decorated with
|
||||
@ipdocstring. This is OK for test-only code, but not for user-facing
|
||||
docstrings where we want to keep the ipython syntax.
|
||||
|
||||
- Using nosetests --with-doctest file.py
|
||||
also finds doctests if the file name doesn't have 'test' in it, because it is
|
||||
treated like a normal module. But if nose treats the file like a test file,
|
||||
then for normal classes to be doctested the extra --doctest-tests is
|
||||
necessary.
|
||||
|
||||
- running this script with python (it has a __main__ section at the end) misses
|
||||
one docstring test, the one embedded in the Foo object method. Since our
|
||||
approach relies on using decorators that create standalone TestCase
|
||||
instances, it can only be used for functions, not for methods of objects.
|
||||
Authors
|
||||
-------
|
||||
|
||||
- Fernando Perez <Fernando.Perez@berkeley.edu>
|
||||
"""
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (C) 2009-2011 The IPython Development Team
|
||||
#
|
||||
# Distributed under the terms of the BSD License. The full license is in
|
||||
# the file COPYING, distributed as part of this software.
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Imports
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
from IPython.testing.ipunittest import ipdoctest, ipdocstring
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Test classes and functions
|
||||
#-----------------------------------------------------------------------------
|
||||
@ipdoctest
|
||||
def simple_dt():
|
||||
"""
|
||||
>>> print(1+1)
|
||||
2
|
||||
"""
|
||||
|
||||
|
||||
@ipdoctest
|
||||
def ipdt_flush():
|
||||
"""
|
||||
In [20]: print(1)
|
||||
1
|
||||
|
||||
In [26]: for i in range(4):
|
||||
....: print(i)
|
||||
....:
|
||||
....:
|
||||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
|
||||
In [27]: 3+4
|
||||
Out[27]: 7
|
||||
"""
|
||||
|
||||
|
||||
@ipdoctest
|
||||
def ipdt_indented_test():
|
||||
"""
|
||||
In [20]: print(1)
|
||||
1
|
||||
|
||||
In [26]: for i in range(4):
|
||||
....: print(i)
|
||||
....:
|
||||
....:
|
||||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
|
||||
In [27]: 3+4
|
||||
Out[27]: 7
|
||||
"""
|
||||
|
||||
|
||||
class Foo(object):
|
||||
"""For methods, the normal decorator doesn't work.
|
||||
|
||||
But rewriting the docstring with ip2py does, *but only if using nose
|
||||
--with-doctest*. Do we want to have that as a dependency?
|
||||
"""
|
||||
|
||||
@ipdocstring
|
||||
def ipdt_method(self):
|
||||
"""
|
||||
In [20]: print(1)
|
||||
1
|
||||
|
||||
In [26]: for i in range(4):
|
||||
....: print(i)
|
||||
....:
|
||||
....:
|
||||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
|
||||
In [27]: 3+4
|
||||
Out[27]: 7
|
||||
"""
|
||||
|
||||
def normaldt_method(self):
|
||||
"""
|
||||
>>> print(1+1)
|
||||
2
|
||||
"""
|
135
venv/Lib/site-packages/IPython/testing/tests/test_tools.py
Normal file
135
venv/Lib/site-packages/IPython/testing/tests/test_tools.py
Normal file
|
@ -0,0 +1,135 @@
|
|||
# encoding: utf-8
|
||||
"""
|
||||
Tests for testing.tools
|
||||
"""
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (C) 2008-2011 The IPython Development Team
|
||||
#
|
||||
# Distributed under the terms of the BSD License. The full license is in
|
||||
# the file COPYING, distributed as part of this software.
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Imports
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import unittest
|
||||
|
||||
import nose.tools as nt
|
||||
|
||||
from IPython.testing import decorators as dec
|
||||
from IPython.testing import tools as tt
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Tests
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
@dec.skip_win32
|
||||
def test_full_path_posix():
|
||||
spath = '/foo/bar.py'
|
||||
result = tt.full_path(spath,['a.txt','b.txt'])
|
||||
nt.assert_equal(result, ['/foo/a.txt', '/foo/b.txt'])
|
||||
spath = '/foo'
|
||||
result = tt.full_path(spath,['a.txt','b.txt'])
|
||||
nt.assert_equal(result, ['/a.txt', '/b.txt'])
|
||||
result = tt.full_path(spath,'a.txt')
|
||||
nt.assert_equal(result, ['/a.txt'])
|
||||
|
||||
|
||||
@dec.skip_if_not_win32
|
||||
def test_full_path_win32():
|
||||
spath = 'c:\\foo\\bar.py'
|
||||
result = tt.full_path(spath,['a.txt','b.txt'])
|
||||
nt.assert_equal(result, ['c:\\foo\\a.txt', 'c:\\foo\\b.txt'])
|
||||
spath = 'c:\\foo'
|
||||
result = tt.full_path(spath,['a.txt','b.txt'])
|
||||
nt.assert_equal(result, ['c:\\a.txt', 'c:\\b.txt'])
|
||||
result = tt.full_path(spath,'a.txt')
|
||||
nt.assert_equal(result, ['c:\\a.txt'])
|
||||
|
||||
|
||||
def test_parser():
|
||||
err = ("FAILED (errors=1)", 1, 0)
|
||||
fail = ("FAILED (failures=1)", 0, 1)
|
||||
both = ("FAILED (errors=1, failures=1)", 1, 1)
|
||||
for txt, nerr, nfail in [err, fail, both]:
|
||||
nerr1, nfail1 = tt.parse_test_output(txt)
|
||||
nt.assert_equal(nerr, nerr1)
|
||||
nt.assert_equal(nfail, nfail1)
|
||||
|
||||
|
||||
def test_temp_pyfile():
|
||||
src = 'pass\n'
|
||||
fname = tt.temp_pyfile(src)
|
||||
assert os.path.isfile(fname)
|
||||
with open(fname) as fh2:
|
||||
src2 = fh2.read()
|
||||
nt.assert_equal(src2, src)
|
||||
|
||||
class TestAssertPrints(unittest.TestCase):
|
||||
def test_passing(self):
|
||||
with tt.AssertPrints("abc"):
|
||||
print("abcd")
|
||||
print("def")
|
||||
print(b"ghi")
|
||||
|
||||
def test_failing(self):
|
||||
def func():
|
||||
with tt.AssertPrints("abc"):
|
||||
print("acd")
|
||||
print("def")
|
||||
print(b"ghi")
|
||||
|
||||
self.assertRaises(AssertionError, func)
|
||||
|
||||
|
||||
class Test_ipexec_validate(tt.TempFileMixin):
|
||||
def test_main_path(self):
|
||||
"""Test with only stdout results.
|
||||
"""
|
||||
self.mktmp("print('A')\n"
|
||||
"print('B')\n"
|
||||
)
|
||||
out = "A\nB"
|
||||
tt.ipexec_validate(self.fname, out)
|
||||
|
||||
def test_main_path2(self):
|
||||
"""Test with only stdout results, expecting windows line endings.
|
||||
"""
|
||||
self.mktmp("print('A')\n"
|
||||
"print('B')\n"
|
||||
)
|
||||
out = "A\r\nB"
|
||||
tt.ipexec_validate(self.fname, out)
|
||||
|
||||
def test_exception_path(self):
|
||||
"""Test exception path in exception_validate.
|
||||
"""
|
||||
self.mktmp("import sys\n"
|
||||
"print('A')\n"
|
||||
"print('B')\n"
|
||||
"print('C', file=sys.stderr)\n"
|
||||
"print('D', file=sys.stderr)\n"
|
||||
)
|
||||
out = "A\nB"
|
||||
tt.ipexec_validate(self.fname, expected_out=out, expected_err="C\nD")
|
||||
|
||||
def test_exception_path2(self):
|
||||
"""Test exception path in exception_validate, expecting windows line endings.
|
||||
"""
|
||||
self.mktmp("import sys\n"
|
||||
"print('A')\n"
|
||||
"print('B')\n"
|
||||
"print('C', file=sys.stderr)\n"
|
||||
"print('D', file=sys.stderr)\n"
|
||||
)
|
||||
out = "A\r\nB"
|
||||
tt.ipexec_validate(self.fname, expected_out=out, expected_err="C\r\nD")
|
||||
|
||||
|
||||
def tearDown(self):
|
||||
# tear down correctly the mixin,
|
||||
# unittest.TestCase.tearDown does nothing
|
||||
tt.TempFileMixin.tearDown(self)
|
471
venv/Lib/site-packages/IPython/testing/tools.py
Normal file
471
venv/Lib/site-packages/IPython/testing/tools.py
Normal file
|
@ -0,0 +1,471 @@
|
|||
"""Generic testing tools.
|
||||
|
||||
Authors
|
||||
-------
|
||||
- Fernando Perez <Fernando.Perez@berkeley.edu>
|
||||
"""
|
||||
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from contextlib import contextmanager
|
||||
from io import StringIO
|
||||
from subprocess import Popen, PIPE
|
||||
from unittest.mock import patch
|
||||
|
||||
try:
|
||||
# These tools are used by parts of the runtime, so we make the nose
|
||||
# dependency optional at this point. Nose is a hard dependency to run the
|
||||
# test suite, but NOT to use ipython itself.
|
||||
import nose.tools as nt
|
||||
has_nose = True
|
||||
except ImportError:
|
||||
has_nose = False
|
||||
|
||||
from traitlets.config.loader import Config
|
||||
from IPython.utils.process import get_output_error_code
|
||||
from IPython.utils.text import list_strings
|
||||
from IPython.utils.io import temp_pyfile, Tee
|
||||
from IPython.utils import py3compat
|
||||
|
||||
from . import decorators as dec
|
||||
from . import skipdoctest
|
||||
|
||||
|
||||
# The docstring for full_path doctests differently on win32 (different path
|
||||
# separator) so just skip the doctest there. The example remains informative.
|
||||
doctest_deco = skipdoctest.skip_doctest if sys.platform == 'win32' else dec.null_deco
|
||||
|
||||
@doctest_deco
|
||||
def full_path(startPath,files):
|
||||
"""Make full paths for all the listed files, based on startPath.
|
||||
|
||||
Only the base part of startPath is kept, since this routine is typically
|
||||
used with a script's ``__file__`` variable as startPath. The base of startPath
|
||||
is then prepended to all the listed files, forming the output list.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
startPath : string
|
||||
Initial path to use as the base for the results. This path is split
|
||||
using os.path.split() and only its first component is kept.
|
||||
|
||||
files : string or list
|
||||
One or more files.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
>>> full_path('/foo/bar.py',['a.txt','b.txt'])
|
||||
['/foo/a.txt', '/foo/b.txt']
|
||||
|
||||
>>> full_path('/foo',['a.txt','b.txt'])
|
||||
['/a.txt', '/b.txt']
|
||||
|
||||
If a single file is given, the output is still a list::
|
||||
|
||||
>>> full_path('/foo','a.txt')
|
||||
['/a.txt']
|
||||
"""
|
||||
|
||||
files = list_strings(files)
|
||||
base = os.path.split(startPath)[0]
|
||||
return [ os.path.join(base,f) for f in files ]
|
||||
|
||||
|
||||
def parse_test_output(txt):
|
||||
"""Parse the output of a test run and return errors, failures.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
txt : str
|
||||
Text output of a test run, assumed to contain a line of one of the
|
||||
following forms::
|
||||
|
||||
'FAILED (errors=1)'
|
||||
'FAILED (failures=1)'
|
||||
'FAILED (errors=1, failures=1)'
|
||||
|
||||
Returns
|
||||
-------
|
||||
nerr, nfail
|
||||
number of errors and failures.
|
||||
"""
|
||||
|
||||
err_m = re.search(r'^FAILED \(errors=(\d+)\)', txt, re.MULTILINE)
|
||||
if err_m:
|
||||
nerr = int(err_m.group(1))
|
||||
nfail = 0
|
||||
return nerr, nfail
|
||||
|
||||
fail_m = re.search(r'^FAILED \(failures=(\d+)\)', txt, re.MULTILINE)
|
||||
if fail_m:
|
||||
nerr = 0
|
||||
nfail = int(fail_m.group(1))
|
||||
return nerr, nfail
|
||||
|
||||
both_m = re.search(r'^FAILED \(errors=(\d+), failures=(\d+)\)', txt,
|
||||
re.MULTILINE)
|
||||
if both_m:
|
||||
nerr = int(both_m.group(1))
|
||||
nfail = int(both_m.group(2))
|
||||
return nerr, nfail
|
||||
|
||||
# If the input didn't match any of these forms, assume no error/failures
|
||||
return 0, 0
|
||||
|
||||
|
||||
# So nose doesn't think this is a test
|
||||
parse_test_output.__test__ = False
|
||||
|
||||
|
||||
def default_argv():
|
||||
"""Return a valid default argv for creating testing instances of ipython"""
|
||||
|
||||
return ['--quick', # so no config file is loaded
|
||||
# Other defaults to minimize side effects on stdout
|
||||
'--colors=NoColor', '--no-term-title','--no-banner',
|
||||
'--autocall=0']
|
||||
|
||||
|
||||
def default_config():
|
||||
"""Return a config object with good defaults for testing."""
|
||||
config = Config()
|
||||
config.TerminalInteractiveShell.colors = 'NoColor'
|
||||
config.TerminalTerminalInteractiveShell.term_title = False,
|
||||
config.TerminalInteractiveShell.autocall = 0
|
||||
f = tempfile.NamedTemporaryFile(suffix=u'test_hist.sqlite', delete=False)
|
||||
config.HistoryManager.hist_file = f.name
|
||||
f.close()
|
||||
config.HistoryManager.db_cache_size = 10000
|
||||
return config
|
||||
|
||||
|
||||
def get_ipython_cmd(as_string=False):
|
||||
"""
|
||||
Return appropriate IPython command line name. By default, this will return
|
||||
a list that can be used with subprocess.Popen, for example, but passing
|
||||
`as_string=True` allows for returning the IPython command as a string.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
as_string: bool
|
||||
Flag to allow to return the command as a string.
|
||||
"""
|
||||
ipython_cmd = [sys.executable, "-m", "IPython"]
|
||||
|
||||
if as_string:
|
||||
ipython_cmd = " ".join(ipython_cmd)
|
||||
|
||||
return ipython_cmd
|
||||
|
||||
def ipexec(fname, options=None, commands=()):
|
||||
"""Utility to call 'ipython filename'.
|
||||
|
||||
Starts IPython with a minimal and safe configuration to make startup as fast
|
||||
as possible.
|
||||
|
||||
Note that this starts IPython in a subprocess!
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fname : str
|
||||
Name of file to be executed (should have .py or .ipy extension).
|
||||
|
||||
options : optional, list
|
||||
Extra command-line flags to be passed to IPython.
|
||||
|
||||
commands : optional, list
|
||||
Commands to send in on stdin
|
||||
|
||||
Returns
|
||||
-------
|
||||
``(stdout, stderr)`` of ipython subprocess.
|
||||
"""
|
||||
if options is None: options = []
|
||||
|
||||
cmdargs = default_argv() + options
|
||||
|
||||
test_dir = os.path.dirname(__file__)
|
||||
|
||||
ipython_cmd = get_ipython_cmd()
|
||||
# Absolute path for filename
|
||||
full_fname = os.path.join(test_dir, fname)
|
||||
full_cmd = ipython_cmd + cmdargs + [full_fname]
|
||||
env = os.environ.copy()
|
||||
# FIXME: ignore all warnings in ipexec while we have shims
|
||||
# should we keep suppressing warnings here, even after removing shims?
|
||||
env['PYTHONWARNINGS'] = 'ignore'
|
||||
# env.pop('PYTHONWARNINGS', None) # Avoid extraneous warnings appearing on stderr
|
||||
for k, v in env.items():
|
||||
# Debug a bizarre failure we've seen on Windows:
|
||||
# TypeError: environment can only contain strings
|
||||
if not isinstance(v, str):
|
||||
print(k, v)
|
||||
p = Popen(full_cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE, env=env)
|
||||
out, err = p.communicate(input=py3compat.encode('\n'.join(commands)) or None)
|
||||
out, err = py3compat.decode(out), py3compat.decode(err)
|
||||
# `import readline` causes 'ESC[?1034h' to be output sometimes,
|
||||
# so strip that out before doing comparisons
|
||||
if out:
|
||||
out = re.sub(r'\x1b\[[^h]+h', '', out)
|
||||
return out, err
|
||||
|
||||
|
||||
def ipexec_validate(fname, expected_out, expected_err='',
|
||||
options=None, commands=()):
|
||||
"""Utility to call 'ipython filename' and validate output/error.
|
||||
|
||||
This function raises an AssertionError if the validation fails.
|
||||
|
||||
Note that this starts IPython in a subprocess!
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fname : str
|
||||
Name of the file to be executed (should have .py or .ipy extension).
|
||||
|
||||
expected_out : str
|
||||
Expected stdout of the process.
|
||||
|
||||
expected_err : optional, str
|
||||
Expected stderr of the process.
|
||||
|
||||
options : optional, list
|
||||
Extra command-line flags to be passed to IPython.
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
|
||||
import nose.tools as nt
|
||||
|
||||
out, err = ipexec(fname, options, commands)
|
||||
#print 'OUT', out # dbg
|
||||
#print 'ERR', err # dbg
|
||||
# If there are any errors, we must check those before stdout, as they may be
|
||||
# more informative than simply having an empty stdout.
|
||||
if err:
|
||||
if expected_err:
|
||||
nt.assert_equal("\n".join(err.strip().splitlines()), "\n".join(expected_err.strip().splitlines()))
|
||||
else:
|
||||
raise ValueError('Running file %r produced error: %r' %
|
||||
(fname, err))
|
||||
# If no errors or output on stderr was expected, match stdout
|
||||
nt.assert_equal("\n".join(out.strip().splitlines()), "\n".join(expected_out.strip().splitlines()))
|
||||
|
||||
|
||||
class TempFileMixin(unittest.TestCase):
|
||||
"""Utility class to create temporary Python/IPython files.
|
||||
|
||||
Meant as a mixin class for test cases."""
|
||||
|
||||
def mktmp(self, src, ext='.py'):
|
||||
"""Make a valid python temp file."""
|
||||
fname = temp_pyfile(src, ext)
|
||||
if not hasattr(self, 'tmps'):
|
||||
self.tmps=[]
|
||||
self.tmps.append(fname)
|
||||
self.fname = fname
|
||||
|
||||
def tearDown(self):
|
||||
# If the tmpfile wasn't made because of skipped tests, like in
|
||||
# win32, there's nothing to cleanup.
|
||||
if hasattr(self, 'tmps'):
|
||||
for fname in self.tmps:
|
||||
# If the tmpfile wasn't made because of skipped tests, like in
|
||||
# win32, there's nothing to cleanup.
|
||||
try:
|
||||
os.unlink(fname)
|
||||
except:
|
||||
# On Windows, even though we close the file, we still can't
|
||||
# delete it. I have no clue why
|
||||
pass
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
self.tearDown()
|
||||
|
||||
|
||||
pair_fail_msg = ("Testing {0}\n\n"
|
||||
"In:\n"
|
||||
" {1!r}\n"
|
||||
"Expected:\n"
|
||||
" {2!r}\n"
|
||||
"Got:\n"
|
||||
" {3!r}\n")
|
||||
def check_pairs(func, pairs):
|
||||
"""Utility function for the common case of checking a function with a
|
||||
sequence of input/output pairs.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
func : callable
|
||||
The function to be tested. Should accept a single argument.
|
||||
pairs : iterable
|
||||
A list of (input, expected_output) tuples.
|
||||
|
||||
Returns
|
||||
-------
|
||||
None. Raises an AssertionError if any output does not match the expected
|
||||
value.
|
||||
"""
|
||||
name = getattr(func, "func_name", getattr(func, "__name__", "<unknown>"))
|
||||
for inp, expected in pairs:
|
||||
out = func(inp)
|
||||
assert out == expected, pair_fail_msg.format(name, inp, expected, out)
|
||||
|
||||
|
||||
MyStringIO = StringIO
|
||||
|
||||
_re_type = type(re.compile(r''))
|
||||
|
||||
notprinted_msg = """Did not find {0!r} in printed output (on {1}):
|
||||
-------
|
||||
{2!s}
|
||||
-------
|
||||
"""
|
||||
|
||||
class AssertPrints(object):
|
||||
"""Context manager for testing that code prints certain text.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> with AssertPrints("abc", suppress=False):
|
||||
... print("abcd")
|
||||
... print("def")
|
||||
...
|
||||
abcd
|
||||
def
|
||||
"""
|
||||
def __init__(self, s, channel='stdout', suppress=True):
|
||||
self.s = s
|
||||
if isinstance(self.s, (str, _re_type)):
|
||||
self.s = [self.s]
|
||||
self.channel = channel
|
||||
self.suppress = suppress
|
||||
|
||||
def __enter__(self):
|
||||
self.orig_stream = getattr(sys, self.channel)
|
||||
self.buffer = MyStringIO()
|
||||
self.tee = Tee(self.buffer, channel=self.channel)
|
||||
setattr(sys, self.channel, self.buffer if self.suppress else self.tee)
|
||||
|
||||
def __exit__(self, etype, value, traceback):
|
||||
try:
|
||||
if value is not None:
|
||||
# If an error was raised, don't check anything else
|
||||
return False
|
||||
self.tee.flush()
|
||||
setattr(sys, self.channel, self.orig_stream)
|
||||
printed = self.buffer.getvalue()
|
||||
for s in self.s:
|
||||
if isinstance(s, _re_type):
|
||||
assert s.search(printed), notprinted_msg.format(s.pattern, self.channel, printed)
|
||||
else:
|
||||
assert s in printed, notprinted_msg.format(s, self.channel, printed)
|
||||
return False
|
||||
finally:
|
||||
self.tee.close()
|
||||
|
||||
printed_msg = """Found {0!r} in printed output (on {1}):
|
||||
-------
|
||||
{2!s}
|
||||
-------
|
||||
"""
|
||||
|
||||
class AssertNotPrints(AssertPrints):
|
||||
"""Context manager for checking that certain output *isn't* produced.
|
||||
|
||||
Counterpart of AssertPrints"""
|
||||
def __exit__(self, etype, value, traceback):
|
||||
try:
|
||||
if value is not None:
|
||||
# If an error was raised, don't check anything else
|
||||
self.tee.close()
|
||||
return False
|
||||
self.tee.flush()
|
||||
setattr(sys, self.channel, self.orig_stream)
|
||||
printed = self.buffer.getvalue()
|
||||
for s in self.s:
|
||||
if isinstance(s, _re_type):
|
||||
assert not s.search(printed),printed_msg.format(
|
||||
s.pattern, self.channel, printed)
|
||||
else:
|
||||
assert s not in printed, printed_msg.format(
|
||||
s, self.channel, printed)
|
||||
return False
|
||||
finally:
|
||||
self.tee.close()
|
||||
|
||||
@contextmanager
|
||||
def mute_warn():
|
||||
from IPython.utils import warn
|
||||
save_warn = warn.warn
|
||||
warn.warn = lambda *a, **kw: None
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
warn.warn = save_warn
|
||||
|
||||
@contextmanager
|
||||
def make_tempfile(name):
|
||||
""" Create an empty, named, temporary file for the duration of the context.
|
||||
"""
|
||||
open(name, 'w').close()
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
os.unlink(name)
|
||||
|
||||
def fake_input(inputs):
|
||||
"""Temporarily replace the input() function to return the given values
|
||||
|
||||
Use as a context manager:
|
||||
|
||||
with fake_input(['result1', 'result2']):
|
||||
...
|
||||
|
||||
Values are returned in order. If input() is called again after the last value
|
||||
was used, EOFError is raised.
|
||||
"""
|
||||
it = iter(inputs)
|
||||
def mock_input(prompt=''):
|
||||
try:
|
||||
return next(it)
|
||||
except StopIteration:
|
||||
raise EOFError('No more inputs given')
|
||||
|
||||
return patch('builtins.input', mock_input)
|
||||
|
||||
def help_output_test(subcommand=''):
|
||||
"""test that `ipython [subcommand] -h` works"""
|
||||
cmd = get_ipython_cmd() + [subcommand, '-h']
|
||||
out, err, rc = get_output_error_code(cmd)
|
||||
nt.assert_equal(rc, 0, err)
|
||||
nt.assert_not_in("Traceback", err)
|
||||
nt.assert_in("Options", out)
|
||||
nt.assert_in("--help-all", out)
|
||||
return out, err
|
||||
|
||||
|
||||
def help_all_output_test(subcommand=''):
|
||||
"""test that `ipython [subcommand] --help-all` works"""
|
||||
cmd = get_ipython_cmd() + [subcommand, '--help-all']
|
||||
out, err, rc = get_output_error_code(cmd)
|
||||
nt.assert_equal(rc, 0, err)
|
||||
nt.assert_not_in("Traceback", err)
|
||||
nt.assert_in("Options", out)
|
||||
nt.assert_in("Class", out)
|
||||
return out, err
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue