mirror of
https://github.com/aykhans/AzSuicideDataVisualization.git
synced 2025-07-01 22:13:01 +00:00
first commit
This commit is contained in:
20
.venv/Lib/site-packages/IPython/testing/__init__.py
Normal file
20
.venv/Lib/site-packages/IPython/testing/__init__.py
Normal file
@ -0,0 +1,20 @@
|
||||
"""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
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# 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))
|
202
.venv/Lib/site-packages/IPython/testing/decorators.py
Normal file
202
.venv/Lib/site-packages/IPython/testing/decorators.py
Normal file
@ -0,0 +1,202 @@
|
||||
# -*- 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
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# 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 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.
|
||||
"""
|
||||
if msg is None:
|
||||
msg = "Test skipped due to test condition."
|
||||
|
||||
import pytest
|
||||
|
||||
assert isinstance(skip_condition, bool)
|
||||
return pytest.mark.skipif(skip_condition, reason=msg)
|
||||
|
||||
|
||||
# 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."""
|
||||
|
||||
return skipif(not 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
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# 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")
|
||||
|
||||
_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)
|
||||
|
||||
# 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')
|
||||
|
||||
# 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.
|
||||
"""
|
||||
assert (
|
||||
os.environ.get("IPTEST_WORKING_DIR", None) is None
|
||||
), "iptest deprecated since IPython 8.0"
|
||||
for cmd in commands:
|
||||
reason = f"This test runs only if command '{cmd}' is installed"
|
||||
if not shutil.which(cmd):
|
||||
import pytest
|
||||
|
||||
return pytest.mark.skip(reason=reason)
|
||||
return null_deco
|
115
.venv/Lib/site-packages/IPython/testing/globalipapp.py
Normal file
115
.venv/Lib/site-packages/IPython/testing/globalipapp.py
Normal file
@ -0,0 +1,115 @@
|
||||
"""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 pathlib import Path
|
||||
|
||||
from . import tools
|
||||
|
||||
from IPython.core import page
|
||||
from IPython.utils import io
|
||||
from IPython.terminal.interactiveshell import TerminalInteractiveShell
|
||||
|
||||
|
||||
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(Path(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
|
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
|
||||
|
167
.venv/Lib/site-packages/IPython/testing/plugin/dtexample.py
Normal file
167
.venv/Lib/site-packages/IPython/testing/plugin/dtexample.py
Normal file
@ -0,0 +1,167 @@
|
||||
"""Simple example using doctests.
|
||||
|
||||
This file just contains doctests both using plain python and IPython prompts.
|
||||
All tests should be loaded by nose.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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 ipos():
|
||||
"""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
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
ipos.__skip_doctest__ = os.name == "nt"
|
||||
|
||||
|
||||
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'
|
300
.venv/Lib/site-packages/IPython/testing/plugin/ipdoctest.py
Normal file
300
.venv/Lib/site-packages/IPython/testing/plugin/ipdoctest.py
Normal file
@ -0,0 +1,300 @@
|
||||
"""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 doctest
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
|
||||
from testpath import modified_env
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Module globals and other constants
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Classes and functions
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
class DocTestFinder(doctest.DocTestFinder):
|
||||
def _get_test(self, obj, name, module, globs, source_lines):
|
||||
test = super()._get_test(obj, name, module, globs, source_lines)
|
||||
|
||||
if bool(getattr(obj, "__skip_doctest__", False)) and test is not None:
|
||||
for example in test.examples:
|
||||
example.options[doctest.SKIP] = True
|
||||
|
||||
return test
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
# 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 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+')
|
||||
|
||||
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
|
||||
Example = doctest.Example
|
||||
else:
|
||||
# It's an ipython example.
|
||||
terms = list(self._EXAMPLE_RE_IP.finditer(string))
|
||||
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
|
||||
|
||||
# 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):
|
||||
# Override terminal size to standardise traceback format
|
||||
with modified_env({'COLUMNS': '80', 'LINES': '24'}):
|
||||
return super(IPDocTestRunner,self).run(test,
|
||||
compileflags,out,clear_globs)
|
@ -0,0 +1,860 @@
|
||||
# Based on Pytest doctest.py
|
||||
# Original license:
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2004-2021 Holger Krekel and others
|
||||
"""Discover and run ipdoctests in modules and test files."""
|
||||
import builtins
|
||||
import bdb
|
||||
import inspect
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
import traceback
|
||||
import types
|
||||
import warnings
|
||||
from contextlib import contextmanager
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from typing import Callable
|
||||
from typing import Dict
|
||||
from typing import Generator
|
||||
from typing import Iterable
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Pattern
|
||||
from typing import Sequence
|
||||
from typing import Tuple
|
||||
from typing import Type
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import Union
|
||||
|
||||
import pytest
|
||||
from _pytest import outcomes
|
||||
from _pytest._code.code import ExceptionInfo
|
||||
from _pytest._code.code import ReprFileLocation
|
||||
from _pytest._code.code import TerminalRepr
|
||||
from _pytest._io import TerminalWriter
|
||||
from _pytest.compat import safe_getattr
|
||||
from _pytest.config import Config
|
||||
from _pytest.config.argparsing import Parser
|
||||
from _pytest.fixtures import FixtureRequest
|
||||
from _pytest.nodes import Collector
|
||||
from _pytest.outcomes import OutcomeException
|
||||
from _pytest.pathlib import fnmatch_ex
|
||||
from _pytest.pathlib import import_path
|
||||
from _pytest.python_api import approx
|
||||
from _pytest.warning_types import PytestWarning
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import doctest
|
||||
|
||||
DOCTEST_REPORT_CHOICE_NONE = "none"
|
||||
DOCTEST_REPORT_CHOICE_CDIFF = "cdiff"
|
||||
DOCTEST_REPORT_CHOICE_NDIFF = "ndiff"
|
||||
DOCTEST_REPORT_CHOICE_UDIFF = "udiff"
|
||||
DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE = "only_first_failure"
|
||||
|
||||
DOCTEST_REPORT_CHOICES = (
|
||||
DOCTEST_REPORT_CHOICE_NONE,
|
||||
DOCTEST_REPORT_CHOICE_CDIFF,
|
||||
DOCTEST_REPORT_CHOICE_NDIFF,
|
||||
DOCTEST_REPORT_CHOICE_UDIFF,
|
||||
DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE,
|
||||
)
|
||||
|
||||
# Lazy definition of runner class
|
||||
RUNNER_CLASS = None
|
||||
# Lazy definition of output checker class
|
||||
CHECKER_CLASS: Optional[Type["IPDoctestOutputChecker"]] = None
|
||||
|
||||
|
||||
def pytest_addoption(parser: Parser) -> None:
|
||||
parser.addini(
|
||||
"ipdoctest_optionflags",
|
||||
"option flags for ipdoctests",
|
||||
type="args",
|
||||
default=["ELLIPSIS"],
|
||||
)
|
||||
parser.addini(
|
||||
"ipdoctest_encoding", "encoding used for ipdoctest files", default="utf-8"
|
||||
)
|
||||
group = parser.getgroup("collect")
|
||||
group.addoption(
|
||||
"--ipdoctest-modules",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="run ipdoctests in all .py modules",
|
||||
dest="ipdoctestmodules",
|
||||
)
|
||||
group.addoption(
|
||||
"--ipdoctest-report",
|
||||
type=str.lower,
|
||||
default="udiff",
|
||||
help="choose another output format for diffs on ipdoctest failure",
|
||||
choices=DOCTEST_REPORT_CHOICES,
|
||||
dest="ipdoctestreport",
|
||||
)
|
||||
group.addoption(
|
||||
"--ipdoctest-glob",
|
||||
action="append",
|
||||
default=[],
|
||||
metavar="pat",
|
||||
help="ipdoctests file matching pattern, default: test*.txt",
|
||||
dest="ipdoctestglob",
|
||||
)
|
||||
group.addoption(
|
||||
"--ipdoctest-ignore-import-errors",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="ignore ipdoctest ImportErrors",
|
||||
dest="ipdoctest_ignore_import_errors",
|
||||
)
|
||||
group.addoption(
|
||||
"--ipdoctest-continue-on-failure",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="for a given ipdoctest, continue to run after the first failure",
|
||||
dest="ipdoctest_continue_on_failure",
|
||||
)
|
||||
|
||||
|
||||
def pytest_unconfigure() -> None:
|
||||
global RUNNER_CLASS
|
||||
|
||||
RUNNER_CLASS = None
|
||||
|
||||
|
||||
def pytest_collect_file(
|
||||
file_path: Path,
|
||||
parent: Collector,
|
||||
) -> Optional[Union["IPDoctestModule", "IPDoctestTextfile"]]:
|
||||
config = parent.config
|
||||
if file_path.suffix == ".py":
|
||||
if config.option.ipdoctestmodules and not any(
|
||||
(_is_setup_py(file_path), _is_main_py(file_path))
|
||||
):
|
||||
mod: IPDoctestModule = IPDoctestModule.from_parent(parent, path=file_path)
|
||||
return mod
|
||||
elif _is_ipdoctest(config, file_path, parent):
|
||||
txt: IPDoctestTextfile = IPDoctestTextfile.from_parent(parent, path=file_path)
|
||||
return txt
|
||||
return None
|
||||
|
||||
|
||||
if int(pytest.__version__.split(".")[0]) < 7:
|
||||
_collect_file = pytest_collect_file
|
||||
|
||||
def pytest_collect_file(
|
||||
path,
|
||||
parent: Collector,
|
||||
) -> Optional[Union["IPDoctestModule", "IPDoctestTextfile"]]:
|
||||
return _collect_file(Path(path), parent)
|
||||
|
||||
_import_path = import_path
|
||||
|
||||
def import_path(path, root):
|
||||
import py.path
|
||||
|
||||
return _import_path(py.path.local(path))
|
||||
|
||||
|
||||
def _is_setup_py(path: Path) -> bool:
|
||||
if path.name != "setup.py":
|
||||
return False
|
||||
contents = path.read_bytes()
|
||||
return b"setuptools" in contents or b"distutils" in contents
|
||||
|
||||
|
||||
def _is_ipdoctest(config: Config, path: Path, parent: Collector) -> bool:
|
||||
if path.suffix in (".txt", ".rst") and parent.session.isinitpath(path):
|
||||
return True
|
||||
globs = config.getoption("ipdoctestglob") or ["test*.txt"]
|
||||
return any(fnmatch_ex(glob, path) for glob in globs)
|
||||
|
||||
|
||||
def _is_main_py(path: Path) -> bool:
|
||||
return path.name == "__main__.py"
|
||||
|
||||
|
||||
class ReprFailDoctest(TerminalRepr):
|
||||
def __init__(
|
||||
self, reprlocation_lines: Sequence[Tuple[ReprFileLocation, Sequence[str]]]
|
||||
) -> None:
|
||||
self.reprlocation_lines = reprlocation_lines
|
||||
|
||||
def toterminal(self, tw: TerminalWriter) -> None:
|
||||
for reprlocation, lines in self.reprlocation_lines:
|
||||
for line in lines:
|
||||
tw.line(line)
|
||||
reprlocation.toterminal(tw)
|
||||
|
||||
|
||||
class MultipleDoctestFailures(Exception):
|
||||
def __init__(self, failures: Sequence["doctest.DocTestFailure"]) -> None:
|
||||
super().__init__()
|
||||
self.failures = failures
|
||||
|
||||
|
||||
def _init_runner_class() -> Type["IPDocTestRunner"]:
|
||||
import doctest
|
||||
from .ipdoctest import IPDocTestRunner
|
||||
|
||||
class PytestDoctestRunner(IPDocTestRunner):
|
||||
"""Runner to collect failures.
|
||||
|
||||
Note that the out variable in this case is a list instead of a
|
||||
stdout-like object.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
checker: Optional["IPDoctestOutputChecker"] = None,
|
||||
verbose: Optional[bool] = None,
|
||||
optionflags: int = 0,
|
||||
continue_on_failure: bool = True,
|
||||
) -> None:
|
||||
super().__init__(checker=checker, verbose=verbose, optionflags=optionflags)
|
||||
self.continue_on_failure = continue_on_failure
|
||||
|
||||
def report_failure(
|
||||
self,
|
||||
out,
|
||||
test: "doctest.DocTest",
|
||||
example: "doctest.Example",
|
||||
got: str,
|
||||
) -> None:
|
||||
failure = doctest.DocTestFailure(test, example, got)
|
||||
if self.continue_on_failure:
|
||||
out.append(failure)
|
||||
else:
|
||||
raise failure
|
||||
|
||||
def report_unexpected_exception(
|
||||
self,
|
||||
out,
|
||||
test: "doctest.DocTest",
|
||||
example: "doctest.Example",
|
||||
exc_info: Tuple[Type[BaseException], BaseException, types.TracebackType],
|
||||
) -> None:
|
||||
if isinstance(exc_info[1], OutcomeException):
|
||||
raise exc_info[1]
|
||||
if isinstance(exc_info[1], bdb.BdbQuit):
|
||||
outcomes.exit("Quitting debugger")
|
||||
failure = doctest.UnexpectedException(test, example, exc_info)
|
||||
if self.continue_on_failure:
|
||||
out.append(failure)
|
||||
else:
|
||||
raise failure
|
||||
|
||||
return PytestDoctestRunner
|
||||
|
||||
|
||||
def _get_runner(
|
||||
checker: Optional["IPDoctestOutputChecker"] = None,
|
||||
verbose: Optional[bool] = None,
|
||||
optionflags: int = 0,
|
||||
continue_on_failure: bool = True,
|
||||
) -> "IPDocTestRunner":
|
||||
# We need this in order to do a lazy import on doctest
|
||||
global RUNNER_CLASS
|
||||
if RUNNER_CLASS is None:
|
||||
RUNNER_CLASS = _init_runner_class()
|
||||
# Type ignored because the continue_on_failure argument is only defined on
|
||||
# PytestDoctestRunner, which is lazily defined so can't be used as a type.
|
||||
return RUNNER_CLASS( # type: ignore
|
||||
checker=checker,
|
||||
verbose=verbose,
|
||||
optionflags=optionflags,
|
||||
continue_on_failure=continue_on_failure,
|
||||
)
|
||||
|
||||
|
||||
class IPDoctestItem(pytest.Item):
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
parent: "Union[IPDoctestTextfile, IPDoctestModule]",
|
||||
runner: Optional["IPDocTestRunner"] = None,
|
||||
dtest: Optional["doctest.DocTest"] = None,
|
||||
) -> None:
|
||||
super().__init__(name, parent)
|
||||
self.runner = runner
|
||||
self.dtest = dtest
|
||||
self.obj = None
|
||||
self.fixture_request: Optional[FixtureRequest] = None
|
||||
|
||||
@classmethod
|
||||
def from_parent( # type: ignore
|
||||
cls,
|
||||
parent: "Union[IPDoctestTextfile, IPDoctestModule]",
|
||||
*,
|
||||
name: str,
|
||||
runner: "IPDocTestRunner",
|
||||
dtest: "doctest.DocTest",
|
||||
):
|
||||
# incompatible signature due to imposed limits on subclass
|
||||
"""The public named constructor."""
|
||||
return super().from_parent(name=name, parent=parent, runner=runner, dtest=dtest)
|
||||
|
||||
def setup(self) -> None:
|
||||
if self.dtest is not None:
|
||||
self.fixture_request = _setup_fixtures(self)
|
||||
globs = dict(getfixture=self.fixture_request.getfixturevalue)
|
||||
for name, value in self.fixture_request.getfixturevalue(
|
||||
"ipdoctest_namespace"
|
||||
).items():
|
||||
globs[name] = value
|
||||
self.dtest.globs.update(globs)
|
||||
|
||||
from .ipdoctest import IPExample
|
||||
|
||||
if isinstance(self.dtest.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.dtest.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__"] = builtins
|
||||
self.dtest.globs = _ip.user_ns
|
||||
|
||||
def teardown(self) -> None:
|
||||
from .ipdoctest import IPExample
|
||||
|
||||
# Undo the test.globs reassignment we made
|
||||
if isinstance(self.dtest.examples[0], IPExample):
|
||||
self.dtest.globs = {}
|
||||
_ip.user_ns.clear()
|
||||
_ip.user_ns.update(self._user_ns_orig)
|
||||
del self._user_ns_orig
|
||||
|
||||
self.dtest.globs.clear()
|
||||
|
||||
def runtest(self) -> None:
|
||||
assert self.dtest is not None
|
||||
assert self.runner is not None
|
||||
_check_all_skipped(self.dtest)
|
||||
self._disable_output_capturing_for_darwin()
|
||||
failures: List["doctest.DocTestFailure"] = []
|
||||
|
||||
# exec(compile(..., "single", ...), ...) puts result in builtins._
|
||||
had_underscore_value = hasattr(builtins, "_")
|
||||
underscore_original_value = getattr(builtins, "_", None)
|
||||
|
||||
# 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()
|
||||
os.chdir(self.fspath.dirname)
|
||||
try:
|
||||
# Type ignored because we change the type of `out` from what
|
||||
# ipdoctest expects.
|
||||
self.runner.run(self.dtest, out=failures, clear_globs=False) # type: ignore[arg-type]
|
||||
finally:
|
||||
os.chdir(curdir)
|
||||
if had_underscore_value:
|
||||
setattr(builtins, "_", underscore_original_value)
|
||||
elif hasattr(builtins, "_"):
|
||||
delattr(builtins, "_")
|
||||
|
||||
if failures:
|
||||
raise MultipleDoctestFailures(failures)
|
||||
|
||||
def _disable_output_capturing_for_darwin(self) -> None:
|
||||
"""Disable output capturing. Otherwise, stdout is lost to ipdoctest (pytest#985)."""
|
||||
if platform.system() != "Darwin":
|
||||
return
|
||||
capman = self.config.pluginmanager.getplugin("capturemanager")
|
||||
if capman:
|
||||
capman.suspend_global_capture(in_=True)
|
||||
out, err = capman.read_global_capture()
|
||||
sys.stdout.write(out)
|
||||
sys.stderr.write(err)
|
||||
|
||||
# TODO: Type ignored -- breaks Liskov Substitution.
|
||||
def repr_failure( # type: ignore[override]
|
||||
self,
|
||||
excinfo: ExceptionInfo[BaseException],
|
||||
) -> Union[str, TerminalRepr]:
|
||||
import doctest
|
||||
|
||||
failures: Optional[
|
||||
Sequence[Union[doctest.DocTestFailure, doctest.UnexpectedException]]
|
||||
] = None
|
||||
if isinstance(
|
||||
excinfo.value, (doctest.DocTestFailure, doctest.UnexpectedException)
|
||||
):
|
||||
failures = [excinfo.value]
|
||||
elif isinstance(excinfo.value, MultipleDoctestFailures):
|
||||
failures = excinfo.value.failures
|
||||
|
||||
if failures is None:
|
||||
return super().repr_failure(excinfo)
|
||||
|
||||
reprlocation_lines = []
|
||||
for failure in failures:
|
||||
example = failure.example
|
||||
test = failure.test
|
||||
filename = test.filename
|
||||
if test.lineno is None:
|
||||
lineno = None
|
||||
else:
|
||||
lineno = test.lineno + example.lineno + 1
|
||||
message = type(failure).__name__
|
||||
# TODO: ReprFileLocation doesn't expect a None lineno.
|
||||
reprlocation = ReprFileLocation(filename, lineno, message) # type: ignore[arg-type]
|
||||
checker = _get_checker()
|
||||
report_choice = _get_report_choice(self.config.getoption("ipdoctestreport"))
|
||||
if lineno is not None:
|
||||
assert failure.test.docstring is not None
|
||||
lines = failure.test.docstring.splitlines(False)
|
||||
# add line numbers to the left of the error message
|
||||
assert test.lineno is not None
|
||||
lines = [
|
||||
"%03d %s" % (i + test.lineno + 1, x) for (i, x) in enumerate(lines)
|
||||
]
|
||||
# trim docstring error lines to 10
|
||||
lines = lines[max(example.lineno - 9, 0) : example.lineno + 1]
|
||||
else:
|
||||
lines = [
|
||||
"EXAMPLE LOCATION UNKNOWN, not showing all tests of that example"
|
||||
]
|
||||
indent = ">>>"
|
||||
for line in example.source.splitlines():
|
||||
lines.append(f"??? {indent} {line}")
|
||||
indent = "..."
|
||||
if isinstance(failure, doctest.DocTestFailure):
|
||||
lines += checker.output_difference(
|
||||
example, failure.got, report_choice
|
||||
).split("\n")
|
||||
else:
|
||||
inner_excinfo = ExceptionInfo.from_exc_info(failure.exc_info)
|
||||
lines += ["UNEXPECTED EXCEPTION: %s" % repr(inner_excinfo.value)]
|
||||
lines += [
|
||||
x.strip("\n") for x in traceback.format_exception(*failure.exc_info)
|
||||
]
|
||||
reprlocation_lines.append((reprlocation, lines))
|
||||
return ReprFailDoctest(reprlocation_lines)
|
||||
|
||||
def reportinfo(self) -> Tuple[Union["os.PathLike[str]", str], Optional[int], str]:
|
||||
assert self.dtest is not None
|
||||
return self.path, self.dtest.lineno, "[ipdoctest] %s" % self.name
|
||||
|
||||
if int(pytest.__version__.split(".")[0]) < 7:
|
||||
|
||||
@property
|
||||
def path(self) -> Path:
|
||||
return Path(self.fspath)
|
||||
|
||||
|
||||
def _get_flag_lookup() -> Dict[str, int]:
|
||||
import doctest
|
||||
|
||||
return dict(
|
||||
DONT_ACCEPT_TRUE_FOR_1=doctest.DONT_ACCEPT_TRUE_FOR_1,
|
||||
DONT_ACCEPT_BLANKLINE=doctest.DONT_ACCEPT_BLANKLINE,
|
||||
NORMALIZE_WHITESPACE=doctest.NORMALIZE_WHITESPACE,
|
||||
ELLIPSIS=doctest.ELLIPSIS,
|
||||
IGNORE_EXCEPTION_DETAIL=doctest.IGNORE_EXCEPTION_DETAIL,
|
||||
COMPARISON_FLAGS=doctest.COMPARISON_FLAGS,
|
||||
ALLOW_UNICODE=_get_allow_unicode_flag(),
|
||||
ALLOW_BYTES=_get_allow_bytes_flag(),
|
||||
NUMBER=_get_number_flag(),
|
||||
)
|
||||
|
||||
|
||||
def get_optionflags(parent):
|
||||
optionflags_str = parent.config.getini("ipdoctest_optionflags")
|
||||
flag_lookup_table = _get_flag_lookup()
|
||||
flag_acc = 0
|
||||
for flag in optionflags_str:
|
||||
flag_acc |= flag_lookup_table[flag]
|
||||
return flag_acc
|
||||
|
||||
|
||||
def _get_continue_on_failure(config):
|
||||
continue_on_failure = config.getvalue("ipdoctest_continue_on_failure")
|
||||
if continue_on_failure:
|
||||
# We need to turn off this if we use pdb since we should stop at
|
||||
# the first failure.
|
||||
if config.getvalue("usepdb"):
|
||||
continue_on_failure = False
|
||||
return continue_on_failure
|
||||
|
||||
|
||||
class IPDoctestTextfile(pytest.Module):
|
||||
obj = None
|
||||
|
||||
def collect(self) -> Iterable[IPDoctestItem]:
|
||||
import doctest
|
||||
from .ipdoctest import IPDocTestParser
|
||||
|
||||
# Inspired by doctest.testfile; ideally we would use it directly,
|
||||
# but it doesn't support passing a custom checker.
|
||||
encoding = self.config.getini("ipdoctest_encoding")
|
||||
text = self.path.read_text(encoding)
|
||||
filename = str(self.path)
|
||||
name = self.path.name
|
||||
globs = {"__name__": "__main__"}
|
||||
|
||||
optionflags = get_optionflags(self)
|
||||
|
||||
runner = _get_runner(
|
||||
verbose=False,
|
||||
optionflags=optionflags,
|
||||
checker=_get_checker(),
|
||||
continue_on_failure=_get_continue_on_failure(self.config),
|
||||
)
|
||||
|
||||
parser = IPDocTestParser()
|
||||
test = parser.get_doctest(text, globs, name, filename, 0)
|
||||
if test.examples:
|
||||
yield IPDoctestItem.from_parent(
|
||||
self, name=test.name, runner=runner, dtest=test
|
||||
)
|
||||
|
||||
if int(pytest.__version__.split(".")[0]) < 7:
|
||||
|
||||
@property
|
||||
def path(self) -> Path:
|
||||
return Path(self.fspath)
|
||||
|
||||
@classmethod
|
||||
def from_parent(
|
||||
cls,
|
||||
parent,
|
||||
*,
|
||||
fspath=None,
|
||||
path: Optional[Path] = None,
|
||||
**kw,
|
||||
):
|
||||
if path is not None:
|
||||
import py.path
|
||||
|
||||
fspath = py.path.local(path)
|
||||
return super().from_parent(parent=parent, fspath=fspath, **kw)
|
||||
|
||||
|
||||
def _check_all_skipped(test: "doctest.DocTest") -> None:
|
||||
"""Raise pytest.skip() if all examples in the given DocTest have the SKIP
|
||||
option set."""
|
||||
import doctest
|
||||
|
||||
all_skipped = all(x.options.get(doctest.SKIP, False) for x in test.examples)
|
||||
if all_skipped:
|
||||
pytest.skip("all docstests skipped by +SKIP option")
|
||||
|
||||
|
||||
def _is_mocked(obj: object) -> bool:
|
||||
"""Return if an object is possibly a mock object by checking the
|
||||
existence of a highly improbable attribute."""
|
||||
return (
|
||||
safe_getattr(obj, "pytest_mock_example_attribute_that_shouldnt_exist", None)
|
||||
is not None
|
||||
)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def _patch_unwrap_mock_aware() -> Generator[None, None, None]:
|
||||
"""Context manager which replaces ``inspect.unwrap`` with a version
|
||||
that's aware of mock objects and doesn't recurse into them."""
|
||||
real_unwrap = inspect.unwrap
|
||||
|
||||
def _mock_aware_unwrap(
|
||||
func: Callable[..., Any], *, stop: Optional[Callable[[Any], Any]] = None
|
||||
) -> Any:
|
||||
try:
|
||||
if stop is None or stop is _is_mocked:
|
||||
return real_unwrap(func, stop=_is_mocked)
|
||||
_stop = stop
|
||||
return real_unwrap(func, stop=lambda obj: _is_mocked(obj) or _stop(func))
|
||||
except Exception as e:
|
||||
warnings.warn(
|
||||
"Got %r when unwrapping %r. This is usually caused "
|
||||
"by a violation of Python's object protocol; see e.g. "
|
||||
"https://github.com/pytest-dev/pytest/issues/5080" % (e, func),
|
||||
PytestWarning,
|
||||
)
|
||||
raise
|
||||
|
||||
inspect.unwrap = _mock_aware_unwrap
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
inspect.unwrap = real_unwrap
|
||||
|
||||
|
||||
class IPDoctestModule(pytest.Module):
|
||||
def collect(self) -> Iterable[IPDoctestItem]:
|
||||
import doctest
|
||||
from .ipdoctest import DocTestFinder, IPDocTestParser
|
||||
|
||||
class MockAwareDocTestFinder(DocTestFinder):
|
||||
"""A hackish ipdoctest finder that overrides stdlib internals to fix a stdlib bug.
|
||||
|
||||
https://github.com/pytest-dev/pytest/issues/3456
|
||||
https://bugs.python.org/issue25532
|
||||
"""
|
||||
|
||||
def _find_lineno(self, obj, source_lines):
|
||||
"""Doctest code does not take into account `@property`, this
|
||||
is a hackish way to fix it. https://bugs.python.org/issue17446
|
||||
|
||||
Wrapped Doctests will need to be unwrapped so the correct
|
||||
line number is returned. This will be reported upstream. #8796
|
||||
"""
|
||||
if isinstance(obj, property):
|
||||
obj = getattr(obj, "fget", obj)
|
||||
|
||||
if hasattr(obj, "__wrapped__"):
|
||||
# Get the main obj in case of it being wrapped
|
||||
obj = inspect.unwrap(obj)
|
||||
|
||||
# Type ignored because this is a private function.
|
||||
return super()._find_lineno( # type:ignore[misc]
|
||||
obj,
|
||||
source_lines,
|
||||
)
|
||||
|
||||
def _find(
|
||||
self, tests, obj, name, module, source_lines, globs, seen
|
||||
) -> None:
|
||||
if _is_mocked(obj):
|
||||
return
|
||||
with _patch_unwrap_mock_aware():
|
||||
|
||||
# Type ignored because this is a private function.
|
||||
super()._find( # type:ignore[misc]
|
||||
tests, obj, name, module, source_lines, globs, seen
|
||||
)
|
||||
|
||||
if self.path.name == "conftest.py":
|
||||
if int(pytest.__version__.split(".")[0]) < 7:
|
||||
module = self.config.pluginmanager._importconftest(
|
||||
self.path,
|
||||
self.config.getoption("importmode"),
|
||||
)
|
||||
else:
|
||||
module = self.config.pluginmanager._importconftest(
|
||||
self.path,
|
||||
self.config.getoption("importmode"),
|
||||
rootpath=self.config.rootpath,
|
||||
)
|
||||
else:
|
||||
try:
|
||||
module = import_path(self.path, root=self.config.rootpath)
|
||||
except ImportError:
|
||||
if self.config.getvalue("ipdoctest_ignore_import_errors"):
|
||||
pytest.skip("unable to import module %r" % self.path)
|
||||
else:
|
||||
raise
|
||||
# Uses internal doctest module parsing mechanism.
|
||||
finder = MockAwareDocTestFinder(parser=IPDocTestParser())
|
||||
optionflags = get_optionflags(self)
|
||||
runner = _get_runner(
|
||||
verbose=False,
|
||||
optionflags=optionflags,
|
||||
checker=_get_checker(),
|
||||
continue_on_failure=_get_continue_on_failure(self.config),
|
||||
)
|
||||
|
||||
for test in finder.find(module, module.__name__):
|
||||
if test.examples: # skip empty ipdoctests
|
||||
yield IPDoctestItem.from_parent(
|
||||
self, name=test.name, runner=runner, dtest=test
|
||||
)
|
||||
|
||||
if int(pytest.__version__.split(".")[0]) < 7:
|
||||
|
||||
@property
|
||||
def path(self) -> Path:
|
||||
return Path(self.fspath)
|
||||
|
||||
@classmethod
|
||||
def from_parent(
|
||||
cls,
|
||||
parent,
|
||||
*,
|
||||
fspath=None,
|
||||
path: Optional[Path] = None,
|
||||
**kw,
|
||||
):
|
||||
if path is not None:
|
||||
import py.path
|
||||
|
||||
fspath = py.path.local(path)
|
||||
return super().from_parent(parent=parent, fspath=fspath, **kw)
|
||||
|
||||
|
||||
def _setup_fixtures(doctest_item: IPDoctestItem) -> FixtureRequest:
|
||||
"""Used by IPDoctestTextfile and IPDoctestItem to setup fixture information."""
|
||||
|
||||
def func() -> None:
|
||||
pass
|
||||
|
||||
doctest_item.funcargs = {} # type: ignore[attr-defined]
|
||||
fm = doctest_item.session._fixturemanager
|
||||
doctest_item._fixtureinfo = fm.getfixtureinfo( # type: ignore[attr-defined]
|
||||
node=doctest_item, func=func, cls=None, funcargs=False
|
||||
)
|
||||
fixture_request = FixtureRequest(doctest_item, _ispytest=True)
|
||||
fixture_request._fillfixtures()
|
||||
return fixture_request
|
||||
|
||||
|
||||
def _init_checker_class() -> Type["IPDoctestOutputChecker"]:
|
||||
import doctest
|
||||
import re
|
||||
from .ipdoctest import IPDoctestOutputChecker
|
||||
|
||||
class LiteralsOutputChecker(IPDoctestOutputChecker):
|
||||
# Based on doctest_nose_plugin.py from the nltk project
|
||||
# (https://github.com/nltk/nltk) and on the "numtest" doctest extension
|
||||
# by Sebastien Boisgerault (https://github.com/boisgera/numtest).
|
||||
|
||||
_unicode_literal_re = re.compile(r"(\W|^)[uU]([rR]?[\'\"])", re.UNICODE)
|
||||
_bytes_literal_re = re.compile(r"(\W|^)[bB]([rR]?[\'\"])", re.UNICODE)
|
||||
_number_re = re.compile(
|
||||
r"""
|
||||
(?P<number>
|
||||
(?P<mantissa>
|
||||
(?P<integer1> [+-]?\d*)\.(?P<fraction>\d+)
|
||||
|
|
||||
(?P<integer2> [+-]?\d+)\.
|
||||
)
|
||||
(?:
|
||||
[Ee]
|
||||
(?P<exponent1> [+-]?\d+)
|
||||
)?
|
||||
|
|
||||
(?P<integer3> [+-]?\d+)
|
||||
(?:
|
||||
[Ee]
|
||||
(?P<exponent2> [+-]?\d+)
|
||||
)
|
||||
)
|
||||
""",
|
||||
re.VERBOSE,
|
||||
)
|
||||
|
||||
def check_output(self, want: str, got: str, optionflags: int) -> bool:
|
||||
if super().check_output(want, got, optionflags):
|
||||
return True
|
||||
|
||||
allow_unicode = optionflags & _get_allow_unicode_flag()
|
||||
allow_bytes = optionflags & _get_allow_bytes_flag()
|
||||
allow_number = optionflags & _get_number_flag()
|
||||
|
||||
if not allow_unicode and not allow_bytes and not allow_number:
|
||||
return False
|
||||
|
||||
def remove_prefixes(regex: Pattern[str], txt: str) -> str:
|
||||
return re.sub(regex, r"\1\2", txt)
|
||||
|
||||
if allow_unicode:
|
||||
want = remove_prefixes(self._unicode_literal_re, want)
|
||||
got = remove_prefixes(self._unicode_literal_re, got)
|
||||
|
||||
if allow_bytes:
|
||||
want = remove_prefixes(self._bytes_literal_re, want)
|
||||
got = remove_prefixes(self._bytes_literal_re, got)
|
||||
|
||||
if allow_number:
|
||||
got = self._remove_unwanted_precision(want, got)
|
||||
|
||||
return super().check_output(want, got, optionflags)
|
||||
|
||||
def _remove_unwanted_precision(self, want: str, got: str) -> str:
|
||||
wants = list(self._number_re.finditer(want))
|
||||
gots = list(self._number_re.finditer(got))
|
||||
if len(wants) != len(gots):
|
||||
return got
|
||||
offset = 0
|
||||
for w, g in zip(wants, gots):
|
||||
fraction: Optional[str] = w.group("fraction")
|
||||
exponent: Optional[str] = w.group("exponent1")
|
||||
if exponent is None:
|
||||
exponent = w.group("exponent2")
|
||||
precision = 0 if fraction is None else len(fraction)
|
||||
if exponent is not None:
|
||||
precision -= int(exponent)
|
||||
if float(w.group()) == approx(float(g.group()), abs=10 ** -precision):
|
||||
# They're close enough. Replace the text we actually
|
||||
# got with the text we want, so that it will match when we
|
||||
# check the string literally.
|
||||
got = (
|
||||
got[: g.start() + offset] + w.group() + got[g.end() + offset :]
|
||||
)
|
||||
offset += w.end() - w.start() - (g.end() - g.start())
|
||||
return got
|
||||
|
||||
return LiteralsOutputChecker
|
||||
|
||||
|
||||
def _get_checker() -> "IPDoctestOutputChecker":
|
||||
"""Return a IPDoctestOutputChecker subclass that supports some
|
||||
additional options:
|
||||
|
||||
* ALLOW_UNICODE and ALLOW_BYTES options to ignore u'' and b''
|
||||
prefixes (respectively) in string literals. Useful when the same
|
||||
ipdoctest should run in Python 2 and Python 3.
|
||||
|
||||
* NUMBER to ignore floating-point differences smaller than the
|
||||
precision of the literal number in the ipdoctest.
|
||||
|
||||
An inner class is used to avoid importing "ipdoctest" at the module
|
||||
level.
|
||||
"""
|
||||
global CHECKER_CLASS
|
||||
if CHECKER_CLASS is None:
|
||||
CHECKER_CLASS = _init_checker_class()
|
||||
return CHECKER_CLASS()
|
||||
|
||||
|
||||
def _get_allow_unicode_flag() -> int:
|
||||
"""Register and return the ALLOW_UNICODE flag."""
|
||||
import doctest
|
||||
|
||||
return doctest.register_optionflag("ALLOW_UNICODE")
|
||||
|
||||
|
||||
def _get_allow_bytes_flag() -> int:
|
||||
"""Register and return the ALLOW_BYTES flag."""
|
||||
import doctest
|
||||
|
||||
return doctest.register_optionflag("ALLOW_BYTES")
|
||||
|
||||
|
||||
def _get_number_flag() -> int:
|
||||
"""Register and return the NUMBER flag."""
|
||||
import doctest
|
||||
|
||||
return doctest.register_optionflag("NUMBER")
|
||||
|
||||
|
||||
def _get_report_choice(key: str) -> int:
|
||||
"""Return the actual `ipdoctest` module flag value.
|
||||
|
||||
We want to do it as late as possible to avoid importing `ipdoctest` and all
|
||||
its dependencies when parsing options, as it adds overhead and breaks tests.
|
||||
"""
|
||||
import doctest
|
||||
|
||||
return {
|
||||
DOCTEST_REPORT_CHOICE_UDIFF: doctest.REPORT_UDIFF,
|
||||
DOCTEST_REPORT_CHOICE_CDIFF: doctest.REPORT_CDIFF,
|
||||
DOCTEST_REPORT_CHOICE_NDIFF: doctest.REPORT_NDIFF,
|
||||
DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE: doctest.REPORT_ONLY_FIRST_FAILURE,
|
||||
DOCTEST_REPORT_CHOICE_NONE: 0,
|
||||
}[key]
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def ipdoctest_namespace() -> Dict[str, Any]:
|
||||
"""Fixture that returns a :py:class:`dict` that will be injected into the
|
||||
namespace of ipdoctests."""
|
||||
return dict()
|
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',
|
||||
],
|
||||
},
|
||||
)
|
44
.venv/Lib/site-packages/IPython/testing/plugin/simple.py
Normal file
44
.venv/Lib/site-packages/IPython/testing/plugin/simple.py
Normal file
@ -0,0 +1,44 @@
|
||||
"""Simple example using doctests.
|
||||
|
||||
This file just contains doctests both using plain python and IPython prompts.
|
||||
All tests should be loaded by Pytest.
|
||||
"""
|
||||
|
||||
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 ipyfunc():
|
||||
"""Some IPython tests...
|
||||
|
||||
In [1]: ipyfunc()
|
||||
Out[1]: 'ipyfunc'
|
||||
|
||||
In [2]: import os
|
||||
|
||||
In [3]: 2+3
|
||||
Out[3]: 5
|
||||
|
||||
In [4]: for i in range(3):
|
||||
...: print(i, end=' ')
|
||||
...: print(i+1, end=' ')
|
||||
...:
|
||||
Out[4]: 0 1 1 2 2 3
|
||||
"""
|
||||
return "ipyfunc"
|
@ -0,0 +1,2 @@
|
||||
x = 1
|
||||
print('x is:',x)
|
@ -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,92 @@
|
||||
"""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
|
||||
"""
|
||||
|
||||
|
||||
def doctest_builtin_underscore():
|
||||
"""Defining builtins._ should not break anything outside the doctest
|
||||
while also should be working as expected inside the doctest.
|
||||
|
||||
In [1]: import builtins
|
||||
|
||||
In [2]: builtins._ = 42
|
||||
|
||||
In [3]: builtins._
|
||||
Out[3]: 42
|
||||
|
||||
In [4]: _
|
||||
Out[4]: 42
|
||||
"""
|
39
.venv/Lib/site-packages/IPython/testing/plugin/test_refs.py
Normal file
39
.venv/Lib/site-packages/IPython/testing/plugin/test_refs.py
Normal file
@ -0,0 +1,39 @@
|
||||
"""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
|
||||
"""
|
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.
|
||||
#-------------------------------------------------------------------------------
|
166
.venv/Lib/site-packages/IPython/testing/tests/test_decorators.py
Normal file
166
.venv/Lib/site-packages/IPython/testing/tests/test_decorators.py
Normal file
@ -0,0 +1,166 @@
|
||||
"""Tests for the decorators we've created for IPython.
|
||||
"""
|
||||
|
||||
# Module imports
|
||||
# Std lib
|
||||
import inspect
|
||||
import sys
|
||||
|
||||
# Our own
|
||||
from IPython.testing import decorators as dec
|
||||
from IPython.testing.skipdoctest import skip_doctest
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# 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.
|
||||
@skip_doctest
|
||||
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__
|
||||
|
||||
assert check == val, "doctest_bad docstrings don't match"
|
||||
|
||||
|
||||
# Doctest skipping should work for class methods too
|
||||
class FooClass(object):
|
||||
"""FooClass
|
||||
|
||||
Example:
|
||||
|
||||
>>> 1+1
|
||||
2
|
||||
"""
|
||||
|
||||
@skip_doctest
|
||||
def __init__(self,x):
|
||||
"""Make a FooClass.
|
||||
|
||||
Example:
|
||||
|
||||
>>> f = FooClass(3)
|
||||
junk
|
||||
"""
|
||||
print('Making a FooClass.')
|
||||
self.x = x
|
||||
|
||||
@skip_doctest
|
||||
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():
|
||||
assert sys.platform.startswith("linux") is False, "This test can't run under linux"
|
||||
|
||||
|
||||
@dec.skip_win32
|
||||
def test_win32():
|
||||
assert sys.platform != "win32", "This test can't run under windows"
|
||||
|
||||
|
||||
@dec.skip_osx
|
||||
def test_osx():
|
||||
assert 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
|
||||
"""
|
133
.venv/Lib/site-packages/IPython/testing/tests/test_tools.py
Normal file
133
.venv/Lib/site-packages/IPython/testing/tests/test_tools.py
Normal file
@ -0,0 +1,133 @@
|
||||
# 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
|
||||
|
||||
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"])
|
||||
assert result, ["/foo/a.txt" == "/foo/b.txt"]
|
||||
spath = "/foo"
|
||||
result = tt.full_path(spath, ["a.txt", "b.txt"])
|
||||
assert result, ["/a.txt" == "/b.txt"]
|
||||
result = tt.full_path(spath, "a.txt")
|
||||
assert 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"])
|
||||
assert result, ["c:\\foo\\a.txt" == "c:\\foo\\b.txt"]
|
||||
spath = "c:\\foo"
|
||||
result = tt.full_path(spath, ["a.txt", "b.txt"])
|
||||
assert result, ["c:\\a.txt" == "c:\\b.txt"]
|
||||
result = tt.full_path(spath, "a.txt")
|
||||
assert 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)
|
||||
assert nerr == nerr1
|
||||
assert nfail == nfail1
|
||||
|
||||
|
||||
def test_temp_pyfile():
|
||||
src = 'pass\n'
|
||||
fname = tt.temp_pyfile(src)
|
||||
assert os.path.isfile(fname)
|
||||
with open(fname, encoding="utf-8") as fh2:
|
||||
src2 = fh2.read()
|
||||
assert 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)
|
476
.venv/Lib/site-packages/IPython/testing/tools.py
Normal file
476
.venv/Lib/site-packages/IPython/testing/tools.py
Normal file
@ -0,0 +1,476 @@
|
||||
"""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
|
||||
from pathlib import Path
|
||||
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
|
||||
|
||||
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 = Path(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, Path
|
||||
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.
|
||||
"""
|
||||
__tracebackhide__ = True
|
||||
|
||||
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
|
||||
# Prevent coloring under PyCharm ("\x1b[0m" at the end of the stdout)
|
||||
env.pop("PYCHARM_HOSTED", None)
|
||||
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, Path
|
||||
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
|
||||
"""
|
||||
__tracebackhide__ = True
|
||||
|
||||
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:
|
||||
assert "\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
|
||||
assert "\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.
|
||||
"""
|
||||
__tracebackhide__ = True
|
||||
|
||||
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):
|
||||
__tracebackhide__ = True
|
||||
|
||||
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):
|
||||
__tracebackhide__ = True
|
||||
|
||||
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", encoding="utf-8").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 as e:
|
||||
raise EOFError('No more inputs given') from e
|
||||
|
||||
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)
|
||||
assert rc == 0, err
|
||||
assert "Traceback" not in err
|
||||
assert "Options" in out
|
||||
assert "--help-all" in 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)
|
||||
assert rc == 0, err
|
||||
assert "Traceback" not in err
|
||||
assert "Options" in out
|
||||
assert "Class" in out
|
||||
return out, err
|
||||
|
Reference in New Issue
Block a user