Show More
@@ -38,11 +38,6 b' jobs:' | |||
|
38 | 38 | python -m pip install --upgrade check-manifest pytest-cov anyio |
|
39 | 39 | - name: Check manifest |
|
40 | 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 | 41 | - name: pytest |
|
47 | 42 | env: |
|
48 | 43 | COLUMNS: 120 |
@@ -76,20 +76,15 b' For more detailed information, see our [GitHub Workflow](https://github.com/ipyt' | |||
|
76 | 76 | |
|
77 | 77 | All the tests can be run by using |
|
78 | 78 | ```shell |
|
79 |
|
|
|
79 | pytest | |
|
80 | 80 | ``` |
|
81 | 81 | |
|
82 | 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 | 83 | ```shell |
|
84 |
|
|
|
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 | 88 | ```shell |
|
89 |
|
|
|
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 | |
|
89 | pytest IPython/core/tests/test_alias.py::test_alias_lifecycle | |
|
95 | 90 | ``` |
@@ -51,7 +51,6 b' from .core.application import Application' | |||
|
51 | 51 | from .terminal.embed import embed |
|
52 | 52 | |
|
53 | 53 | from .core.interactiveshell import InteractiveShell |
|
54 | from .testing import test | |
|
55 | 54 | from .utils.sysinfo import sys_info |
|
56 | 55 | from .utils.frame import extract_module_locals |
|
57 | 56 |
@@ -5,6 +5,7 b'' | |||
|
5 | 5 | # Distributed under the terms of the Modified BSD License. |
|
6 | 6 | |
|
7 | 7 | import os |
|
8 | import pytest | |
|
8 | 9 | import sys |
|
9 | 10 | import textwrap |
|
10 | 11 | import unittest |
@@ -14,7 +15,6 b' from contextlib import contextmanager' | |||
|
14 | 15 | from traitlets.config.loader import Config |
|
15 | 16 | from IPython import get_ipython |
|
16 | 17 | from IPython.core import completer |
|
17 | from IPython.external import decorators | |
|
18 | 18 | from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory |
|
19 | 19 | from IPython.utils.generics import complete_object |
|
20 | 20 | from IPython.testing import decorators as dec |
@@ -317,8 +317,8 b' class TestCompleter(unittest.TestCase):' | |||
|
317 | 317 | self.assertEqual(matches, ["\u2164"]) # same as above but explicit. |
|
318 | 318 | |
|
319 | 319 | @unittest.skip("now we have a completion for \jmath") |
|
320 | @decorators.knownfailureif( | |
|
321 | sys.platform == "win32", "Fails if there is a C:\\j... path" | |
|
320 | @pytest.mark.xfail( | |
|
321 | sys.platform == "win32", reason="Fails if there is a C:\\j... path" | |
|
322 | 322 | ) |
|
323 | 323 | def test_no_ascii_back_completion(self): |
|
324 | 324 | ip = get_ipython() |
@@ -357,8 +357,8 b' class TestCompleter(unittest.TestCase):' | |||
|
357 | 357 | for s in ['""', '""" """', '"hi" "ipython"']: |
|
358 | 358 | self.assertFalse(completer.has_open_quotes(s)) |
|
359 | 359 | |
|
360 | @decorators.knownfailureif( | |
|
361 | sys.platform == "win32", "abspath completions fail on Windows" | |
|
360 | @pytest.mark.xfail( | |
|
361 | sys.platform == "win32", reason="abspath completions fail on Windows" | |
|
362 | 362 | ) |
|
363 | 363 | def test_abspath_file_completions(self): |
|
364 | 364 | ip = get_ipython() |
@@ -1,8 +1,5 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | """Tests for various magic functions. | |
|
3 | ||
|
4 | Needs to be run by nose (to make ipython session available). | |
|
5 | """ | |
|
2 | """Tests for various magic functions.""" | |
|
6 | 3 | |
|
7 | 4 | import asyncio |
|
8 | 5 | import io |
@@ -1,7 +1,4 b'' | |||
|
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 | """ | |
|
1 | """Tests for various magic functions specific to the terminal frontend.""" | |
|
5 | 2 | |
|
6 | 3 | #----------------------------------------------------------------------------- |
|
7 | 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 | 131 | @dec.skip_if_not_win32 |
|
129 | 132 | def doctest_run_option_parser_for_windows(): |
|
130 | 133 | r"""Test option parser in %run (Windows specific). |
@@ -132,16 +135,19 b' def doctest_run_option_parser_for_windows():' | |||
|
132 | 135 | In Windows, you can't escape ``*` `by backslash: |
|
133 | 136 | |
|
134 | 137 | In [1]: %run print_argv.py print\\*.py |
|
135 | ['print\\*.py'] | |
|
138 | ['print\\\\*.py'] | |
|
136 | 139 | |
|
137 | 140 | You can use quote to escape glob: |
|
138 | 141 | |
|
139 | 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 | 151 | def doctest_reset_del(): |
|
146 | 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 | 563 | with TemporaryDirectory() as td: |
|
558 | 564 | mpm = sys.modules.get('__mp_main__') |
|
559 | assert mpm is not None | |
|
560 | 565 | sys.modules['__mp_main__'] = None |
|
561 | 566 | try: |
|
562 | 567 | path = pjoin(td, 'test.py') |
@@ -575,7 +580,7 b' def test_multiprocessing_run():' | |||
|
575 | 580 | finally: |
|
576 | 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 | 584 | def test_script_tb(): |
|
580 | 585 | """Test traceback offset in `ipython script.py`""" |
|
581 | 586 | with TemporaryDirectory() as td: |
@@ -12,38 +12,9 b'' | |||
|
12 | 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 | 15 | # Constants |
|
41 | 16 | #----------------------------------------------------------------------------- |
|
42 | 17 | |
|
43 | 18 | # We scale all timeouts via this factor, slow machines can increase it |
|
44 | 19 | IPYTHON_TESTING_TIMEOUT_SCALE = float(os.getenv( |
|
45 | 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 | 44 | # Expose the unittest-driven decorators |
|
45 | 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 | 48 | # Classes and functions |
|
54 | 49 | #----------------------------------------------------------------------------- |
@@ -165,11 +160,8 b' def skip_iptest_but_not_pytest(f):' | |||
|
165 | 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 | 163 | def skipif(skip_condition, msg=None): |
|
172 |
|
|
|
164 | """Make function raise SkipTest exception if skip_condition is true | |
|
173 | 165 | |
|
174 | 166 | Parameters |
|
175 | 167 | ---------- |
@@ -188,57 +180,15 b' def skipif(skip_condition, msg=None):' | |||
|
188 | 180 | Decorator, which, when applied to a function, causes SkipTest |
|
189 | 181 | to be raised when the skip_condition was True, and the function |
|
190 | 182 | to be called normally otherwise. |
|
183 | """ | |
|
184 | if msg is None: | |
|
185 | msg = "Test skipped due to test condition." | |
|
191 | 186 | |
|
192 |
|
|
|
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 | |
|
187 | import pytest | |
|
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 | 193 | # A version with the condition set to true, common case just to attach a message |
|
244 | 194 | # to a skip decorator |
@@ -265,12 +215,7 b' def skip(msg=None):' | |||
|
265 | 215 | def onlyif(condition, msg): |
|
266 | 216 | """The reverse from skipif, see skipif for details.""" |
|
267 | 217 | |
|
268 | if callable(condition): | |
|
269 | skip_condition = lambda : not condition() | |
|
270 | else: | |
|
271 | skip_condition = lambda : not condition | |
|
272 | ||
|
273 | return skipif(skip_condition, msg) | |
|
218 | return skipif(not condition, msg) | |
|
274 | 219 | |
|
275 | 220 | #----------------------------------------------------------------------------- |
|
276 | 221 | # Utility functions for decorators |
@@ -351,8 +296,6 b" skipif_not_matplotlib = skip_without('matplotlib')" | |||
|
351 | 296 | |
|
352 | 297 | skipif_not_sympy = skip_without('sympy') |
|
353 | 298 | |
|
354 | skip_known_failure = knownfailureif(True,'This test is known to fail') | |
|
355 | ||
|
356 | 299 | # A null 'decorator', useful to make more readable code that needs to pick |
|
357 | 300 | # between different decorators based on OS or other conditions |
|
358 | 301 | null_deco = lambda f: f |
@@ -19,35 +19,14 b' Limitations:' | |||
|
19 | 19 | # Module imports |
|
20 | 20 | |
|
21 | 21 | # From the standard library |
|
22 | import builtins as builtin_mod | |
|
23 | 22 | import doctest |
|
24 | 23 | import inspect |
|
25 | 24 | import logging |
|
26 | 25 | import os |
|
27 | 26 | import re |
|
28 | import sys | |
|
29 | from importlib import import_module | |
|
30 | from io import StringIO | |
|
31 | 27 | |
|
32 | 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 | 31 | # Module globals and other constants |
|
53 | 32 | #----------------------------------------------------------------------------- |
@@ -195,129 +174,6 b' class IPDoctestOutputChecker(doctest.OutputChecker):' | |||
|
195 | 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 | 177 | # A simple subclassing of the original with a different class name, so we can |
|
322 | 178 | # distinguish and treat differently IPython examples from pure python ones. |
|
323 | 179 | class IPExample(doctest.Example): pass |
@@ -594,169 +450,3 b' class DocFileCase(doctest.DocFileCase):' | |||
|
594 | 450 | """ |
|
595 | 451 | def address(self): |
|
596 | 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 | 14 | from os.path import join, abspath |
|
15 | 15 | from imp import reload |
|
16 | 16 | |
|
17 | from nose import SkipTest, with_setup | |
|
18 | 17 | import pytest |
|
19 | 18 | |
|
20 | 19 | import IPython |
@@ -98,8 +97,17 b' def teardown_environment():' | |||
|
98 | 97 | if hasattr(sys, 'frozen'): |
|
99 | 98 | del sys.frozen |
|
100 | 99 | |
|
100 | ||
|
101 | 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 | 112 | @skip_if_not_win32 |
|
105 | 113 | @with_environment |
@@ -291,7 +299,8 b' class TestRaiseDeprecation(unittest.TestCase):' | |||
|
291 | 299 | else: |
|
292 | 300 | # I can still write to an unwritable dir, |
|
293 | 301 | # assume I'm root and skip the test |
|
294 |
|
|
|
302 | pytest.skip("I can't create directories that I can't write to") | |
|
303 | ||
|
295 | 304 | with self.assertWarnsRegex(UserWarning, 'is not a writable location'): |
|
296 | 305 | ipdir = paths.get_ipython_dir() |
|
297 | 306 | env.pop('IPYTHON_DIR', None) |
@@ -26,13 +26,9 b' init:' | |||
|
26 | 26 | install: |
|
27 | 27 | - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" |
|
28 | 28 | - python -m pip install --upgrade setuptools pip |
|
29 |
- pip install |
|
|
29 | - pip install pytest pytest-cov pytest-trio matplotlib pandas | |
|
30 | 30 | - pip install -e .[test] |
|
31 | - mkdir results | |
|
32 | - cd results | |
|
33 | 31 | test_script: |
|
34 | - iptest --coverage xml | |
|
35 | - cd .. | |
|
36 | 32 | - pytest --color=yes -ra --cov --cov-report=xml |
|
37 | 33 | on_finish: |
|
38 | 34 | - curl -Os https://uploader.codecov.io/latest/windows/codecov.exe |
@@ -5,7 +5,6 b' dependencies:' | |||
|
5 | 5 | - sphinx>=1.8 |
|
6 | 6 | - sphinx_rtd_theme |
|
7 | 7 | - numpy |
|
8 | - nose | |
|
9 | 8 | - testpath |
|
10 | 9 | - matplotlib |
|
11 | 10 | - pip: |
@@ -100,12 +100,11 b' permissions, you may need to run the last command with :command:`sudo`. You can' | |||
|
100 | 100 | also install in user specific location by using the ``--user`` flag in |
|
101 | 101 | conjunction with pip. |
|
102 | 102 | |
|
103 |
To run IPython's test suite, use the :command:` |
|
|
104 | the IPython source tree: | |
|
103 | To run IPython's test suite, use the :command:`pytest` command: | |
|
105 | 104 | |
|
106 | 105 | .. code-block:: bash |
|
107 | 106 | |
|
108 |
$ |
|
|
107 | $ pytest | |
|
109 | 108 | |
|
110 | 109 | .. _devinstall: |
|
111 | 110 |
@@ -175,7 +175,6 b' extras_require = dict(' | |||
|
175 | 175 | qtconsole=["qtconsole"], |
|
176 | 176 | doc=["Sphinx>=1.3"], |
|
177 | 177 | test=[ |
|
178 | "nose>=0.10.1", | |
|
179 | 178 | "pytest", |
|
180 | 179 | "requests", |
|
181 | 180 | "testpath", |
@@ -234,7 +234,6 b' def find_entry_points():' | |||
|
234 | 234 | """ |
|
235 | 235 | ep = [ |
|
236 | 236 | 'ipython%s = IPython:start_ipython', |
|
237 | 'iptest%s = IPython.testing.iptestcontroller:main', | |
|
238 | 237 | ] |
|
239 | 238 | suffix = str(sys.version_info[0]) |
|
240 | 239 | return [e % '' for e in ep] + [e % suffix for e in ep] |
@@ -7,7 +7,7 b" python -c 'import keyring'" | |||
|
7 | 7 | python -c 'import twine' |
|
8 | 8 | python -c 'import sphinx' |
|
9 | 9 | python -c 'import sphinx_rtd_theme' |
|
10 |
python -c 'import |
|
|
10 | python -c 'import pytest' | |
|
11 | 11 | |
|
12 | 12 | |
|
13 | 13 | BLACK=$(tput setaf 1) |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now