##// END OF EJS Templates
Remove iptest and other Nose-dependent code
Nikita Kniazev -
Show More
@@ -38,11 +38,6 b' jobs:'
38 python -m pip install --upgrade check-manifest pytest-cov anyio
38 python -m pip install --upgrade check-manifest pytest-cov anyio
39 - name: Check manifest
39 - name: Check manifest
40 run: check-manifest
40 run: check-manifest
41 - name: iptest
42 run: |
43 cd /tmp && iptest --coverage xml && cd -
44 cp /tmp/ipy_coverage.xml ./
45 cp /tmp/.coverage ./
46 - name: pytest
41 - name: pytest
47 env:
42 env:
48 COLUMNS: 120
43 COLUMNS: 120
@@ -76,20 +76,15 b' For more detailed information, see our [GitHub Workflow](https://github.com/ipyt'
76
76
77 All the tests can be run by using
77 All the tests can be run by using
78 ```shell
78 ```shell
79 iptest
79 pytest
80 ```
80 ```
81
81
82 All the tests for a single module (for example **test_alias**) can be run by using the fully qualified path to the module.
82 All the tests for a single module (for example **test_alias**) can be run by using the fully qualified path to the module.
83 ```shell
83 ```shell
84 iptest IPython.core.tests.test_alias
84 pytest IPython/core/tests/test_alias.py
85 ```
85 ```
86
86
87 Only a single test (for example **test_alias_lifecycle**) within a single file can be run by adding the specific test after a `:` at the end:
87 Only a single test (for example **test_alias_lifecycle**) within a single file can be run by adding the specific test after a `::` at the end:
88 ```shell
88 ```shell
89 iptest IPython.core.tests.test_alias:test_alias_lifecycle
89 pytest IPython/core/tests/test_alias.py::test_alias_lifecycle
90 ```
91
92 For convenience, the full path to a file can often be used instead of the module path on unix systems. For example we can run all the tests by using
93 ```shell
94 iptest IPython/core/tests/test_alias.py
95 ```
90 ```
@@ -51,7 +51,6 b' from .core.application import Application'
51 from .terminal.embed import embed
51 from .terminal.embed import embed
52
52
53 from .core.interactiveshell import InteractiveShell
53 from .core.interactiveshell import InteractiveShell
54 from .testing import test
55 from .utils.sysinfo import sys_info
54 from .utils.sysinfo import sys_info
56 from .utils.frame import extract_module_locals
55 from .utils.frame import extract_module_locals
57
56
@@ -5,6 +5,7 b''
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 import os
7 import os
8 import pytest
8 import sys
9 import sys
9 import textwrap
10 import textwrap
10 import unittest
11 import unittest
@@ -14,7 +15,6 b' from contextlib import contextmanager'
14 from traitlets.config.loader import Config
15 from traitlets.config.loader import Config
15 from IPython import get_ipython
16 from IPython import get_ipython
16 from IPython.core import completer
17 from IPython.core import completer
17 from IPython.external import decorators
18 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
18 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
19 from IPython.utils.generics import complete_object
19 from IPython.utils.generics import complete_object
20 from IPython.testing import decorators as dec
20 from IPython.testing import decorators as dec
@@ -317,8 +317,8 b' class TestCompleter(unittest.TestCase):'
317 self.assertEqual(matches, ["\u2164"]) # same as above but explicit.
317 self.assertEqual(matches, ["\u2164"]) # same as above but explicit.
318
318
319 @unittest.skip("now we have a completion for \jmath")
319 @unittest.skip("now we have a completion for \jmath")
320 @decorators.knownfailureif(
320 @pytest.mark.xfail(
321 sys.platform == "win32", "Fails if there is a C:\\j... path"
321 sys.platform == "win32", reason="Fails if there is a C:\\j... path"
322 )
322 )
323 def test_no_ascii_back_completion(self):
323 def test_no_ascii_back_completion(self):
324 ip = get_ipython()
324 ip = get_ipython()
@@ -357,8 +357,8 b' class TestCompleter(unittest.TestCase):'
357 for s in ['""', '""" """', '"hi" "ipython"']:
357 for s in ['""', '""" """', '"hi" "ipython"']:
358 self.assertFalse(completer.has_open_quotes(s))
358 self.assertFalse(completer.has_open_quotes(s))
359
359
360 @decorators.knownfailureif(
360 @pytest.mark.xfail(
361 sys.platform == "win32", "abspath completions fail on Windows"
361 sys.platform == "win32", reason="abspath completions fail on Windows"
362 )
362 )
363 def test_abspath_file_completions(self):
363 def test_abspath_file_completions(self):
364 ip = get_ipython()
364 ip = get_ipython()
@@ -1,8 +1,5 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for various magic functions.
2 """Tests for various magic functions."""
3
4 Needs to be run by nose (to make ipython session available).
5 """
6
3
7 import asyncio
4 import asyncio
8 import io
5 import io
@@ -1,7 +1,4 b''
1 """Tests for various magic functions specific to the terminal frontend.
1 """Tests for various magic functions specific to the terminal frontend."""
2
3 Needs to be run by nose (to make ipython session available).
4 """
5
2
6 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
7 # Imports
4 # Imports
@@ -125,6 +125,9 b' def doctest_run_option_parser_for_posix():'
125 """
125 """
126
126
127
127
128 doctest_run_option_parser_for_posix.__skip_doctest__ = sys.platform == "win32"
129
130
128 @dec.skip_if_not_win32
131 @dec.skip_if_not_win32
129 def doctest_run_option_parser_for_windows():
132 def doctest_run_option_parser_for_windows():
130 r"""Test option parser in %run (Windows specific).
133 r"""Test option parser in %run (Windows specific).
@@ -132,16 +135,19 b' def doctest_run_option_parser_for_windows():'
132 In Windows, you can't escape ``*` `by backslash:
135 In Windows, you can't escape ``*` `by backslash:
133
136
134 In [1]: %run print_argv.py print\\*.py
137 In [1]: %run print_argv.py print\\*.py
135 ['print\\*.py']
138 ['print\\\\*.py']
136
139
137 You can use quote to escape glob:
140 You can use quote to escape glob:
138
141
139 In [2]: %run print_argv.py 'print*.py'
142 In [2]: %run print_argv.py 'print*.py'
140 ['print*.py']
143 ["'print*.py'"]
141
144
142 """
145 """
143
146
144
147
148 doctest_run_option_parser_for_windows.__skip_doctest__ = sys.platform != "win32"
149
150
145 def doctest_reset_del():
151 def doctest_reset_del():
146 """Test that resetting doesn't cause errors in __del__ methods.
152 """Test that resetting doesn't cause errors in __del__ methods.
147
153
@@ -556,7 +562,6 b' def test_multiprocessing_run():'
556 """
562 """
557 with TemporaryDirectory() as td:
563 with TemporaryDirectory() as td:
558 mpm = sys.modules.get('__mp_main__')
564 mpm = sys.modules.get('__mp_main__')
559 assert mpm is not None
560 sys.modules['__mp_main__'] = None
565 sys.modules['__mp_main__'] = None
561 try:
566 try:
562 path = pjoin(td, 'test.py')
567 path = pjoin(td, 'test.py')
@@ -575,7 +580,7 b' def test_multiprocessing_run():'
575 finally:
580 finally:
576 sys.modules['__mp_main__'] = mpm
581 sys.modules['__mp_main__'] = mpm
577
582
578 @dec.knownfailureif(sys.platform == 'win32', "writes to io.stdout aren't captured on Windows")
583
579 def test_script_tb():
584 def test_script_tb():
580 """Test traceback offset in `ipython script.py`"""
585 """Test traceback offset in `ipython script.py`"""
581 with TemporaryDirectory() as td:
586 with TemporaryDirectory() as td:
@@ -12,38 +12,9 b''
12 import os
12 import os
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Functions
16 #-----------------------------------------------------------------------------
17
18 # User-level entry point for testing
19 def test(**kwargs):
20 """Run the entire IPython test suite.
21
22 Any of the options for run_iptestall() may be passed as keyword arguments.
23
24 For example::
25
26 IPython.test(testgroups=['lib', 'config', 'utils'], fast=2)
27
28 will run those three sections of the test suite, using two processes.
29 """
30
31 # Do the import internally, so that this function doesn't increase total
32 # import time
33 from .iptestcontroller import run_iptestall, default_options
34 options = default_options()
35 for name, val in kwargs.items():
36 setattr(options, name, val)
37 run_iptestall(options)
38
39 #-----------------------------------------------------------------------------
40 # Constants
15 # Constants
41 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
42
17
43 # We scale all timeouts via this factor, slow machines can increase it
18 # We scale all timeouts via this factor, slow machines can increase it
44 IPYTHON_TESTING_TIMEOUT_SCALE = float(os.getenv(
19 IPYTHON_TESTING_TIMEOUT_SCALE = float(os.getenv(
45 'IPYTHON_TESTING_TIMEOUT_SCALE', 1))
20 'IPYTHON_TESTING_TIMEOUT_SCALE', 1))
46
47 # So nose doesn't try to run this as a test itself and we end up with an
48 # infinite test loop
49 test.__test__ = False
@@ -44,11 +44,6 b' from decorator import decorator'
44 # Expose the unittest-driven decorators
44 # Expose the unittest-driven decorators
45 from .ipunittest import ipdoctest, ipdocstring
45 from .ipunittest import ipdoctest, ipdocstring
46
46
47 # Grab the numpy-specific decorators which we keep in a file that we
48 # occasionally update from upstream: decorators.py is a copy of
49 # numpy.testing.decorators, we expose all of it here.
50 from IPython.external.decorators import knownfailureif
51
52 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
53 # Classes and functions
48 # Classes and functions
54 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
@@ -165,11 +160,8 b' def skip_iptest_but_not_pytest(f):'
165 return f
160 return f
166
161
167
162
168 # Inspired by numpy's skipif, but uses the full apply_wrapper utility to
169 # preserve function metadata better and allows the skip condition to be a
170 # callable.
171 def skipif(skip_condition, msg=None):
163 def skipif(skip_condition, msg=None):
172 ''' Make function raise SkipTest exception if skip_condition is true
164 """Make function raise SkipTest exception if skip_condition is true
173
165
174 Parameters
166 Parameters
175 ----------
167 ----------
@@ -188,57 +180,15 b' def skipif(skip_condition, msg=None):'
188 Decorator, which, when applied to a function, causes SkipTest
180 Decorator, which, when applied to a function, causes SkipTest
189 to be raised when the skip_condition was True, and the function
181 to be raised when the skip_condition was True, and the function
190 to be called normally otherwise.
182 to be called normally otherwise.
183 """
184 if msg is None:
185 msg = "Test skipped due to test condition."
191
186
192 Notes
187 import pytest
193 -----
194 You will see from the code that we had to further decorate the
195 decorator with the nose.tools.make_decorator function in order to
196 transmit function name, and various other metadata.
197 '''
198
199 def skip_decorator(f):
200 # Local import to avoid a hard nose dependency and only incur the
201 # import time overhead at actual test-time.
202 import nose
203
204 # Allow for both boolean or callable skip conditions.
205 if callable(skip_condition):
206 skip_val = skip_condition
207 else:
208 skip_val = lambda : skip_condition
209
210 def get_msg(func,msg=None):
211 """Skip message with information about function being skipped."""
212 if msg is None: out = 'Test skipped due to test condition.'
213 else: out = msg
214 return "Skipping test: %s. %s" % (func.__name__,out)
215
216 # We need to define *two* skippers because Python doesn't allow both
217 # return with value and yield inside the same function.
218 def skipper_func(*args, **kwargs):
219 """Skipper for normal test functions."""
220 if skip_val():
221 raise nose.SkipTest(get_msg(f,msg))
222 else:
223 return f(*args, **kwargs)
224
225 def skipper_gen(*args, **kwargs):
226 """Skipper for test generators."""
227 if skip_val():
228 raise nose.SkipTest(get_msg(f,msg))
229 else:
230 for x in f(*args, **kwargs):
231 yield x
232
233 # Choose the right skipper to use when building the actual generator.
234 if nose.util.isgenerator(f):
235 skipper = skipper_gen
236 else:
237 skipper = skipper_func
238
188
239 return nose.tools.make_decorator(f)(skipper)
189 assert isinstance(skip_condition, bool)
190 return pytest.mark.skipif(skip_condition, reason=msg)
240
191
241 return skip_decorator
242
192
243 # A version with the condition set to true, common case just to attach a message
193 # A version with the condition set to true, common case just to attach a message
244 # to a skip decorator
194 # to a skip decorator
@@ -265,12 +215,7 b' def skip(msg=None):'
265 def onlyif(condition, msg):
215 def onlyif(condition, msg):
266 """The reverse from skipif, see skipif for details."""
216 """The reverse from skipif, see skipif for details."""
267
217
268 if callable(condition):
218 return skipif(not condition, msg)
269 skip_condition = lambda : not condition()
270 else:
271 skip_condition = lambda : not condition
272
273 return skipif(skip_condition, msg)
274
219
275 #-----------------------------------------------------------------------------
220 #-----------------------------------------------------------------------------
276 # Utility functions for decorators
221 # Utility functions for decorators
@@ -351,8 +296,6 b" skipif_not_matplotlib = skip_without('matplotlib')"
351
296
352 skipif_not_sympy = skip_without('sympy')
297 skipif_not_sympy = skip_without('sympy')
353
298
354 skip_known_failure = knownfailureif(True,'This test is known to fail')
355
356 # A null 'decorator', useful to make more readable code that needs to pick
299 # A null 'decorator', useful to make more readable code that needs to pick
357 # between different decorators based on OS or other conditions
300 # between different decorators based on OS or other conditions
358 null_deco = lambda f: f
301 null_deco = lambda f: f
@@ -19,35 +19,14 b' Limitations:'
19 # Module imports
19 # Module imports
20
20
21 # From the standard library
21 # From the standard library
22 import builtins as builtin_mod
23 import doctest
22 import doctest
24 import inspect
23 import inspect
25 import logging
24 import logging
26 import os
25 import os
27 import re
26 import re
28 import sys
29 from importlib import import_module
30 from io import StringIO
31
27
32 from testpath import modified_env
28 from testpath import modified_env
33
29
34 from inspect import getmodule
35
36 from pathlib import Path, PurePath
37
38 # We are overriding the default doctest runner, so we need to import a few
39 # things from doctest directly
40 from doctest import (REPORTING_FLAGS, REPORT_ONLY_FIRST_FAILURE,
41 _unittest_reportflags, DocTestRunner,
42 _extract_future_flags, pdb, _OutputRedirectingPdb,
43 _exception_traceback,
44 linecache)
45
46 # Third-party modules
47
48 from nose.plugins import doctests, Plugin
49 from nose.util import anyp, tolist
50
51 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
52 # Module globals and other constants
31 # Module globals and other constants
53 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
@@ -195,129 +174,6 b' class IPDoctestOutputChecker(doctest.OutputChecker):'
195 return ret
174 return ret
196
175
197
176
198 class DocTestCase(doctests.DocTestCase):
199 """Proxy for DocTestCase: provides an address() method that
200 returns the correct address for the doctest case. Otherwise
201 acts as a proxy to the test case. To provide hints for address(),
202 an obj may also be passed -- this will be used as the test object
203 for purposes of determining the test address, if it is provided.
204 """
205
206 # Note: this method was taken from numpy's nosetester module.
207
208 # Subclass nose.plugins.doctests.DocTestCase to work around a bug in
209 # its constructor that blocks non-default arguments from being passed
210 # down into doctest.DocTestCase
211
212 def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
213 checker=None, obj=None, result_var='_'):
214 self._result_var = result_var
215 doctests.DocTestCase.__init__(self, test,
216 optionflags=optionflags,
217 setUp=setUp, tearDown=tearDown,
218 checker=checker)
219 # Now we must actually copy the original constructor from the stdlib
220 # doctest class, because we can't call it directly and a bug in nose
221 # means it never gets passed the right arguments.
222
223 self._dt_optionflags = optionflags
224 self._dt_checker = checker
225 self._dt_test = test
226 self._dt_test_globs_ori = test.globs
227 self._dt_setUp = setUp
228 self._dt_tearDown = tearDown
229
230 # XXX - store this runner once in the object!
231 runner = IPDocTestRunner(optionflags=optionflags,
232 checker=checker, verbose=False)
233 self._dt_runner = runner
234
235
236 # Each doctest should remember the directory it was loaded from, so
237 # things like %run work without too many contortions
238 self._ori_dir = os.path.dirname(test.filename)
239
240 # Modified runTest from the default stdlib
241 def runTest(self):
242 test = self._dt_test
243 runner = self._dt_runner
244
245 old = sys.stdout
246 new = StringIO()
247 optionflags = self._dt_optionflags
248
249 if not (optionflags & REPORTING_FLAGS):
250 # The option flags don't include any reporting flags,
251 # so add the default reporting flags
252 optionflags |= _unittest_reportflags
253
254 try:
255 # Save our current directory and switch out to the one where the
256 # test was originally created, in case another doctest did a
257 # directory change. We'll restore this in the finally clause.
258 curdir = os.getcwd()
259 #print 'runTest in dir:', self._ori_dir # dbg
260 os.chdir(self._ori_dir)
261
262 runner.DIVIDER = "-"*70
263 failures, tries = runner.run(test,out=new.write,
264 clear_globs=False)
265 finally:
266 sys.stdout = old
267 os.chdir(curdir)
268
269 if failures:
270 raise self.failureException(self.format_failure(new.getvalue()))
271
272 def setUp(self):
273 """Modified test setup that syncs with ipython namespace"""
274 #print "setUp test", self._dt_test.examples # dbg
275 if isinstance(self._dt_test.examples[0], IPExample):
276 # for IPython examples *only*, we swap the globals with the ipython
277 # namespace, after updating it with the globals (which doctest
278 # fills with the necessary info from the module being tested).
279 self.user_ns_orig = {}
280 self.user_ns_orig.update(_ip.user_ns)
281 _ip.user_ns.update(self._dt_test.globs)
282 # We must remove the _ key in the namespace, so that Python's
283 # doctest code sets it naturally
284 _ip.user_ns.pop('_', None)
285 _ip.user_ns['__builtins__'] = builtin_mod
286 self._dt_test.globs = _ip.user_ns
287
288 super(DocTestCase, self).setUp()
289
290 def tearDown(self):
291
292 # Undo the test.globs reassignment we made, so that the parent class
293 # teardown doesn't destroy the ipython namespace
294 if isinstance(self._dt_test.examples[0], IPExample):
295 self._dt_test.globs = self._dt_test_globs_ori
296 _ip.user_ns.clear()
297 _ip.user_ns.update(self.user_ns_orig)
298
299 # XXX - fperez: I am not sure if this is truly a bug in nose 0.11, but
300 # it does look like one to me: its tearDown method tries to run
301 #
302 # delattr(builtin_mod, self._result_var)
303 #
304 # without checking that the attribute really is there; it implicitly
305 # assumes it should have been set via displayhook. But if the
306 # displayhook was never called, this doesn't necessarily happen. I
307 # haven't been able to find a little self-contained example outside of
308 # ipython that would show the problem so I can report it to the nose
309 # team, but it does happen a lot in our code.
310 #
311 # So here, we just protect as narrowly as possible by trapping an
312 # attribute error whose message would be the name of self._result_var,
313 # and letting any other error propagate.
314 try:
315 super(DocTestCase, self).tearDown()
316 except AttributeError as exc:
317 if exc.args[0] != self._result_var:
318 raise
319
320
321 # A simple subclassing of the original with a different class name, so we can
177 # A simple subclassing of the original with a different class name, so we can
322 # distinguish and treat differently IPython examples from pure python ones.
178 # distinguish and treat differently IPython examples from pure python ones.
323 class IPExample(doctest.Example): pass
179 class IPExample(doctest.Example): pass
@@ -594,169 +450,3 b' class DocFileCase(doctest.DocFileCase):'
594 """
450 """
595 def address(self):
451 def address(self):
596 return (self._dt_test.filename, None, None)
452 return (self._dt_test.filename, None, None)
597
598
599 class ExtensionDoctest(doctests.Doctest):
600 """Nose Plugin that supports doctests in extension modules.
601 """
602 name = 'extdoctest' # call nosetests with --with-extdoctest
603 enabled = True
604
605 def options(self, parser, env=os.environ):
606 Plugin.options(self, parser, env)
607 parser.add_option('--doctest-tests', action='store_true',
608 dest='doctest_tests',
609 default=env.get('NOSE_DOCTEST_TESTS',True),
610 help="Also look for doctests in test modules. "
611 "Note that classes, methods and functions should "
612 "have either doctests or non-doctest tests, "
613 "not both. [NOSE_DOCTEST_TESTS]")
614 parser.add_option('--doctest-extension', action="append",
615 dest="doctestExtension",
616 help="Also look for doctests in files with "
617 "this extension [NOSE_DOCTEST_EXTENSION]")
618 # Set the default as a list, if given in env; otherwise
619 # an additional value set on the command line will cause
620 # an error.
621 env_setting = env.get('NOSE_DOCTEST_EXTENSION')
622 if env_setting is not None:
623 parser.set_defaults(doctestExtension=tolist(env_setting))
624
625
626 def configure(self, options, config):
627 Plugin.configure(self, options, config)
628 # Pull standard doctest plugin out of config; we will do doctesting
629 config.plugins.plugins = [p for p in config.plugins.plugins
630 if p.name != 'doctest']
631 self.doctest_tests = options.doctest_tests
632 self.extension = tolist(options.doctestExtension)
633
634 self.parser = doctest.DocTestParser()
635 self.finder = DocTestFinder()
636 self.checker = IPDoctestOutputChecker()
637 self.globs = None
638 self.extraglobs = None
639
640
641 def loadTestsFromExtensionModule(self,filename):
642 bpath,mod = os.path.split(filename)
643 modname = os.path.splitext(mod)[0]
644 try:
645 sys.path.append(bpath)
646 module = import_module(modname)
647 tests = list(self.loadTestsFromModule(module))
648 finally:
649 sys.path.pop()
650 return tests
651
652 # NOTE: the method below is almost a copy of the original one in nose, with
653 # a few modifications to control output checking.
654
655 def loadTestsFromModule(self, module):
656 #print '*** ipdoctest - lTM',module # dbg
657
658 if not self.matches(module.__name__):
659 log.debug("Doctest doesn't want module %s", module)
660 return
661
662 tests = self.finder.find(module,globs=self.globs,
663 extraglobs=self.extraglobs)
664 if not tests:
665 return
666
667 # always use whitespace and ellipsis options
668 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
669
670 tests.sort()
671 module_file = module.__file__
672 if module_file[-4:] in ('.pyc', '.pyo'):
673 module_file = module_file[:-1]
674 for test in tests:
675 if not test.examples:
676 continue
677 if not test.filename:
678 test.filename = module_file
679
680 yield DocTestCase(test,
681 optionflags=optionflags,
682 checker=self.checker)
683
684
685 def loadTestsFromFile(self, filename):
686 #print "ipdoctest - from file", filename # dbg
687 if is_extension_module(filename):
688 for t in self.loadTestsFromExtensionModule(filename):
689 yield t
690 else:
691 if self.extension and anyp(filename.endswith, self.extension):
692 name = PurePath(filename).name
693 doc = Path(filename).read_text()
694 test = self.parser.get_doctest(
695 doc, globs={'__file__': filename}, name=name,
696 filename=filename, lineno=0)
697 if test.examples:
698 #print 'FileCase:',test.examples # dbg
699 yield DocFileCase(test)
700 else:
701 yield False # no tests to load
702
703
704 class IPythonDoctest(ExtensionDoctest):
705 """Nose Plugin that supports doctests in extension modules.
706 """
707 name = 'ipdoctest' # call nosetests with --with-ipdoctest
708 enabled = True
709
710 def makeTest(self, obj, parent):
711 """Look for doctests in the given object, which will be a
712 function, method or class.
713 """
714 #print 'Plugin analyzing:', obj, parent # dbg
715 # always use whitespace and ellipsis options
716 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
717
718 doctests = self.finder.find(obj, module=getmodule(parent))
719 if doctests:
720 for test in doctests:
721 if len(test.examples) == 0:
722 continue
723
724 yield DocTestCase(test, obj=obj,
725 optionflags=optionflags,
726 checker=self.checker)
727
728 def options(self, parser, env=os.environ):
729 #print "Options for nose plugin:", self.name # dbg
730 Plugin.options(self, parser, env)
731 parser.add_option('--ipdoctest-tests', action='store_true',
732 dest='ipdoctest_tests',
733 default=env.get('NOSE_IPDOCTEST_TESTS',True),
734 help="Also look for doctests in test modules. "
735 "Note that classes, methods and functions should "
736 "have either doctests or non-doctest tests, "
737 "not both. [NOSE_IPDOCTEST_TESTS]")
738 parser.add_option('--ipdoctest-extension', action="append",
739 dest="ipdoctest_extension",
740 help="Also look for doctests in files with "
741 "this extension [NOSE_IPDOCTEST_EXTENSION]")
742 # Set the default as a list, if given in env; otherwise
743 # an additional value set on the command line will cause
744 # an error.
745 env_setting = env.get('NOSE_IPDOCTEST_EXTENSION')
746 if env_setting is not None:
747 parser.set_defaults(ipdoctest_extension=tolist(env_setting))
748
749 def configure(self, options, config):
750 #print "Configuring nose plugin:", self.name # dbg
751 Plugin.configure(self, options, config)
752 # Pull standard doctest plugin out of config; we will do doctesting
753 config.plugins.plugins = [p for p in config.plugins.plugins
754 if p.name != 'doctest']
755 self.doctest_tests = options.ipdoctest_tests
756 self.extension = tolist(options.ipdoctest_extension)
757
758 self.parser = IPDocTestParser()
759 self.finder = DocTestFinder(parser=self.parser)
760 self.checker = IPDoctestOutputChecker()
761 self.globs = None
762 self.extraglobs = None
@@ -14,7 +14,6 b' from unittest.mock import patch'
14 from os.path import join, abspath
14 from os.path import join, abspath
15 from imp import reload
15 from imp import reload
16
16
17 from nose import SkipTest, with_setup
18 import pytest
17 import pytest
19
18
20 import IPython
19 import IPython
@@ -98,8 +97,17 b' def teardown_environment():'
98 if hasattr(sys, 'frozen'):
97 if hasattr(sys, 'frozen'):
99 del sys.frozen
98 del sys.frozen
100
99
100
101 # Build decorator that uses the setup_environment/setup_environment
101 # Build decorator that uses the setup_environment/setup_environment
102 with_environment = with_setup(setup_environment, teardown_environment)
102 @pytest.fixture
103 def environment():
104 setup_environment()
105 yield
106 teardown_environment()
107
108
109 with_environment = pytest.mark.usefixtures("environment")
110
103
111
104 @skip_if_not_win32
112 @skip_if_not_win32
105 @with_environment
113 @with_environment
@@ -291,7 +299,8 b' class TestRaiseDeprecation(unittest.TestCase):'
291 else:
299 else:
292 # I can still write to an unwritable dir,
300 # I can still write to an unwritable dir,
293 # assume I'm root and skip the test
301 # assume I'm root and skip the test
294 raise SkipTest("I can't create directories that I can't write to")
302 pytest.skip("I can't create directories that I can't write to")
303
295 with self.assertWarnsRegex(UserWarning, 'is not a writable location'):
304 with self.assertWarnsRegex(UserWarning, 'is not a writable location'):
296 ipdir = paths.get_ipython_dir()
305 ipdir = paths.get_ipython_dir()
297 env.pop('IPYTHON_DIR', None)
306 env.pop('IPYTHON_DIR', None)
@@ -26,13 +26,9 b' init:'
26 install:
26 install:
27 - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
27 - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
28 - python -m pip install --upgrade setuptools pip
28 - python -m pip install --upgrade setuptools pip
29 - pip install nose coverage pytest pytest-cov pytest-trio matplotlib pandas
29 - pip install pytest pytest-cov pytest-trio matplotlib pandas
30 - pip install -e .[test]
30 - pip install -e .[test]
31 - mkdir results
32 - cd results
33 test_script:
31 test_script:
34 - iptest --coverage xml
35 - cd ..
36 - pytest --color=yes -ra --cov --cov-report=xml
32 - pytest --color=yes -ra --cov --cov-report=xml
37 on_finish:
33 on_finish:
38 - curl -Os https://uploader.codecov.io/latest/windows/codecov.exe
34 - curl -Os https://uploader.codecov.io/latest/windows/codecov.exe
@@ -5,7 +5,6 b' dependencies:'
5 - sphinx>=1.8
5 - sphinx>=1.8
6 - sphinx_rtd_theme
6 - sphinx_rtd_theme
7 - numpy
7 - numpy
8 - nose
9 - testpath
8 - testpath
10 - matplotlib
9 - matplotlib
11 - pip:
10 - pip:
@@ -100,12 +100,11 b' permissions, you may need to run the last command with :command:`sudo`. You can'
100 also install in user specific location by using the ``--user`` flag in
100 also install in user specific location by using the ``--user`` flag in
101 conjunction with pip.
101 conjunction with pip.
102
102
103 To run IPython's test suite, use the :command:`iptest` command from outside of
103 To run IPython's test suite, use the :command:`pytest` command:
104 the IPython source tree:
105
104
106 .. code-block:: bash
105 .. code-block:: bash
107
106
108 $ iptest
107 $ pytest
109
108
110 .. _devinstall:
109 .. _devinstall:
111
110
@@ -175,7 +175,6 b' extras_require = dict('
175 qtconsole=["qtconsole"],
175 qtconsole=["qtconsole"],
176 doc=["Sphinx>=1.3"],
176 doc=["Sphinx>=1.3"],
177 test=[
177 test=[
178 "nose>=0.10.1",
179 "pytest",
178 "pytest",
180 "requests",
179 "requests",
181 "testpath",
180 "testpath",
@@ -234,7 +234,6 b' def find_entry_points():'
234 """
234 """
235 ep = [
235 ep = [
236 'ipython%s = IPython:start_ipython',
236 'ipython%s = IPython:start_ipython',
237 'iptest%s = IPython.testing.iptestcontroller:main',
238 ]
237 ]
239 suffix = str(sys.version_info[0])
238 suffix = str(sys.version_info[0])
240 return [e % '' for e in ep] + [e % suffix for e in ep]
239 return [e % '' for e in ep] + [e % suffix for e in ep]
@@ -7,7 +7,7 b" python -c 'import keyring'"
7 python -c 'import twine'
7 python -c 'import twine'
8 python -c 'import sphinx'
8 python -c 'import sphinx'
9 python -c 'import sphinx_rtd_theme'
9 python -c 'import sphinx_rtd_theme'
10 python -c 'import nose'
10 python -c 'import pytest'
11
11
12
12
13 BLACK=$(tput setaf 1)
13 BLACK=$(tput setaf 1)
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now