aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/ipython/py3/IPython/testing
diff options
context:
space:
mode:
authorrobot-contrib <robot-contrib@yandex-team.ru>2022-05-18 00:43:36 +0300
committerrobot-contrib <robot-contrib@yandex-team.ru>2022-05-18 00:43:36 +0300
commit9e5f436a8b2a27bcc7802e443ea3ef3e41a82a75 (patch)
tree78b522cab9f76336e62064d4d8ff7c897659b20e /contrib/python/ipython/py3/IPython/testing
parent8113a823ffca6451bb5ff8f0334560885a939a24 (diff)
downloadydb-9e5f436a8b2a27bcc7802e443ea3ef3e41a82a75.tar.gz
Update contrib/python/ipython/py3 to 8.3.0
ref:e84342d4d30476f9148137f37fd0c6405fd36f55
Diffstat (limited to 'contrib/python/ipython/py3/IPython/testing')
-rw-r--r--contrib/python/ipython/py3/IPython/testing/__init__.py29
-rw-r--r--contrib/python/ipython/py3/IPython/testing/__main__.py3
-rw-r--r--contrib/python/ipython/py3/IPython/testing/decorators.py215
-rw-r--r--contrib/python/ipython/py3/IPython/testing/globalipapp.py28
-rw-r--r--contrib/python/ipython/py3/IPython/testing/iptest.py460
-rw-r--r--contrib/python/ipython/py3/IPython/testing/iptestcontroller.py491
-rw-r--r--contrib/python/ipython/py3/IPython/testing/plugin/dtexample.py40
-rw-r--r--contrib/python/ipython/py3/IPython/testing/plugin/ipdoctest.py479
-rw-r--r--contrib/python/ipython/py3/IPython/testing/plugin/iptest.py18
-rw-r--r--contrib/python/ipython/py3/IPython/testing/plugin/pytest_ipdoctest.py860
-rw-r--r--contrib/python/ipython/py3/IPython/testing/plugin/show_refs.py19
-rw-r--r--contrib/python/ipython/py3/IPython/testing/plugin/simple.py23
-rw-r--r--contrib/python/ipython/py3/IPython/testing/plugin/test_exampleip.txt2
-rw-r--r--contrib/python/ipython/py3/IPython/testing/plugin/test_ipdoctest.py16
-rw-r--r--contrib/python/ipython/py3/IPython/testing/plugin/test_refs.py7
-rw-r--r--contrib/python/ipython/py3/IPython/testing/skipdoctest.py2
-rw-r--r--contrib/python/ipython/py3/IPython/testing/tools.py65
17 files changed, 984 insertions, 1773 deletions
diff --git a/contrib/python/ipython/py3/IPython/testing/__init__.py b/contrib/python/ipython/py3/IPython/testing/__init__.py
index 552608792d..8fcd65ea41 100644
--- a/contrib/python/ipython/py3/IPython/testing/__init__.py
+++ b/contrib/python/ipython/py3/IPython/testing/__init__.py
@@ -12,38 +12,9 @@
import os
#-----------------------------------------------------------------------------
-# Functions
-#-----------------------------------------------------------------------------
-
-# User-level entry point for testing
-def test(**kwargs):
- """Run the entire IPython test suite.
-
- Any of the options for run_iptestall() may be passed as keyword arguments.
-
- For example::
-
- IPython.test(testgroups=['lib', 'config', 'utils'], fast=2)
-
- will run those three sections of the test suite, using two processes.
- """
-
- # Do the import internally, so that this function doesn't increase total
- # import time
- from .iptestcontroller import run_iptestall, default_options
- options = default_options()
- for name, val in kwargs.items():
- setattr(options, name, val)
- run_iptestall(options)
-
-#-----------------------------------------------------------------------------
# Constants
#-----------------------------------------------------------------------------
# We scale all timeouts via this factor, slow machines can increase it
IPYTHON_TESTING_TIMEOUT_SCALE = float(os.getenv(
'IPYTHON_TESTING_TIMEOUT_SCALE', 1))
-
-# So nose doesn't try to run this as a test itself and we end up with an
-# infinite test loop
-test.__test__ = False
diff --git a/contrib/python/ipython/py3/IPython/testing/__main__.py b/contrib/python/ipython/py3/IPython/testing/__main__.py
deleted file mode 100644
index 4b0bb8ba9c..0000000000
--- a/contrib/python/ipython/py3/IPython/testing/__main__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-if __name__ == '__main__':
- from IPython.testing import iptestcontroller
- iptestcontroller.main()
diff --git a/contrib/python/ipython/py3/IPython/testing/decorators.py b/contrib/python/ipython/py3/IPython/testing/decorators.py
index 4539a72a8c..644a513a8c 100644
--- a/contrib/python/ipython/py3/IPython/testing/decorators.py
+++ b/contrib/python/ipython/py3/IPython/testing/decorators.py
@@ -44,11 +44,6 @@ from decorator import decorator
# Expose the unittest-driven decorators
from .ipunittest import ipdoctest, ipdocstring
-# Grab the numpy-specific decorators which we keep in a file that we
-# occasionally update from upstream: decorators.py is a copy of
-# numpy.testing.decorators, we expose all of it here.
-from IPython.external.decorators import knownfailureif
-
#-----------------------------------------------------------------------------
# Classes and functions
#-----------------------------------------------------------------------------
@@ -66,99 +61,9 @@ def as_unittest(func):
# Utility functions
-def apply_wrapper(wrapper, func):
- """Apply a wrapper to a function for decoration.
-
- This mixes Michele Simionato's decorator tool with nose's make_decorator,
- to apply a wrapper in a decorator so that all nose attributes, as well as
- function signature and other properties, survive the decoration cleanly.
- This will ensure that wrapped functions can still be well introspected via
- IPython, for example.
- """
- warnings.warn("The function `apply_wrapper` is deprecated since IPython 4.0",
- DeprecationWarning, stacklevel=2)
- import nose.tools
-
- return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
-
-
-def make_label_dec(label, ds=None):
- """Factory function to create a decorator that applies one or more labels.
-
- Parameters
- ----------
- label : string or sequence
- One or more labels that will be applied by the decorator to the functions
- it decorates. Labels are attributes of the decorated function with their
- value set to True.
-
- ds : string
- An optional docstring for the resulting decorator. If not given, a
- default docstring is auto-generated.
-
- Returns
- -------
- A decorator.
-
- Examples
- --------
-
- A simple labeling decorator:
-
- >>> slow = make_label_dec('slow')
- >>> slow.__doc__
- "Labels a test as 'slow'."
-
- And one that uses multiple labels and a custom docstring:
-
- >>> rare = make_label_dec(['slow','hard'],
- ... "Mix labels 'slow' and 'hard' for rare tests.")
- >>> rare.__doc__
- "Mix labels 'slow' and 'hard' for rare tests."
-
- Now, let's test using this one:
- >>> @rare
- ... def f(): pass
- ...
- >>>
- >>> f.slow
- True
- >>> f.hard
- True
- """
-
- warnings.warn("The function `make_label_dec` is deprecated since IPython 4.0",
- DeprecationWarning, stacklevel=2)
- if isinstance(label, str):
- labels = [label]
- else:
- labels = label
-
- # Validate that the given label(s) are OK for use in setattr() by doing a
- # dry run on a dummy function.
- tmp = lambda : None
- for label in labels:
- setattr(tmp,label,True)
-
- # This is the actual decorator we'll return
- def decor(f):
- for label in labels:
- setattr(f,label,True)
- return f
- # Apply the user's docstring, or autogenerate a basic one
- if ds is None:
- ds = "Labels a test as %r." % label
- decor.__doc__ = ds
-
- return decor
-
-
-# Inspired by numpy's skipif, but uses the full apply_wrapper utility to
-# preserve function metadata better and allows the skip condition to be a
-# callable.
def skipif(skip_condition, msg=None):
- ''' Make function raise SkipTest exception if skip_condition is true
+ """Make function raise SkipTest exception if skip_condition is true
Parameters
----------
@@ -177,57 +82,15 @@ def skipif(skip_condition, msg=None):
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)
- Notes
- -----
- You will see from the code that we had to further decorate the
- decorator with the nose.tools.make_decorator function in order to
- transmit function name, and various other metadata.
- '''
-
- def skip_decorator(f):
- # Local import to avoid a hard nose dependency and only incur the
- # import time overhead at actual test-time.
- import nose
-
- # Allow for both boolean or callable skip conditions.
- if callable(skip_condition):
- skip_val = skip_condition
- else:
- skip_val = lambda : skip_condition
-
- def get_msg(func,msg=None):
- """Skip message with information about function being skipped."""
- if msg is None: out = 'Test skipped due to test condition.'
- else: out = msg
- return "Skipping test: %s. %s" % (func.__name__,out)
-
- # We need to define *two* skippers because Python doesn't allow both
- # return with value and yield inside the same function.
- def skipper_func(*args, **kwargs):
- """Skipper for normal test functions."""
- if skip_val():
- raise nose.SkipTest(get_msg(f,msg))
- else:
- return f(*args, **kwargs)
-
- def skipper_gen(*args, **kwargs):
- """Skipper for test generators."""
- if skip_val():
- raise nose.SkipTest(get_msg(f,msg))
- else:
- for x in f(*args, **kwargs):
- yield x
-
- # Choose the right skipper to use when building the actual generator.
- if nose.util.isgenerator(f):
- skipper = skipper_gen
- else:
- skipper = skipper_func
-
- return nose.tools.make_decorator(f)(skipper)
-
- return skip_decorator
# A version with the condition set to true, common case just to attach a message
# to a skip decorator
@@ -254,12 +117,7 @@ def skip(msg=None):
def onlyif(condition, msg):
"""The reverse from skipif, see skipif for details."""
- if callable(condition):
- skip_condition = lambda : not condition()
- else:
- skip_condition = lambda : not condition
-
- return skipif(skip_condition, msg)
+ return skipif(not condition, msg)
#-----------------------------------------------------------------------------
# Utility functions for decorators
@@ -278,20 +136,6 @@ def module_not_available(module):
return mod_not_avail
-def decorated_dummy(dec, name):
- """Return a dummy function decorated with dec, with the given name.
-
- Examples
- --------
- import IPython.testing.decorators as dec
- setup = dec.decorated_dummy(dec.skip_if_no_x11, __name__)
- """
- warnings.warn("The function `decorated_dummy` is deprecated since IPython 4.0",
- DeprecationWarning, stacklevel=2)
- dummy = lambda: None
- dummy.__name__ = name
- return dec(dummy)
-
#-----------------------------------------------------------------------------
# Decorators for public use
@@ -308,9 +152,6 @@ skip_if_not_win32 = skipif(sys.platform != 'win32',
"This test only runs under Windows")
skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
"This test only runs under Linux")
-skip_if_not_osx = skipif(sys.platform != 'darwin',
- "This test only runs under OSX")
-
_x11_skip_cond = (sys.platform not in ('darwin', 'win32') and
os.environ.get('DISPLAY', '') == '')
@@ -318,17 +159,6 @@ _x11_skip_msg = "Skipped under *nix when X11/XOrg not available"
skip_if_no_x11 = skipif(_x11_skip_cond, _x11_skip_msg)
-
-# Decorators to skip certain tests on specific platform/python combinations
-skip_win32_py38 = skipif(sys.version_info > (3,8) and os.name == 'nt')
-
-
-# not a decorator itself, returns a dummy function to be used as setup
-def skip_file_no_x11(name):
- warnings.warn("The function `skip_file_no_x11` is deprecated since IPython 4.0",
- DeprecationWarning, stacklevel=2)
- return decorated_dummy(skip_if_no_x11, name) if _x11_skip_cond else None
-
# Other skip decorators
# generic skip without module
@@ -338,10 +168,6 @@ skipif_not_numpy = skip_without('numpy')
skipif_not_matplotlib = skip_without('matplotlib')
-skipif_not_sympy = skip_without('sympy')
-
-skip_known_failure = knownfailureif(True,'This test is known to fail')
-
# A null 'decorator', useful to make more readable code that needs to pick
# between different decorators based on OS or other conditions
null_deco = lambda f: f
@@ -364,20 +190,13 @@ 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):
- return skip("This test runs only if command '{0}' "
- "is installed".format(cmd))
- return null_deco
+ import pytest
-def onlyif_any_cmd_exists(*commands):
- """
- Decorator to skip test unless at least one of `commands` is found.
- """
- warnings.warn("The function `onlyif_any_cmd_exists` is deprecated since IPython 4.0",
- DeprecationWarning, stacklevel=2)
- for cmd in commands:
- if shutil.which(cmd):
- return null_deco
- return skip("This test runs only if one of the commands {0} "
- "is installed".format(commands))
+ return pytest.mark.skip(reason=reason)
+ return null_deco
diff --git a/contrib/python/ipython/py3/IPython/testing/globalipapp.py b/contrib/python/ipython/py3/IPython/testing/globalipapp.py
index c435f9d087..698e3d845a 100644
--- a/contrib/python/ipython/py3/IPython/testing/globalipapp.py
+++ b/contrib/python/ipython/py3/IPython/testing/globalipapp.py
@@ -14,6 +14,8 @@ import sys
import types
import warnings
+from pathlib import Path
+
from . import tools
from IPython.core import page
@@ -21,30 +23,6 @@ from IPython.utils import io
from IPython.terminal.interactiveshell import TerminalInteractiveShell
-class StreamProxy(io.IOStream):
- """Proxy for sys.stdout/err. This will request the stream *at call time*
- allowing for nose's Capture plugin's redirection of sys.stdout/err.
-
- Parameters
- ----------
- name : str
- The name of the stream. This will be requested anew at every call
- """
-
- def __init__(self, name):
- warnings.warn("StreamProxy is deprecated and unused as of IPython 5", DeprecationWarning,
- stacklevel=2,
- )
- self.name=name
-
- @property
- def stream(self):
- return getattr(sys, self.name)
-
- def flush(self):
- self.stream.flush()
-
-
def get_ipython():
# This will get replaced by the real thing once we start IPython below
return start_ipython()
@@ -95,7 +73,7 @@ def start_ipython():
# A few more tweaks needed for playing nicely with doctests...
# remove history file
- shell.tempfiles.append(config.HistoryManager.hist_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.
diff --git a/contrib/python/ipython/py3/IPython/testing/iptest.py b/contrib/python/ipython/py3/IPython/testing/iptest.py
deleted file mode 100644
index 8efcc97201..0000000000
--- a/contrib/python/ipython/py3/IPython/testing/iptest.py
+++ /dev/null
@@ -1,460 +0,0 @@
-# -*- coding: utf-8 -*-
-"""IPython Test Suite Runner.
-
-This module provides a main entry point to a user script to test IPython
-itself from the command line. There are two ways of running this script:
-
-1. With the syntax `iptest all`. This runs our entire test suite by
- calling this script (with different arguments) recursively. This
- causes modules and package to be tested in different processes, using nose
- or trial where appropriate.
-2. With the regular nose syntax, like `iptest IPython -- -vvs`. In this form
- the script simply calls nose, but with special command line flags and
- plugins loaded. Options after `--` are passed to nose.
-
-"""
-
-# Copyright (c) IPython Development Team.
-# Distributed under the terms of the Modified BSD License.
-
-
-import glob
-from io import BytesIO
-import os
-import os.path as path
-import sys
-from threading import Thread, Lock, Event
-import warnings
-
-import nose.plugins.builtin
-from nose.plugins.xunit import Xunit
-from nose import SkipTest
-from nose.core import TestProgram
-from nose.plugins import Plugin
-from nose.util import safe_str
-
-from IPython import version_info
-from IPython.utils.py3compat import decode
-from IPython.utils.importstring import import_item
-from IPython.testing.plugin.ipdoctest import IPythonDoctest
-from IPython.external.decorators import KnownFailure, knownfailureif
-
-pjoin = path.join
-
-
-# Enable printing all warnings raise by IPython's modules
-warnings.filterwarnings('ignore', message='.*Matplotlib is building the font cache.*', category=UserWarning, module='.*')
-warnings.filterwarnings('error', message='.*', category=ResourceWarning, module='.*')
-warnings.filterwarnings('error', message=".*{'config': True}.*", category=DeprecationWarning, module='IPy.*')
-warnings.filterwarnings('default', message='.*', category=Warning, module='IPy.*')
-
-warnings.filterwarnings('error', message='.*apply_wrapper.*', category=DeprecationWarning, module='.*')
-warnings.filterwarnings('error', message='.*make_label_dec', category=DeprecationWarning, module='.*')
-warnings.filterwarnings('error', message='.*decorated_dummy.*', category=DeprecationWarning, module='.*')
-warnings.filterwarnings('error', message='.*skip_file_no_x11.*', category=DeprecationWarning, module='.*')
-warnings.filterwarnings('error', message='.*onlyif_any_cmd_exists.*', category=DeprecationWarning, module='.*')
-
-warnings.filterwarnings('error', message='.*disable_gui.*', category=DeprecationWarning, module='.*')
-
-warnings.filterwarnings('error', message='.*ExceptionColors global is deprecated.*', category=DeprecationWarning, module='.*')
-
-# Jedi older versions
-warnings.filterwarnings(
- 'error', message='.*elementwise != comparison failed and.*', category=FutureWarning, module='.*')
-
-if version_info < (6,):
- # nose.tools renames all things from `camelCase` to `snake_case` which raise an
- # warning with the runner they also import from standard import library. (as of Dec 2015)
- # Ignore, let's revisit that in a couple of years for IPython 6.
- warnings.filterwarnings(
- 'ignore', message='.*Please use assertEqual instead', category=Warning, module='IPython.*')
-
-if version_info < (8,):
- warnings.filterwarnings('ignore', message='.*Completer.complete.*',
- category=PendingDeprecationWarning, module='.*')
-else:
- warnings.warn(
- 'Completer.complete was pending deprecation and should be changed to Deprecated', FutureWarning)
-
-
-
-# ------------------------------------------------------------------------------
-# Monkeypatch Xunit to count known failures as skipped.
-# ------------------------------------------------------------------------------
-def monkeypatch_xunit():
- try:
- dec.knownfailureif(True)(lambda: None)()
- except Exception as e:
- KnownFailureTest = type(e)
-
- def addError(self, test, err, capt=None):
- if issubclass(err[0], KnownFailureTest):
- err = (SkipTest,) + err[1:]
- return self.orig_addError(test, err, capt)
-
- Xunit.orig_addError = Xunit.addError
- Xunit.addError = addError
-
-#-----------------------------------------------------------------------------
-# Check which dependencies are installed and greater than minimum version.
-#-----------------------------------------------------------------------------
-def extract_version(mod):
- return mod.__version__
-
-def test_for(item, min_version=None, callback=extract_version):
- """Test to see if item is importable, and optionally check against a minimum
- version.
-
- If min_version is given, the default behavior is to check against the
- `__version__` attribute of the item, but specifying `callback` allows you to
- extract the value you are interested in. e.g::
-
- In [1]: import sys
-
- In [2]: from IPython.testing.iptest import test_for
-
- In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
- Out[3]: True
-
- """
- try:
- check = import_item(item)
- except (ImportError, RuntimeError):
- # GTK reports Runtime error if it can't be initialized even if it's
- # importable.
- return False
- else:
- if min_version:
- if callback:
- # extra processing step to get version to compare
- check = callback(check)
-
- return check >= min_version
- else:
- return True
-
-# Global dict where we can store information on what we have and what we don't
-# have available at test run time
-have = {'matplotlib': test_for('matplotlib'),
- 'pygments': test_for('pygments'),
- 'sqlite3': test_for('sqlite3')}
-
-#-----------------------------------------------------------------------------
-# Test suite definitions
-#-----------------------------------------------------------------------------
-
-test_group_names = ['core',
- 'extensions', 'lib', 'terminal', 'testing', 'utils',
- ]
-
-class TestSection(object):
- def __init__(self, name, includes):
- self.name = name
- self.includes = includes
- self.excludes = []
- self.dependencies = []
- self.enabled = True
-
- def exclude(self, module):
- if not module.startswith('IPython'):
- module = self.includes[0] + "." + module
- self.excludes.append(module.replace('.', os.sep))
-
- def requires(self, *packages):
- self.dependencies.extend(packages)
-
- @property
- def will_run(self):
- return self.enabled and all(have[p] for p in self.dependencies)
-
-# Name -> (include, exclude, dependencies_met)
-test_sections = {n:TestSection(n, ['IPython.%s' % n]) for n in test_group_names}
-
-
-# Exclusions and dependencies
-# ---------------------------
-
-# core:
-sec = test_sections['core']
-if not have['sqlite3']:
- sec.exclude('tests.test_history')
- sec.exclude('history')
-if not have['matplotlib']:
- sec.exclude('pylabtools'),
- sec.exclude('tests.test_pylabtools')
-
-# lib:
-sec = test_sections['lib']
-sec.exclude('kernel')
-if not have['pygments']:
- sec.exclude('tests.test_lexers')
-# We do this unconditionally, so that the test suite doesn't import
-# gtk, changing the default encoding and masking some unicode bugs.
-sec.exclude('inputhookgtk')
-# We also do this unconditionally, because wx can interfere with Unix signals.
-# There are currently no tests for it anyway.
-sec.exclude('inputhookwx')
-# Testing inputhook will need a lot of thought, to figure out
-# how to have tests that don't lock up with the gui event
-# loops in the picture
-sec.exclude('inputhook')
-
-# testing:
-sec = test_sections['testing']
-# These have to be skipped on win32 because they use echo, rm, cd, etc.
-# See ticket https://github.com/ipython/ipython/issues/87
-if sys.platform == 'win32':
- sec.exclude('plugin.test_exampleip')
- sec.exclude('plugin.dtexample')
-
-# don't run jupyter_console tests found via shim
-test_sections['terminal'].exclude('console')
-
-# extensions:
-sec = test_sections['extensions']
-# This is deprecated in favour of rpy2
-sec.exclude('rmagic')
-# autoreload does some strange stuff, so move it to its own test section
-sec.exclude('autoreload')
-sec.exclude('tests.test_autoreload')
-test_sections['autoreload'] = TestSection('autoreload',
- ['IPython.extensions.autoreload', 'IPython.extensions.tests.test_autoreload'])
-test_group_names.append('autoreload')
-
-
-#-----------------------------------------------------------------------------
-# Functions and classes
-#-----------------------------------------------------------------------------
-
-def check_exclusions_exist():
- from IPython.paths import get_ipython_package_dir
- from warnings import warn
- parent = os.path.dirname(get_ipython_package_dir())
- for sec in test_sections:
- for pattern in sec.exclusions:
- fullpath = pjoin(parent, pattern)
- if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
- warn("Excluding nonexistent file: %r" % pattern)
-
-
-class ExclusionPlugin(Plugin):
- """A nose plugin to effect our exclusions of files and directories.
- """
- name = 'exclusions'
- score = 3000 # Should come before any other plugins
-
- def __init__(self, exclude_patterns=None):
- """
- Parameters
- ----------
-
- exclude_patterns : sequence of strings, optional
- Filenames containing these patterns (as raw strings, not as regular
- expressions) are excluded from the tests.
- """
- self.exclude_patterns = exclude_patterns or []
- super(ExclusionPlugin, self).__init__()
-
- def options(self, parser, env=os.environ):
- Plugin.options(self, parser, env)
-
- def configure(self, options, config):
- Plugin.configure(self, options, config)
- # Override nose trying to disable plugin.
- self.enabled = True
-
- def wantFile(self, filename):
- """Return whether the given filename should be scanned for tests.
- """
- if any(pat in filename for pat in self.exclude_patterns):
- return False
- return None
-
- def wantDirectory(self, directory):
- """Return whether the given directory should be scanned for tests.
- """
- if any(pat in directory for pat in self.exclude_patterns):
- return False
- return None
-
-
-class StreamCapturer(Thread):
- daemon = True # Don't hang if main thread crashes
- started = False
- def __init__(self, echo=False):
- super(StreamCapturer, self).__init__()
- self.echo = echo
- self.streams = []
- self.buffer = BytesIO()
- self.readfd, self.writefd = os.pipe()
- self.buffer_lock = Lock()
- self.stop = Event()
-
- def run(self):
- self.started = True
-
- while not self.stop.is_set():
- chunk = os.read(self.readfd, 1024)
-
- with self.buffer_lock:
- self.buffer.write(chunk)
- if self.echo:
- sys.stdout.write(decode(chunk))
-
- os.close(self.readfd)
- os.close(self.writefd)
-
- def reset_buffer(self):
- with self.buffer_lock:
- self.buffer.truncate(0)
- self.buffer.seek(0)
-
- def get_buffer(self):
- with self.buffer_lock:
- return self.buffer.getvalue()
-
- def ensure_started(self):
- if not self.started:
- self.start()
-
- def halt(self):
- """Safely stop the thread."""
- if not self.started:
- return
-
- self.stop.set()
- os.write(self.writefd, b'\0') # Ensure we're not locked in a read()
- self.join()
-
-class SubprocessStreamCapturePlugin(Plugin):
- name='subprocstreams'
- def __init__(self):
- Plugin.__init__(self)
- self.stream_capturer = StreamCapturer()
- self.destination = os.environ.get('IPTEST_SUBPROC_STREAMS', 'capture')
- # This is ugly, but distant parts of the test machinery need to be able
- # to redirect streams, so we make the object globally accessible.
- nose.iptest_stdstreams_fileno = self.get_write_fileno
-
- def get_write_fileno(self):
- if self.destination == 'capture':
- self.stream_capturer.ensure_started()
- return self.stream_capturer.writefd
- elif self.destination == 'discard':
- return os.open(os.devnull, os.O_WRONLY)
- else:
- return sys.__stdout__.fileno()
-
- def configure(self, options, config):
- Plugin.configure(self, options, config)
- # Override nose trying to disable plugin.
- if self.destination == 'capture':
- self.enabled = True
-
- def startTest(self, test):
- # Reset log capture
- self.stream_capturer.reset_buffer()
-
- def formatFailure(self, test, err):
- # Show output
- ec, ev, tb = err
- captured = self.stream_capturer.get_buffer().decode('utf-8', 'replace')
- if captured.strip():
- ev = safe_str(ev)
- out = [ev, '>> begin captured subprocess output <<',
- captured,
- '>> end captured subprocess output <<']
- return ec, '\n'.join(out), tb
-
- return err
-
- formatError = formatFailure
-
- def finalize(self, result):
- self.stream_capturer.halt()
-
-
-def run_iptest():
- """Run the IPython test suite using nose.
-
- This function is called when this script is **not** called with the form
- `iptest all`. It simply calls nose with appropriate command line flags
- and accepts all of the standard nose arguments.
- """
- # Apply our monkeypatch to Xunit
- if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
- monkeypatch_xunit()
-
- arg1 = sys.argv[1]
- if arg1.startswith('IPython/'):
- if arg1.endswith('.py'):
- arg1 = arg1[:-3]
- sys.argv[1] = arg1.replace('/', '.')
-
- arg1 = sys.argv[1]
- if arg1 in test_sections:
- section = test_sections[arg1]
- sys.argv[1:2] = section.includes
- elif arg1.startswith('IPython.') and arg1[8:] in test_sections:
- section = test_sections[arg1[8:]]
- sys.argv[1:2] = section.includes
- else:
- section = TestSection(arg1, includes=[arg1])
-
-
- argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
- # We add --exe because of setuptools' imbecility (it
- # blindly does chmod +x on ALL files). Nose does the
- # right thing and it tries to avoid executables,
- # setuptools unfortunately forces our hand here. This
- # has been discussed on the distutils list and the
- # setuptools devs refuse to fix this problem!
- '--exe',
- ]
- if '-a' not in argv and '-A' not in argv:
- argv = argv + ['-a', '!crash']
-
- if nose.__version__ >= '0.11':
- # I don't fully understand why we need this one, but depending on what
- # directory the test suite is run from, if we don't give it, 0 tests
- # get run. Specifically, if the test suite is run from the source dir
- # with an argument (like 'iptest.py IPython.core', 0 tests are run,
- # even if the same call done in this directory works fine). It appears
- # that if the requested package is in the current dir, nose bails early
- # by default. Since it's otherwise harmless, leave it in by default
- # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
- argv.append('--traverse-namespace')
-
- plugins = [ ExclusionPlugin(section.excludes), KnownFailure(),
- SubprocessStreamCapturePlugin() ]
-
- # we still have some vestigial doctests in core
- if (section.name.startswith(('core', 'IPython.core', 'IPython.utils'))):
- plugins.append(IPythonDoctest())
- argv.extend([
- '--with-ipdoctest',
- '--ipdoctest-tests',
- '--ipdoctest-extension=txt',
- ])
-
-
- # Use working directory set by parent process (see iptestcontroller)
- if 'IPTEST_WORKING_DIR' in os.environ:
- os.chdir(os.environ['IPTEST_WORKING_DIR'])
-
- # We need a global ipython running in this process, but the special
- # in-process group spawns its own IPython kernels, so for *that* group we
- # must avoid also opening the global one (otherwise there's a conflict of
- # singletons). Ultimately the solution to this problem is to refactor our
- # assumptions about what needs to be a singleton and what doesn't (app
- # objects should, individual shells shouldn't). But for now, this
- # workaround allows the test suite for the inprocess module to complete.
- if 'kernel.inprocess' not in section.name:
- from IPython.testing import globalipapp
- globalipapp.start_ipython()
-
- # Now nose can run
- TestProgram(argv=argv, addplugins=plugins)
-
-if __name__ == '__main__':
- run_iptest()
diff --git a/contrib/python/ipython/py3/IPython/testing/iptestcontroller.py b/contrib/python/ipython/py3/IPython/testing/iptestcontroller.py
deleted file mode 100644
index b522f60f37..0000000000
--- a/contrib/python/ipython/py3/IPython/testing/iptestcontroller.py
+++ /dev/null
@@ -1,491 +0,0 @@
-# -*- coding: utf-8 -*-
-"""IPython Test Process Controller
-
-This module runs one or more subprocesses which will actually run the IPython
-test suite.
-
-"""
-
-# Copyright (c) IPython Development Team.
-# Distributed under the terms of the Modified BSD License.
-
-
-import argparse
-import multiprocessing.pool
-import os
-import stat
-import shutil
-import signal
-import sys
-import subprocess
-import time
-
-from .iptest import (
- have, test_group_names as py_test_group_names, test_sections, StreamCapturer,
-)
-from IPython.utils.path import compress_user
-from IPython.utils.py3compat import decode
-from IPython.utils.sysinfo import get_sys_info
-from IPython.utils.tempdir import TemporaryDirectory
-
-class TestController:
- """Run tests in a subprocess
- """
- #: str, IPython test suite to be executed.
- section = None
- #: list, command line arguments to be executed
- cmd = None
- #: dict, extra environment variables to set for the subprocess
- env = None
- #: list, TemporaryDirectory instances to clear up when the process finishes
- dirs = None
- #: subprocess.Popen instance
- process = None
- #: str, process stdout+stderr
- stdout = None
-
- def __init__(self):
- self.cmd = []
- self.env = {}
- self.dirs = []
-
- def setUp(self):
- """Create temporary directories etc.
-
- This is only called when we know the test group will be run. Things
- created here may be cleaned up by self.cleanup().
- """
- pass
-
- def launch(self, buffer_output=False, capture_output=False):
- # print('*** ENV:', self.env) # dbg
- # print('*** CMD:', self.cmd) # dbg
- env = os.environ.copy()
- env.update(self.env)
- if buffer_output:
- capture_output = True
- self.stdout_capturer = c = StreamCapturer(echo=not buffer_output)
- c.start()
- stdout = c.writefd if capture_output else None
- stderr = subprocess.STDOUT if capture_output else None
- self.process = subprocess.Popen(self.cmd, stdout=stdout,
- stderr=stderr, env=env)
-
- def wait(self):
- self.process.wait()
- self.stdout_capturer.halt()
- self.stdout = self.stdout_capturer.get_buffer()
- return self.process.returncode
-
- def cleanup_process(self):
- """Cleanup on exit by killing any leftover processes."""
- subp = self.process
- if subp is None or (subp.poll() is not None):
- return # Process doesn't exist, or is already dead.
-
- try:
- print('Cleaning up stale PID: %d' % subp.pid)
- subp.kill()
- except: # (OSError, WindowsError) ?
- # This is just a best effort, if we fail or the process was
- # really gone, ignore it.
- pass
- else:
- for i in range(10):
- if subp.poll() is None:
- time.sleep(0.1)
- else:
- break
-
- if subp.poll() is None:
- # The process did not die...
- print('... failed. Manual cleanup may be required.')
-
- def cleanup(self):
- "Kill process if it's still alive, and clean up temporary directories"
- self.cleanup_process()
- for td in self.dirs:
- td.cleanup()
-
- __del__ = cleanup
-
-
-class PyTestController(TestController):
- """Run Python tests using IPython.testing.iptest"""
- #: str, Python command to execute in subprocess
- pycmd = None
-
- def __init__(self, section, options):
- """Create new test runner."""
- TestController.__init__(self)
- self.section = section
- # pycmd is put into cmd[2] in PyTestController.launch()
- self.cmd = [sys.executable, '-c', None, section]
- self.pycmd = "from IPython.testing.iptest import run_iptest; run_iptest()"
- self.options = options
-
- def setup(self):
- ipydir = TemporaryDirectory()
- self.dirs.append(ipydir)
- self.env['IPYTHONDIR'] = ipydir.name
- self.workingdir = workingdir = TemporaryDirectory()
- self.dirs.append(workingdir)
- self.env['IPTEST_WORKING_DIR'] = workingdir.name
- # This means we won't get odd effects from our own matplotlib config
- self.env['MPLCONFIGDIR'] = workingdir.name
- # For security reasons (http://bugs.python.org/issue16202), use
- # a temporary directory to which other users have no access.
- self.env['TMPDIR'] = workingdir.name
-
- # Add a non-accessible directory to PATH (see gh-7053)
- noaccess = os.path.join(self.workingdir.name, "_no_access_")
- self.noaccess = noaccess
- os.mkdir(noaccess, 0)
-
- PATH = os.environ.get('PATH', '')
- if PATH:
- PATH = noaccess + os.pathsep + PATH
- else:
- PATH = noaccess
- self.env['PATH'] = PATH
-
- # From options:
- if self.options.xunit:
- self.add_xunit()
- if self.options.coverage:
- self.add_coverage()
- self.env['IPTEST_SUBPROC_STREAMS'] = self.options.subproc_streams
- self.cmd.extend(self.options.extra_args)
-
- def cleanup(self):
- """
- Make the non-accessible directory created in setup() accessible
- again, otherwise deleting the workingdir will fail.
- """
- os.chmod(self.noaccess, stat.S_IRWXU)
- TestController.cleanup(self)
-
- @property
- def will_run(self):
- try:
- return test_sections[self.section].will_run
- except KeyError:
- return True
-
- def add_xunit(self):
- xunit_file = os.path.abspath(self.section + '.xunit.xml')
- self.cmd.extend(['--with-xunit', '--xunit-file', xunit_file])
-
- def add_coverage(self):
- try:
- sources = test_sections[self.section].includes
- except KeyError:
- sources = ['IPython']
-
- coverage_rc = ("[run]\n"
- "data_file = {data_file}\n"
- "source =\n"
- " {source}\n"
- ).format(data_file=os.path.abspath('.coverage.'+self.section),
- source="\n ".join(sources))
- config_file = os.path.join(self.workingdir.name, '.coveragerc')
- with open(config_file, 'w') as f:
- f.write(coverage_rc)
-
- self.env['COVERAGE_PROCESS_START'] = config_file
- self.pycmd = "import coverage; coverage.process_startup(); " + self.pycmd
-
- def launch(self, buffer_output=False):
- self.cmd[2] = self.pycmd
- super(PyTestController, self).launch(buffer_output=buffer_output)
-
-
-def prepare_controllers(options):
- """Returns two lists of TestController instances, those to run, and those
- not to run."""
- testgroups = options.testgroups
- if not testgroups:
- testgroups = py_test_group_names
-
- controllers = [PyTestController(name, options) for name in testgroups]
-
- to_run = [c for c in controllers if c.will_run]
- not_run = [c for c in controllers if not c.will_run]
- return to_run, not_run
-
-def do_run(controller, buffer_output=True):
- """Setup and run a test controller.
-
- If buffer_output is True, no output is displayed, to avoid it appearing
- interleaved. In this case, the caller is responsible for displaying test
- output on failure.
-
- Returns
- -------
- controller : TestController
- The same controller as passed in, as a convenience for using map() type
- APIs.
- exitcode : int
- The exit code of the test subprocess. Non-zero indicates failure.
- """
- try:
- try:
- controller.setup()
- controller.launch(buffer_output=buffer_output)
- except Exception:
- import traceback
- traceback.print_exc()
- return controller, 1 # signal failure
-
- exitcode = controller.wait()
- return controller, exitcode
-
- except KeyboardInterrupt:
- return controller, -signal.SIGINT
- finally:
- controller.cleanup()
-
-def report():
- """Return a string with a summary report of test-related variables."""
- inf = get_sys_info()
- out = []
- def _add(name, value):
- out.append((name, value))
-
- _add('IPython version', inf['ipython_version'])
- _add('IPython commit', "{} ({})".format(inf['commit_hash'], inf['commit_source']))
- _add('IPython package', compress_user(inf['ipython_path']))
- _add('Python version', inf['sys_version'].replace('\n',''))
- _add('sys.executable', compress_user(inf['sys_executable']))
- _add('Platform', inf['platform'])
-
- width = max(len(n) for (n,v) in out)
- out = ["{:<{width}}: {}\n".format(n, v, width=width) for (n,v) in out]
-
- avail = []
- not_avail = []
-
- for k, is_avail in have.items():
- if is_avail:
- avail.append(k)
- else:
- not_avail.append(k)
-
- if avail:
- out.append('\nTools and libraries available at test time:\n')
- avail.sort()
- out.append(' ' + ' '.join(avail)+'\n')
-
- if not_avail:
- out.append('\nTools and libraries NOT available at test time:\n')
- not_avail.sort()
- out.append(' ' + ' '.join(not_avail)+'\n')
-
- return ''.join(out)
-
-def run_iptestall(options):
- """Run the entire IPython test suite by calling nose and trial.
-
- This function constructs :class:`IPTester` instances for all IPython
- modules and package and then runs each of them. This causes the modules
- and packages of IPython to be tested each in their own subprocess using
- nose.
-
- Parameters
- ----------
-
- All parameters are passed as attributes of the options object.
-
- testgroups : list of str
- Run only these sections of the test suite. If empty, run all the available
- sections.
-
- fast : int or None
- Run the test suite in parallel, using n simultaneous processes. If None
- is passed, one process is used per CPU core. Default 1 (i.e. sequential)
-
- inc_slow : bool
- Include slow tests. By default, these tests aren't run.
-
- url : unicode
- Address:port to use when running the JS tests.
-
- xunit : bool
- Produce Xunit XML output. This is written to multiple foo.xunit.xml files.
-
- coverage : bool or str
- Measure code coverage from tests. True will store the raw coverage data,
- or pass 'html' or 'xml' to get reports.
-
- extra_args : list
- Extra arguments to pass to the test subprocesses, e.g. '-v'
- """
- to_run, not_run = prepare_controllers(options)
-
- def justify(ltext, rtext, width=70, fill='-'):
- ltext += ' '
- rtext = (' ' + rtext).rjust(width - len(ltext), fill)
- return ltext + rtext
-
- # Run all test runners, tracking execution time
- failed = []
- t_start = time.time()
-
- print()
- if options.fast == 1:
- # This actually means sequential, i.e. with 1 job
- for controller in to_run:
- print('Test group:', controller.section)
- sys.stdout.flush() # Show in correct order when output is piped
- controller, res = do_run(controller, buffer_output=False)
- if res:
- failed.append(controller)
- if res == -signal.SIGINT:
- print("Interrupted")
- break
- print()
-
- else:
- # Run tests concurrently
- try:
- pool = multiprocessing.pool.ThreadPool(options.fast)
- for (controller, res) in pool.imap_unordered(do_run, to_run):
- res_string = 'OK' if res == 0 else 'FAILED'
- print(justify('Test group: ' + controller.section, res_string))
- if res:
- print(decode(controller.stdout))
- failed.append(controller)
- if res == -signal.SIGINT:
- print("Interrupted")
- break
- except KeyboardInterrupt:
- return
-
- for controller in not_run:
- print(justify('Test group: ' + controller.section, 'NOT RUN'))
-
- t_end = time.time()
- t_tests = t_end - t_start
- nrunners = len(to_run)
- nfail = len(failed)
- # summarize results
- print('_'*70)
- print('Test suite completed for system with the following information:')
- print(report())
- took = "Took %.3fs." % t_tests
- print('Status: ', end='')
- if not failed:
- print('OK (%d test groups).' % nrunners, took)
- else:
- # If anything went wrong, point out what command to rerun manually to
- # see the actual errors and individual summary
- failed_sections = [c.section for c in failed]
- print('ERROR - {} out of {} test groups failed ({}).'.format(nfail,
- nrunners, ', '.join(failed_sections)), took)
- print()
- print('You may wish to rerun these, with:')
- print(' iptest', *failed_sections)
- print()
-
- if options.coverage:
- from coverage import coverage, CoverageException
- cov = coverage(data_file='.coverage')
- cov.combine()
- cov.save()
-
- # Coverage HTML report
- if options.coverage == 'html':
- html_dir = 'ipy_htmlcov'
- shutil.rmtree(html_dir, ignore_errors=True)
- print("Writing HTML coverage report to %s/ ... " % html_dir, end="")
- sys.stdout.flush()
-
- # Custom HTML reporter to clean up module names.
- from coverage.html import HtmlReporter
- class CustomHtmlReporter(HtmlReporter):
- def find_code_units(self, morfs):
- super(CustomHtmlReporter, self).find_code_units(morfs)
- for cu in self.code_units:
- nameparts = cu.name.split(os.sep)
- if 'IPython' not in nameparts:
- continue
- ix = nameparts.index('IPython')
- cu.name = '.'.join(nameparts[ix:])
-
- # Reimplement the html_report method with our custom reporter
- cov.get_data()
- cov.config.from_args(omit='*{0}tests{0}*'.format(os.sep), html_dir=html_dir,
- html_title='IPython test coverage',
- )
- reporter = CustomHtmlReporter(cov, cov.config)
- reporter.report(None)
- print('done.')
-
- # Coverage XML report
- elif options.coverage == 'xml':
- try:
- cov.xml_report(outfile='ipy_coverage.xml')
- except CoverageException as e:
- print('Generating coverage report failed. Are you running javascript tests only?')
- import traceback
- traceback.print_exc()
-
- if failed:
- # Ensure that our exit code indicates failure
- sys.exit(1)
-
-argparser = argparse.ArgumentParser(description='Run IPython test suite')
-argparser.add_argument('testgroups', nargs='*',
- help='Run specified groups of tests. If omitted, run '
- 'all tests.')
-argparser.add_argument('--all', action='store_true',
- help='Include slow tests not run by default.')
-argparser.add_argument('-j', '--fast', nargs='?', const=None, default=1, type=int,
- help='Run test sections in parallel. This starts as many '
- 'processes as you have cores, or you can specify a number.')
-argparser.add_argument('--xunit', action='store_true',
- help='Produce Xunit XML results')
-argparser.add_argument('--coverage', nargs='?', const=True, default=False,
- help="Measure test coverage. Specify 'html' or "
- "'xml' to get reports.")
-argparser.add_argument('--subproc-streams', default='capture',
- help="What to do with stdout/stderr from subprocesses. "
- "'capture' (default), 'show' and 'discard' are the options.")
-
-def default_options():
- """Get an argparse Namespace object with the default arguments, to pass to
- :func:`run_iptestall`.
- """
- options = argparser.parse_args([])
- options.extra_args = []
- return options
-
-def main():
- # iptest doesn't work correctly if the working directory is the
- # root of the IPython source tree. Tell the user to avoid
- # frustration.
- if os.path.exists(os.path.join(os.getcwd(),
- 'IPython', 'testing', '__main__.py')):
- print("Don't run iptest from the IPython source directory",
- file=sys.stderr)
- sys.exit(1)
- # Arguments after -- should be passed through to nose. Argparse treats
- # everything after -- as regular positional arguments, so we separate them
- # first.
- try:
- ix = sys.argv.index('--')
- except ValueError:
- to_parse = sys.argv[1:]
- extra_args = []
- else:
- to_parse = sys.argv[1:ix]
- extra_args = sys.argv[ix+1:]
-
- options = argparser.parse_args(to_parse)
- options.extra_args = extra_args
-
- run_iptestall(options)
-
-
-if __name__ == '__main__':
- main()
diff --git a/contrib/python/ipython/py3/IPython/testing/plugin/dtexample.py b/contrib/python/ipython/py3/IPython/testing/plugin/dtexample.py
index d73cd246fd..68f7016e34 100644
--- a/contrib/python/ipython/py3/IPython/testing/plugin/dtexample.py
+++ b/contrib/python/ipython/py3/IPython/testing/plugin/dtexample.py
@@ -4,6 +4,9 @@ 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...
@@ -35,20 +38,8 @@ def ipfunc():
....: print(i, end=' ')
....: print(i+1, end=' ')
....:
- 0 1 1 2 2 3
-
-
- Examples that access the operating system work:
-
- In [1]: !echo hello
- hello
+ 0 1 1 2 2 3
- In [2]: !echo hello > /tmp/foo_iptest
-
- In [3]: !cat /tmp/foo_iptest
- hello
-
- In [4]: rm -f /tmp/foo_iptest
It's OK to use '_' for the last result, but do NOT try to use IPython's
numbered history of _NN outputs, since those won't exist under the
@@ -59,7 +50,7 @@ def ipfunc():
In [8]: print(repr(_))
'hi'
-
+
In [7]: 3+4
Out[7]: 7
@@ -69,7 +60,26 @@ def ipfunc():
In [9]: ipfunc()
Out[9]: 'ipfunc'
"""
- return '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():
diff --git a/contrib/python/ipython/py3/IPython/testing/plugin/ipdoctest.py b/contrib/python/ipython/py3/IPython/testing/plugin/ipdoctest.py
index 3b8667e72f..52cd8fd3b8 100644
--- a/contrib/python/ipython/py3/IPython/testing/plugin/ipdoctest.py
+++ b/contrib/python/ipython/py3/IPython/testing/plugin/ipdoctest.py
@@ -19,33 +19,13 @@ Limitations:
# Module imports
# From the standard library
-import builtins as builtin_mod
import doctest
-import inspect
import logging
import os
import re
-import sys
-from importlib import import_module
-from io import StringIO
from testpath import modified_env
-from inspect import getmodule
-
-# We are overriding the default doctest runner, so we need to import a few
-# things from doctest directly
-from doctest import (REPORTING_FLAGS, REPORT_ONLY_FIRST_FAILURE,
- _unittest_reportflags, DocTestRunner,
- _extract_future_flags, pdb, _OutputRedirectingPdb,
- _exception_traceback,
- linecache)
-
-# Third-party modules
-
-from nose.plugins import doctests, Plugin
-from nose.util import anyp, tolist
-
#-----------------------------------------------------------------------------
# Module globals and other constants
#-----------------------------------------------------------------------------
@@ -57,114 +37,16 @@ log = logging.getLogger(__name__)
# Classes and functions
#-----------------------------------------------------------------------------
-def is_extension_module(filename):
- """Return whether the given filename is an extension module.
-
- This simply checks that the extension is either .so or .pyd.
- """
- return os.path.splitext(filename)[1].lower() in ('.so','.pyd')
-
-
-class DocTestSkip(object):
- """Object wrapper for doctests to be skipped."""
-
- ds_skip = """Doctest to skip.
- >>> 1 #doctest: +SKIP
- """
-
- def __init__(self,obj):
- self.obj = obj
- def __getattribute__(self,key):
- if key == '__doc__':
- return DocTestSkip.ds_skip
- else:
- return getattr(object.__getattribute__(self,'obj'),key)
-
-# Modified version of the one in the stdlib, that fixes a python bug (doctests
-# not found in extension modules, http://bugs.python.org/issue3158)
class DocTestFinder(doctest.DocTestFinder):
+ def _get_test(self, obj, name, module, globs, source_lines):
+ test = super()._get_test(obj, name, module, globs, source_lines)
- def _from_module(self, module, object):
- """
- Return true if the given object is defined in the given
- module.
- """
- if module is None:
- return True
- elif inspect.isfunction(object):
- return module.__dict__ is object.__globals__
- elif inspect.isbuiltin(object):
- return module.__name__ == object.__module__
- elif inspect.isclass(object):
- return module.__name__ == object.__module__
- elif inspect.ismethod(object):
- # This one may be a bug in cython that fails to correctly set the
- # __module__ attribute of methods, but since the same error is easy
- # to make by extension code writers, having this safety in place
- # isn't such a bad idea
- return module.__name__ == object.__self__.__class__.__module__
- elif inspect.getmodule(object) is not None:
- return module is inspect.getmodule(object)
- elif hasattr(object, '__module__'):
- return module.__name__ == object.__module__
- elif isinstance(object, property):
- return True # [XX] no way not be sure.
- elif inspect.ismethoddescriptor(object):
- # Unbound PyQt signals reach this point in Python 3.4b3, and we want
- # to avoid throwing an error. See also http://bugs.python.org/issue3158
- return False
- else:
- raise ValueError("object must be a class or function, got %r" % object)
+ if bool(getattr(obj, "__skip_doctest__", False)) and test is not None:
+ for example in test.examples:
+ example.options[doctest.SKIP] = True
- def _find(self, tests, obj, name, module, source_lines, globs, seen):
- """
- Find tests for the given object and any contained objects, and
- add them to `tests`.
- """
- print('_find for:', obj, name, module) # dbg
- if hasattr(obj,"skip_doctest"):
- #print 'SKIPPING DOCTEST FOR:',obj # dbg
- obj = DocTestSkip(obj)
-
- doctest.DocTestFinder._find(self,tests, obj, name, module,
- source_lines, globs, seen)
-
- # Below we re-run pieces of the above method with manual modifications,
- # because the original code is buggy and fails to correctly identify
- # doctests in extension modules.
-
- # Local shorthands
- from inspect import isroutine, isclass
-
- # Look for tests in a module's contained objects.
- if inspect.ismodule(obj) and self._recurse:
- for valname, val in obj.__dict__.items():
- valname1 = '%s.%s' % (name, valname)
- if ( (isroutine(val) or isclass(val))
- and self._from_module(module, val) ):
-
- self._find(tests, val, valname1, module, source_lines,
- globs, seen)
-
- # Look for tests in a class's contained objects.
- if inspect.isclass(obj) and self._recurse:
- #print 'RECURSE into class:',obj # dbg
- for valname, val in obj.__dict__.items():
- # Special handling for staticmethod/classmethod.
- if isinstance(val, staticmethod):
- val = getattr(obj, valname)
- if isinstance(val, classmethod):
- val = getattr(obj, valname).__func__
-
- # Recurse to methods, properties, and nested classes.
- if ((inspect.isfunction(val) or inspect.isclass(val) or
- inspect.ismethod(val) or
- isinstance(val, property)) and
- self._from_module(module, val)):
- valname = '%s.%s' % (name, valname)
- self._find(tests, val, valname, module, source_lines,
- globs, seen)
+ return test
class IPDoctestOutputChecker(doctest.OutputChecker):
@@ -193,146 +75,11 @@ class IPDoctestOutputChecker(doctest.OutputChecker):
return ret
-class DocTestCase(doctests.DocTestCase):
- """Proxy for DocTestCase: provides an address() method that
- returns the correct address for the doctest case. Otherwise
- acts as a proxy to the test case. To provide hints for address(),
- an obj may also be passed -- this will be used as the test object
- for purposes of determining the test address, if it is provided.
- """
-
- # Note: this method was taken from numpy's nosetester module.
-
- # Subclass nose.plugins.doctests.DocTestCase to work around a bug in
- # its constructor that blocks non-default arguments from being passed
- # down into doctest.DocTestCase
-
- def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
- checker=None, obj=None, result_var='_'):
- self._result_var = result_var
- doctests.DocTestCase.__init__(self, test,
- optionflags=optionflags,
- setUp=setUp, tearDown=tearDown,
- checker=checker)
- # Now we must actually copy the original constructor from the stdlib
- # doctest class, because we can't call it directly and a bug in nose
- # means it never gets passed the right arguments.
-
- self._dt_optionflags = optionflags
- self._dt_checker = checker
- self._dt_test = test
- self._dt_test_globs_ori = test.globs
- self._dt_setUp = setUp
- self._dt_tearDown = tearDown
-
- # XXX - store this runner once in the object!
- runner = IPDocTestRunner(optionflags=optionflags,
- checker=checker, verbose=False)
- self._dt_runner = runner
-
-
- # Each doctest should remember the directory it was loaded from, so
- # things like %run work without too many contortions
- self._ori_dir = os.path.dirname(test.filename)
-
- # Modified runTest from the default stdlib
- def runTest(self):
- test = self._dt_test
- runner = self._dt_runner
-
- old = sys.stdout
- new = StringIO()
- optionflags = self._dt_optionflags
-
- if not (optionflags & REPORTING_FLAGS):
- # The option flags don't include any reporting flags,
- # so add the default reporting flags
- optionflags |= _unittest_reportflags
-
- try:
- # Save our current directory and switch out to the one where the
- # test was originally created, in case another doctest did a
- # directory change. We'll restore this in the finally clause.
- curdir = os.getcwd()
- #print 'runTest in dir:', self._ori_dir # dbg
- os.chdir(self._ori_dir)
-
- runner.DIVIDER = "-"*70
- failures, tries = runner.run(test,out=new.write,
- clear_globs=False)
- finally:
- sys.stdout = old
- os.chdir(curdir)
-
- if failures:
- raise self.failureException(self.format_failure(new.getvalue()))
-
- def setUp(self):
- """Modified test setup that syncs with ipython namespace"""
- #print "setUp test", self._dt_test.examples # dbg
- if isinstance(self._dt_test.examples[0], IPExample):
- # for IPython examples *only*, we swap the globals with the ipython
- # namespace, after updating it with the globals (which doctest
- # fills with the necessary info from the module being tested).
- self.user_ns_orig = {}
- self.user_ns_orig.update(_ip.user_ns)
- _ip.user_ns.update(self._dt_test.globs)
- # We must remove the _ key in the namespace, so that Python's
- # doctest code sets it naturally
- _ip.user_ns.pop('_', None)
- _ip.user_ns['__builtins__'] = builtin_mod
- self._dt_test.globs = _ip.user_ns
-
- super(DocTestCase, self).setUp()
-
- def tearDown(self):
-
- # Undo the test.globs reassignment we made, so that the parent class
- # teardown doesn't destroy the ipython namespace
- if isinstance(self._dt_test.examples[0], IPExample):
- self._dt_test.globs = self._dt_test_globs_ori
- _ip.user_ns.clear()
- _ip.user_ns.update(self.user_ns_orig)
-
- # XXX - fperez: I am not sure if this is truly a bug in nose 0.11, but
- # it does look like one to me: its tearDown method tries to run
- #
- # delattr(builtin_mod, self._result_var)
- #
- # without checking that the attribute really is there; it implicitly
- # assumes it should have been set via displayhook. But if the
- # displayhook was never called, this doesn't necessarily happen. I
- # haven't been able to find a little self-contained example outside of
- # ipython that would show the problem so I can report it to the nose
- # team, but it does happen a lot in our code.
- #
- # So here, we just protect as narrowly as possible by trapping an
- # attribute error whose message would be the name of self._result_var,
- # and letting any other error propagate.
- try:
- super(DocTestCase, self).tearDown()
- except AttributeError as exc:
- if exc.args[0] != self._result_var:
- raise
-
-
# A simple subclassing of the original with a different class name, so we can
# distinguish and treat differently IPython examples from pure python ones.
class IPExample(doctest.Example): pass
-class IPExternalExample(doctest.Example):
- """Doctest examples to be run in an external process."""
-
- def __init__(self, source, want, exc_msg=None, lineno=0, indent=0,
- options=None):
- # Parent constructor
- doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
-
- # An EXTRA newline is needed to prevent pexpect hangs
- self.source += '\n'
-
-
class IPDocTestParser(doctest.DocTestParser):
"""
A class used to parse strings containing doctest examples.
@@ -378,9 +125,6 @@ class IPDocTestParser(doctest.DocTestParser):
# we don't need to modify any other code.
_RANDOM_TEST = re.compile(r'#\s*all-random\s+')
- # Mark tests to be executed in an external process - currently unsupported.
- _EXTERNAL_IP = re.compile(r'#\s*ipdoctest:\s*EXTERNAL')
-
def ip2py(self,source):
"""Convert input IPython source into valid Python."""
block = _ip.input_transformer_manager.transform_cell(source)
@@ -423,27 +167,12 @@ class IPDocTestParser(doctest.DocTestParser):
terms = list(self._EXAMPLE_RE_PY.finditer(string))
if terms:
# Normal Python example
- #print '-'*70 # dbg
- #print 'PyExample, Source:\n',string # dbg
- #print '-'*70 # dbg
Example = doctest.Example
else:
- # It's an ipython example. Note that IPExamples are run
- # in-process, so their syntax must be turned into valid python.
- # IPExternalExamples are run out-of-process (via pexpect) so they
- # don't need any filtering (a real ipython will be executing them).
+ # It's an ipython example.
terms = list(self._EXAMPLE_RE_IP.finditer(string))
- if self._EXTERNAL_IP.search(string):
- #print '-'*70 # dbg
- #print 'IPExternalExample, Source:\n',string # dbg
- #print '-'*70 # dbg
- Example = IPExternalExample
- else:
- #print '-'*70 # dbg
- #print 'IPExample, Source:\n',string # dbg
- #print '-'*70 # dbg
- Example = IPExample
- ip2py = True
+ Example = IPExample
+ ip2py = True
for m in terms:
# Add the pre-example text to `output`.
@@ -458,10 +187,6 @@ class IPDocTestParser(doctest.DocTestParser):
# cases, it's only non-empty for 'all-random' tests):
want += random_marker
- if Example is IPExternalExample:
- options[doctest.NORMALIZE_WHITESPACE] = True
- want += '\n'
-
# Create an Example, and add it to the list.
if not self._IS_BLANK_OR_COMMENT(source):
output.append(Example(source, want, exc_msg,
@@ -569,193 +294,7 @@ class IPDocTestRunner(doctest.DocTestRunner,object):
"""
def run(self, test, compileflags=None, out=None, clear_globs=True):
-
- # Hack: ipython needs access to the execution context of the example,
- # so that it can propagate user variables loaded by %run into
- # test.globs. We put them here into our modified %run as a function
- # attribute. Our new %run will then only make the namespace update
- # when called (rather than unconditionally updating test.globs here
- # for all examples, most of which won't be calling %run anyway).
- #_ip._ipdoctest_test_globs = test.globs
- #_ip._ipdoctest_test_filename = test.filename
-
- test.globs.update(_ip.user_ns)
-
# Override terminal size to standardise traceback format
with modified_env({'COLUMNS': '80', 'LINES': '24'}):
return super(IPDocTestRunner,self).run(test,
compileflags,out,clear_globs)
-
-
-class DocFileCase(doctest.DocFileCase):
- """Overrides to provide filename
- """
- def address(self):
- return (self._dt_test.filename, None, None)
-
-
-class ExtensionDoctest(doctests.Doctest):
- """Nose Plugin that supports doctests in extension modules.
- """
- name = 'extdoctest' # call nosetests with --with-extdoctest
- enabled = True
-
- def options(self, parser, env=os.environ):
- Plugin.options(self, parser, env)
- parser.add_option('--doctest-tests', action='store_true',
- dest='doctest_tests',
- default=env.get('NOSE_DOCTEST_TESTS',True),
- help="Also look for doctests in test modules. "
- "Note that classes, methods and functions should "
- "have either doctests or non-doctest tests, "
- "not both. [NOSE_DOCTEST_TESTS]")
- parser.add_option('--doctest-extension', action="append",
- dest="doctestExtension",
- help="Also look for doctests in files with "
- "this extension [NOSE_DOCTEST_EXTENSION]")
- # Set the default as a list, if given in env; otherwise
- # an additional value set on the command line will cause
- # an error.
- env_setting = env.get('NOSE_DOCTEST_EXTENSION')
- if env_setting is not None:
- parser.set_defaults(doctestExtension=tolist(env_setting))
-
-
- def configure(self, options, config):
- Plugin.configure(self, options, config)
- # Pull standard doctest plugin out of config; we will do doctesting
- config.plugins.plugins = [p for p in config.plugins.plugins
- if p.name != 'doctest']
- self.doctest_tests = options.doctest_tests
- self.extension = tolist(options.doctestExtension)
-
- self.parser = doctest.DocTestParser()
- self.finder = DocTestFinder()
- self.checker = IPDoctestOutputChecker()
- self.globs = None
- self.extraglobs = None
-
-
- def loadTestsFromExtensionModule(self,filename):
- bpath,mod = os.path.split(filename)
- modname = os.path.splitext(mod)[0]
- try:
- sys.path.append(bpath)
- module = import_module(modname)
- tests = list(self.loadTestsFromModule(module))
- finally:
- sys.path.pop()
- return tests
-
- # NOTE: the method below is almost a copy of the original one in nose, with
- # a few modifications to control output checking.
-
- def loadTestsFromModule(self, module):
- #print '*** ipdoctest - lTM',module # dbg
-
- if not self.matches(module.__name__):
- log.debug("Doctest doesn't want module %s", module)
- return
-
- tests = self.finder.find(module,globs=self.globs,
- extraglobs=self.extraglobs)
- if not tests:
- return
-
- # always use whitespace and ellipsis options
- optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
-
- tests.sort()
- module_file = module.__file__
- if module_file[-4:] in ('.pyc', '.pyo'):
- module_file = module_file[:-1]
- for test in tests:
- if not test.examples:
- continue
- if not test.filename:
- test.filename = module_file
-
- yield DocTestCase(test,
- optionflags=optionflags,
- checker=self.checker)
-
-
- def loadTestsFromFile(self, filename):
- #print "ipdoctest - from file", filename # dbg
- if is_extension_module(filename):
- for t in self.loadTestsFromExtensionModule(filename):
- yield t
- else:
- if self.extension and anyp(filename.endswith, self.extension):
- name = os.path.basename(filename)
- with open(filename) as dh:
- doc = dh.read()
- test = self.parser.get_doctest(
- doc, globs={'__file__': filename}, name=name,
- filename=filename, lineno=0)
- if test.examples:
- #print 'FileCase:',test.examples # dbg
- yield DocFileCase(test)
- else:
- yield False # no tests to load
-
-
-class IPythonDoctest(ExtensionDoctest):
- """Nose Plugin that supports doctests in extension modules.
- """
- name = 'ipdoctest' # call nosetests with --with-ipdoctest
- enabled = True
-
- def makeTest(self, obj, parent):
- """Look for doctests in the given object, which will be a
- function, method or class.
- """
- #print 'Plugin analyzing:', obj, parent # dbg
- # always use whitespace and ellipsis options
- optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
-
- doctests = self.finder.find(obj, module=getmodule(parent))
- if doctests:
- for test in doctests:
- if len(test.examples) == 0:
- continue
-
- yield DocTestCase(test, obj=obj,
- optionflags=optionflags,
- checker=self.checker)
-
- def options(self, parser, env=os.environ):
- #print "Options for nose plugin:", self.name # dbg
- Plugin.options(self, parser, env)
- parser.add_option('--ipdoctest-tests', action='store_true',
- dest='ipdoctest_tests',
- default=env.get('NOSE_IPDOCTEST_TESTS',True),
- help="Also look for doctests in test modules. "
- "Note that classes, methods and functions should "
- "have either doctests or non-doctest tests, "
- "not both. [NOSE_IPDOCTEST_TESTS]")
- parser.add_option('--ipdoctest-extension', action="append",
- dest="ipdoctest_extension",
- help="Also look for doctests in files with "
- "this extension [NOSE_IPDOCTEST_EXTENSION]")
- # Set the default as a list, if given in env; otherwise
- # an additional value set on the command line will cause
- # an error.
- env_setting = env.get('NOSE_IPDOCTEST_EXTENSION')
- if env_setting is not None:
- parser.set_defaults(ipdoctest_extension=tolist(env_setting))
-
- def configure(self, options, config):
- #print "Configuring nose plugin:", self.name # dbg
- Plugin.configure(self, options, config)
- # Pull standard doctest plugin out of config; we will do doctesting
- config.plugins.plugins = [p for p in config.plugins.plugins
- if p.name != 'doctest']
- self.doctest_tests = options.ipdoctest_tests
- self.extension = tolist(options.ipdoctest_extension)
-
- self.parser = IPDocTestParser()
- self.finder = DocTestFinder(parser=self.parser)
- self.checker = IPDoctestOutputChecker()
- self.globs = None
- self.extraglobs = None
diff --git a/contrib/python/ipython/py3/IPython/testing/plugin/iptest.py b/contrib/python/ipython/py3/IPython/testing/plugin/iptest.py
deleted file mode 100644
index e24e22a830..0000000000
--- a/contrib/python/ipython/py3/IPython/testing/plugin/iptest.py
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/usr/bin/env python
-"""Nose-based test runner.
-"""
-
-from nose.core import main
-from nose.plugins.builtin import plugins
-from nose.plugins.doctests import Doctest
-
-from . import ipdoctest
-from .ipdoctest import IPDocTestRunner
-
-if __name__ == '__main__':
- print('WARNING: this code is incomplete!')
- print()
-
- pp = [x() for x in plugins] # activate all builtin plugins first
- main(testRunner=IPDocTestRunner(),
- plugins=pp+[ipdoctest.IPythonDoctest(),Doctest()])
diff --git a/contrib/python/ipython/py3/IPython/testing/plugin/pytest_ipdoctest.py b/contrib/python/ipython/py3/IPython/testing/plugin/pytest_ipdoctest.py
new file mode 100644
index 0000000000..809713d7c8
--- /dev/null
+++ b/contrib/python/ipython/py3/IPython/testing/plugin/pytest_ipdoctest.py
@@ -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()
diff --git a/contrib/python/ipython/py3/IPython/testing/plugin/show_refs.py b/contrib/python/ipython/py3/IPython/testing/plugin/show_refs.py
deleted file mode 100644
index b2c70adfc1..0000000000
--- a/contrib/python/ipython/py3/IPython/testing/plugin/show_refs.py
+++ /dev/null
@@ -1,19 +0,0 @@
-"""Simple script to show reference holding behavior.
-
-This is used by a companion test case.
-"""
-
-import gc
-
-class C(object):
- def __del__(self):
- pass
- #print 'deleting object...' # dbg
-
-if __name__ == '__main__':
- c = C()
-
- c_refs = gc.get_referrers(c)
- ref_ids = list(map(id,c_refs))
-
- print('c referrers:',list(map(type,c_refs)))
diff --git a/contrib/python/ipython/py3/IPython/testing/plugin/simple.py b/contrib/python/ipython/py3/IPython/testing/plugin/simple.py
index 3861977cab..35fbfd2fbd 100644
--- a/contrib/python/ipython/py3/IPython/testing/plugin/simple.py
+++ b/contrib/python/ipython/py3/IPython/testing/plugin/simple.py
@@ -1,7 +1,7 @@
"""Simple example using doctests.
This file just contains doctests both using plain python and IPython prompts.
-All tests should be loaded by nose.
+All tests should be loaded by Pytest.
"""
def pyfunc():
@@ -24,10 +24,21 @@ def pyfunc():
return 'pyfunc'
-def ipyfunc2():
- """Some pure python tests...
+def ipyfunc():
+ """Some IPython tests...
+
+ In [1]: ipyfunc()
+ Out[1]: 'ipyfunc'
+
+ In [2]: import os
+
+ In [3]: 2+3
+ Out[3]: 5
- >>> 1+1
- 2
+ In [4]: for i in range(3):
+ ...: print(i, end=' ')
+ ...: print(i+1, end=' ')
+ ...:
+ Out[4]: 0 1 1 2 2 3
"""
- return 'pyfunc2'
+ return "ipyfunc"
diff --git a/contrib/python/ipython/py3/IPython/testing/plugin/test_exampleip.txt b/contrib/python/ipython/py3/IPython/testing/plugin/test_exampleip.txt
index 8afcbfdf7d..96b1eae19f 100644
--- a/contrib/python/ipython/py3/IPython/testing/plugin/test_exampleip.txt
+++ b/contrib/python/ipython/py3/IPython/testing/plugin/test_exampleip.txt
@@ -21,7 +21,7 @@ Another example::
Just like in IPython docstrings, you can use all IPython syntax and features::
- In [9]: !echo "hello"
+ In [9]: !echo hello
hello
In [10]: a='hi'
diff --git a/contrib/python/ipython/py3/IPython/testing/plugin/test_ipdoctest.py b/contrib/python/ipython/py3/IPython/testing/plugin/test_ipdoctest.py
index d8f5991636..2686172bb2 100644
--- a/contrib/python/ipython/py3/IPython/testing/plugin/test_ipdoctest.py
+++ b/contrib/python/ipython/py3/IPython/testing/plugin/test_ipdoctest.py
@@ -74,3 +74,19 @@ def doctest_multiline3():
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
+ """
diff --git a/contrib/python/ipython/py3/IPython/testing/plugin/test_refs.py b/contrib/python/ipython/py3/IPython/testing/plugin/test_refs.py
index bd7ad8fb3e..b92448be07 100644
--- a/contrib/python/ipython/py3/IPython/testing/plugin/test_refs.py
+++ b/contrib/python/ipython/py3/IPython/testing/plugin/test_refs.py
@@ -37,10 +37,3 @@ def doctest_ivars():
In [6]: zz
Out[6]: 1
"""
-
-def doctest_refs():
- """DocTest reference holding issues when running scripts.
-
- In [32]: run show_refs.py
- c referrers: [<... 'dict'>]
- """
diff --git a/contrib/python/ipython/py3/IPython/testing/skipdoctest.py b/contrib/python/ipython/py3/IPython/testing/skipdoctest.py
index b0cf83c449..f440ea14b2 100644
--- a/contrib/python/ipython/py3/IPython/testing/skipdoctest.py
+++ b/contrib/python/ipython/py3/IPython/testing/skipdoctest.py
@@ -15,5 +15,5 @@ def skip_doctest(f):
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
+ f.__skip_doctest__ = True
return f
diff --git a/contrib/python/ipython/py3/IPython/testing/tools.py b/contrib/python/ipython/py3/IPython/testing/tools.py
index e7e7285f49..2ff63a6d4a 100644
--- a/contrib/python/ipython/py3/IPython/testing/tools.py
+++ b/contrib/python/ipython/py3/IPython/testing/tools.py
@@ -10,6 +10,7 @@ Authors
# Distributed under the terms of the Modified BSD License.
import os
+from pathlib import Path
import re
import sys
import tempfile
@@ -20,15 +21,6 @@ from io import StringIO
from subprocess import Popen, PIPE
from unittest.mock import patch
-try:
- # These tools are used by parts of the runtime, so we make the nose
- # dependency optional at this point. Nose is a hard dependency to run the
- # test suite, but NOT to use ipython itself.
- import nose.tools as nt
- has_nose = True
-except ImportError:
- has_nose = False
-
from traitlets.config.loader import Config
from IPython.utils.process import get_output_error_code
from IPython.utils.text import list_strings
@@ -142,7 +134,7 @@ def default_config():
config.TerminalTerminalInteractiveShell.term_title = False,
config.TerminalInteractiveShell.autocall = 0
f = tempfile.NamedTemporaryFile(suffix=u'test_hist.sqlite', delete=False)
- config.HistoryManager.hist_file = f.name
+ config.HistoryManager.hist_file = Path(f.name)
f.close()
config.HistoryManager.db_cache_size = 10000
return config
@@ -176,7 +168,7 @@ def ipexec(fname, options=None, commands=()):
Parameters
----------
- fname : str
+ fname : str, Path
Name of file to be executed (should have .py or .ipy extension).
options : optional, list
@@ -189,7 +181,10 @@ def ipexec(fname, options=None, commands=()):
-------
``(stdout, stderr)`` of ipython subprocess.
"""
- if options is None: options = []
+ __tracebackhide__ = True
+
+ if options is None:
+ options = []
cmdargs = default_argv() + options
@@ -204,6 +199,8 @@ def ipexec(fname, options=None, commands=()):
# 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
@@ -229,7 +226,7 @@ def ipexec_validate(fname, expected_out, expected_err='',
Parameters
----------
- fname : str
+ fname : str, Path
Name of the file to be executed (should have .py or .ipy extension).
expected_out : str
@@ -245,8 +242,7 @@ def ipexec_validate(fname, expected_out, expected_err='',
-------
None
"""
-
- import nose.tools as nt
+ __tracebackhide__ = True
out, err = ipexec(fname, options, commands)
#print 'OUT', out # dbg
@@ -255,12 +251,16 @@ def ipexec_validate(fname, expected_out, expected_err='',
# more informative than simply having an empty stdout.
if err:
if expected_err:
- nt.assert_equal("\n".join(err.strip().splitlines()), "\n".join(expected_err.strip().splitlines()))
+ 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
- nt.assert_equal("\n".join(out.strip().splitlines()), "\n".join(expected_out.strip().splitlines()))
+ assert "\n".join(out.strip().splitlines()) == "\n".join(
+ expected_out.strip().splitlines()
+ )
class TempFileMixin(unittest.TestCase):
@@ -320,6 +320,8 @@ def check_pairs(func, pairs):
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)
@@ -362,6 +364,8 @@ class AssertPrints(object):
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
@@ -389,6 +393,8 @@ class AssertNotPrints(AssertPrints):
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
@@ -420,9 +426,8 @@ def mute_warn():
@contextmanager
def make_tempfile(name):
- """ Create an empty, named, temporary file for the duration of the context.
- """
- open(name, 'w').close()
+ """Create an empty, named, temporary file for the duration of the context."""
+ open(name, "w", encoding="utf-8").close()
try:
yield
finally:
@@ -443,8 +448,8 @@ def fake_input(inputs):
def mock_input(prompt=''):
try:
return next(it)
- except StopIteration:
- raise EOFError('No more inputs given')
+ except StopIteration as e:
+ raise EOFError('No more inputs given') from e
return patch('builtins.input', mock_input)
@@ -452,10 +457,10 @@ def help_output_test(subcommand=''):
"""test that `ipython [subcommand] -h` works"""
cmd = get_ipython_cmd() + [subcommand, '-h']
out, err, rc = get_output_error_code(cmd)
- nt.assert_equal(rc, 0, err)
- nt.assert_not_in("Traceback", err)
- nt.assert_in("Options", out)
- nt.assert_in("--help-all", out)
+ assert rc == 0, err
+ assert "Traceback" not in err
+ assert "Options" in out
+ assert "--help-all" in out
return out, err
@@ -463,9 +468,9 @@ def help_all_output_test(subcommand=''):
"""test that `ipython [subcommand] --help-all` works"""
cmd = get_ipython_cmd() + [subcommand, '--help-all']
out, err, rc = get_output_error_code(cmd)
- nt.assert_equal(rc, 0, err)
- nt.assert_not_in("Traceback", err)
- nt.assert_in("Options", out)
- nt.assert_in("Class", out)
+ assert rc == 0, err
+ assert "Traceback" not in err
+ assert "Options" in out
+ assert "Class" in out
return out, err