From ea4ab3aa772cb62f5641f12c8188e49fd8055e6c 2019-06-24 20:43:46 From: Matthias Bussonnier Date: 2019-06-24 20:43:46 Subject: [PATCH] Also run test with Pytest. This now run many of the tests using pytest. Many of previous pull-requests brought pytest compatibility; this now actually would run a passing test suite. This does not mean we can drop nose; in particular we still have plugins fro doctests using iptest; which pytest does not run; and we are still limited to pytest 3 (pytest 4 does not support yield-test anymore) There is thus still much work to do. --- diff --git a/.travis.yml b/.travis.yml index 75cf14f..3369a96 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,6 +39,7 @@ install: - pip install setuptools --upgrade - pip install -e file://$PWD#egg=ipython[test] --upgrade - pip install trio curio + - pip install 'pytest<4' matplotlib - pip install codecov check-manifest --upgrade script: @@ -49,6 +50,7 @@ script: cp /home/travis/virtualenv/python3.8-dev/lib/python3.8/site-packages/parso/python/grammar37.txt /home/travis/virtualenv/python3.8-dev/lib/python3.8/site-packages/parso/python/grammar38.txt fi - cd /tmp && iptest --coverage xml && cd - + - pytest IPython # On the latest Python (on Linux) only, make sure that the docs build. - | if [[ "$TRAVIS_PYTHON_VERSION" == "3.7" ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then diff --git a/IPython/conftest.py b/IPython/conftest.py new file mode 100644 index 0000000..b9d1f06 --- /dev/null +++ b/IPython/conftest.py @@ -0,0 +1,69 @@ +import types +import sys +import builtins +import os +import pytest +import pathlib +import shutil + +from IPython.testing import tools + + +def get_ipython(): + from IPython.terminal.interactiveshell import TerminalInteractiveShell + if TerminalInteractiveShell._instance: + return TerminalInteractiveShell.instance() + + config = tools.default_config() + config.TerminalInteractiveShell.simple_prompt = True + + # Create and initialize our test-friendly IPython instance. + shell = TerminalInteractiveShell.instance(config=config) + return shell + + +@pytest.fixture(scope='session', autouse=True) +def work_path(): + path = pathlib.Path("./tmp-ipython-pytest-profiledir") + os.environ["IPYTHONDIR"] = str(path.absolute()) + if path.exists(): + raise ValueError('IPython dir temporary path already exists ! Did previous test run exit successfully ?') + path.mkdir() + yield + shutil.rmtree(str(path.resolve())) + + +def nopage(strng, start=0, screen_lines=0, pager_cmd=None): + if isinstance(strng, dict): + strng = strng.get("text/plain", "") + print(strng) + + +def xsys(self, cmd): + """Replace the default system call with a capturing one for doctest. + """ + # We use getoutput, but we need to strip it because pexpect captures + # the trailing newline differently from commands.getoutput + print(self.getoutput(cmd, split=False, depth=1).rstrip(), end="", file=sys.stdout) + sys.stdout.flush() + + +# for things to work correctly we would need this as a session fixture; +# unfortunately this will fail on some test that get executed as _collection_ +# time (before the fixture run), in particular parametrized test that contain +# yields. so for now execute at import time. +#@pytest.fixture(autouse=True, scope='session') +def inject(): + + builtins.get_ipython = get_ipython + builtins._ip = get_ipython() + builtins.ip = get_ipython() + builtins.ip.system = types.MethodType(xsys, ip) + builtins.ip.builtin_trap.activate() + from IPython.core import page + + page.pager_page = nopage + # yield + + +inject() diff --git a/IPython/core/tests/test_alias.py b/IPython/core/tests/test_alias.py index 7417e95..d990796 100644 --- a/IPython/core/tests/test_alias.py +++ b/IPython/core/tests/test_alias.py @@ -39,7 +39,7 @@ def test_alias_args_error(): _ip.run_cell('parts 1') nt.assert_equal(cap.stderr.split(':')[0], 'UsageError') - + def test_alias_args_commented(): """Check that alias correctly ignores 'commented out' args""" _ip.magic('alias commetarg echo this is %%s a commented out arg') @@ -47,7 +47,10 @@ def test_alias_args_commented(): with capture_output() as cap: _ip.run_cell('commetarg') - nt.assert_equal(cap.stdout, 'this is %s a commented out arg') + # strip() is for pytest compat; testing via iptest patch IPython shell + # in testin.globalipapp and replace the system call which messed up the + # \r\n + assert cap.stdout.strip() == 'this is %s a commented out arg' def test_alias_args_commented_nargs(): """Check that alias correctly counts args, excluding those commented out""" @@ -59,4 +62,4 @@ def test_alias_args_commented_nargs(): assert am.is_alias(alias_name) thealias = am.get_alias(alias_name) - nt.assert_equal(thealias.nargs, 1) \ No newline at end of file + nt.assert_equal(thealias.nargs, 1) diff --git a/IPython/core/tests/test_magic.py b/IPython/core/tests/test_magic.py index 716e7cc..e137c93 100644 --- a/IPython/core/tests/test_magic.py +++ b/IPython/core/tests/test_magic.py @@ -33,9 +33,6 @@ from IPython.utils.tempdir import (TemporaryDirectory, from IPython.utils.process import find_cmd - -_ip = get_ipython() - @magic.magics_class class DummyMagics(magic.Magics): pass @@ -150,6 +147,7 @@ def test_rehashx(): # rehashx must fill up syscmdlist scoms = _ip.db['syscmdlist'] nt.assert_true(len(scoms) > 10) + def test_magic_parse_options(): diff --git a/IPython/core/tests/test_magic_terminal.py b/IPython/core/tests/test_magic_terminal.py index 0981c7b..79e2d3e 100644 --- a/IPython/core/tests/test_magic_terminal.py +++ b/IPython/core/tests/test_magic_terminal.py @@ -16,10 +16,6 @@ import nose.tools as nt from IPython.testing import tools as tt #----------------------------------------------------------------------------- -# Globals -#----------------------------------------------------------------------------- - -#----------------------------------------------------------------------------- # Test functions begin #----------------------------------------------------------------------------- diff --git a/IPython/core/tests/test_oinspect.py b/IPython/core/tests/test_oinspect.py index cd38e09..8bdcfad 100644 --- a/IPython/core/tests/test_oinspect.py +++ b/IPython/core/tests/test_oinspect.py @@ -23,8 +23,12 @@ from IPython.utils.path import compress_user # Globals and constants #----------------------------------------------------------------------------- -inspector = oinspect.Inspector() -ip = get_ipython() +inspector = None + +def setup_module(): + global inspector + inspector = oinspect.Inspector() + #----------------------------------------------------------------------------- # Local utilities @@ -34,7 +38,7 @@ ip = get_ipython() # defined, if any code is inserted above, the following line will need to be # updated. Do NOT insert any whitespace between the next line and the function # definition below. -THIS_LINE_NUMBER = 37 # Put here the actual number of this line +THIS_LINE_NUMBER = 41 # Put here the actual number of this line from unittest import TestCase @@ -123,7 +127,6 @@ class SimpleClass(object): """Some method's docstring""" - class Awkward(object): def __getattr__(self, name): raise Exception(name) @@ -221,7 +224,6 @@ def support_function_one(x, y=2, *a, **kw): def test_calldef_none(): # We should ignore __call__ for all of these. for obj in [support_function_one, SimpleClass().method, any, str.upper]: - print(obj) i = inspector.info(obj) nt.assert_is(i['call_def'], None) diff --git a/IPython/external/decorators/__init__.py b/IPython/external/decorators/__init__.py index 074f614..1db80ed 100644 --- a/IPython/external/decorators/__init__.py +++ b/IPython/external/decorators/__init__.py @@ -1,5 +1,5 @@ try: - from numpy.testing.noseclasses import KnownFailure, knownfailureif + from numpy.testing import KnownFailure, knownfailureif except ImportError: from ._decorators import knownfailureif try: diff --git a/IPython/testing/iptestcontroller.py b/IPython/testing/iptestcontroller.py index 8a8c803..b522f60 100644 --- a/IPython/testing/iptestcontroller.py +++ b/IPython/testing/iptestcontroller.py @@ -49,7 +49,7 @@ class TestController: self.env = {} self.dirs = [] - def setup(self): + def setUp(self): """Create temporary directories etc. This is only called when we know the test group will be run. Things @@ -440,7 +440,6 @@ argparser.add_argument('testgroups', nargs='*', 'all tests.') argparser.add_argument('--all', action='store_true', help='Include slow tests not run by default.') -argparser.add_argument('--url', help="URL to use for the JS tests.") 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.') diff --git a/IPython/testing/ipunittest.py b/IPython/testing/ipunittest.py index 139c305..56146d1 100644 --- a/IPython/testing/ipunittest.py +++ b/IPython/testing/ipunittest.py @@ -38,6 +38,7 @@ Authors import re import unittest from doctest import DocTestFinder, DocTestRunner, TestResults +from IPython.terminal.interactiveshell import InteractiveShell #----------------------------------------------------------------------------- # Classes and functions @@ -78,7 +79,7 @@ class IPython2PythonConverter(object): dnew = self.rps1.sub(pyps1, dnew) dnew = self.rps2.sub(pyps2, dnew) dnew = self.rout.sub(pyout, dnew) - ip = globalipapp.get_ipython() + ip = InteractiveShell.instance() # Convert input IPython source into valid Python. out = [] diff --git a/MANIFEST.in b/MANIFEST.in index 3a19fbf..dee45a2 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -4,6 +4,7 @@ include LICENSE include setupbase.py include setupegg.py include MANIFEST.in +include pytest.ini include .mailmap recursive-exclude tools * diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..537ebeb --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +addopts = --duration=10