##// END OF EJS Templates
Also run test with Pytest....
Matthias Bussonnier -
Show More
@@ -0,0 +1,69 b''
1 import types
2 import sys
3 import builtins
4 import os
5 import pytest
6 import pathlib
7 import shutil
8
9 from IPython.testing import tools
10
11
12 def get_ipython():
13 from IPython.terminal.interactiveshell import TerminalInteractiveShell
14 if TerminalInteractiveShell._instance:
15 return TerminalInteractiveShell.instance()
16
17 config = tools.default_config()
18 config.TerminalInteractiveShell.simple_prompt = True
19
20 # Create and initialize our test-friendly IPython instance.
21 shell = TerminalInteractiveShell.instance(config=config)
22 return shell
23
24
25 @pytest.fixture(scope='session', autouse=True)
26 def work_path():
27 path = pathlib.Path("./tmp-ipython-pytest-profiledir")
28 os.environ["IPYTHONDIR"] = str(path.absolute())
29 if path.exists():
30 raise ValueError('IPython dir temporary path already exists ! Did previous test run exit successfully ?')
31 path.mkdir()
32 yield
33 shutil.rmtree(str(path.resolve()))
34
35
36 def nopage(strng, start=0, screen_lines=0, pager_cmd=None):
37 if isinstance(strng, dict):
38 strng = strng.get("text/plain", "")
39 print(strng)
40
41
42 def xsys(self, cmd):
43 """Replace the default system call with a capturing one for doctest.
44 """
45 # We use getoutput, but we need to strip it because pexpect captures
46 # the trailing newline differently from commands.getoutput
47 print(self.getoutput(cmd, split=False, depth=1).rstrip(), end="", file=sys.stdout)
48 sys.stdout.flush()
49
50
51 # for things to work correctly we would need this as a session fixture;
52 # unfortunately this will fail on some test that get executed as _collection_
53 # time (before the fixture run), in particular parametrized test that contain
54 # yields. so for now execute at import time.
55 #@pytest.fixture(autouse=True, scope='session')
56 def inject():
57
58 builtins.get_ipython = get_ipython
59 builtins._ip = get_ipython()
60 builtins.ip = get_ipython()
61 builtins.ip.system = types.MethodType(xsys, ip)
62 builtins.ip.builtin_trap.activate()
63 from IPython.core import page
64
65 page.pager_page = nopage
66 # yield
67
68
69 inject()
@@ -0,0 +1,2 b''
1 [pytest]
2 addopts = --duration=10
@@ -1,107 +1,109 b''
1 # http://travis-ci.org/#!/ipython/ipython
1 # http://travis-ci.org/#!/ipython/ipython
2 language: python
2 language: python
3 os: linux
3 os: linux
4
4
5 addons:
5 addons:
6 apt:
6 apt:
7 packages:
7 packages:
8 - graphviz
8 - graphviz
9
9
10 python:
10 python:
11 - 3.6
11 - 3.6
12 - 3.5
12 - 3.5
13
13
14 sudo: false
14 sudo: false
15
15
16 env:
16 env:
17 global:
17 global:
18 - PATH=$TRAVIS_BUILD_DIR/pandoc:$PATH
18 - PATH=$TRAVIS_BUILD_DIR/pandoc:$PATH
19
19
20 group: edge
20 group: edge
21
21
22 before_install:
22 before_install:
23 - |
23 - |
24 # install Python on macOS
24 # install Python on macOS
25 if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
25 if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
26 env | sort
26 env | sort
27 if ! which python$TRAVIS_PYTHON_VERSION; then
27 if ! which python$TRAVIS_PYTHON_VERSION; then
28 HOMEBREW_NO_AUTO_UPDATE=1 brew tap minrk/homebrew-python-frameworks
28 HOMEBREW_NO_AUTO_UPDATE=1 brew tap minrk/homebrew-python-frameworks
29 HOMEBREW_NO_AUTO_UPDATE=1 brew cask install python-framework-${TRAVIS_PYTHON_VERSION/./}
29 HOMEBREW_NO_AUTO_UPDATE=1 brew cask install python-framework-${TRAVIS_PYTHON_VERSION/./}
30 fi
30 fi
31 python3 -m pip install virtualenv
31 python3 -m pip install virtualenv
32 python3 -m virtualenv -p $(which python$TRAVIS_PYTHON_VERSION) ~/travis-env
32 python3 -m virtualenv -p $(which python$TRAVIS_PYTHON_VERSION) ~/travis-env
33 source ~/travis-env/bin/activate
33 source ~/travis-env/bin/activate
34 fi
34 fi
35 - python --version
35 - python --version
36
36
37 install:
37 install:
38 - pip install pip --upgrade
38 - pip install pip --upgrade
39 - pip install setuptools --upgrade
39 - pip install setuptools --upgrade
40 - pip install -e file://$PWD#egg=ipython[test] --upgrade
40 - pip install -e file://$PWD#egg=ipython[test] --upgrade
41 - pip install trio curio
41 - pip install trio curio
42 - pip install 'pytest<4' matplotlib
42 - pip install codecov check-manifest --upgrade
43 - pip install codecov check-manifest --upgrade
43
44
44 script:
45 script:
45 - check-manifest
46 - check-manifest
46 - |
47 - |
47 if [[ "$TRAVIS_PYTHON_VERSION" == "nightly" ]]; then
48 if [[ "$TRAVIS_PYTHON_VERSION" == "nightly" ]]; then
48 # on nightly fake parso known the grammar
49 # on nightly fake parso known the grammar
49 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
50 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
50 fi
51 fi
51 - cd /tmp && iptest --coverage xml && cd -
52 - cd /tmp && iptest --coverage xml && cd -
53 - pytest IPython
52 # On the latest Python (on Linux) only, make sure that the docs build.
54 # On the latest Python (on Linux) only, make sure that the docs build.
53 - |
55 - |
54 if [[ "$TRAVIS_PYTHON_VERSION" == "3.7" ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
56 if [[ "$TRAVIS_PYTHON_VERSION" == "3.7" ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
55 pip install -r docs/requirements.txt
57 pip install -r docs/requirements.txt
56 python tools/fixup_whats_new_pr.py
58 python tools/fixup_whats_new_pr.py
57 make -C docs/ html SPHINXOPTS="-W"
59 make -C docs/ html SPHINXOPTS="-W"
58 fi
60 fi
59
61
60 after_success:
62 after_success:
61 - cp /tmp/ipy_coverage.xml ./
63 - cp /tmp/ipy_coverage.xml ./
62 - cp /tmp/.coverage ./
64 - cp /tmp/.coverage ./
63 - codecov
65 - codecov
64
66
65 matrix:
67 matrix:
66 include:
68 include:
67 - python: "3.7"
69 - python: "3.7"
68 dist: xenial
70 dist: xenial
69 sudo: true
71 sudo: true
70 - python: "3.8-dev"
72 - python: "3.8-dev"
71 dist: xenial
73 dist: xenial
72 sudo: true
74 sudo: true
73 - python: "3.7-dev"
75 - python: "3.7-dev"
74 dist: xenial
76 dist: xenial
75 sudo: true
77 sudo: true
76 - python: "nightly"
78 - python: "nightly"
77 dist: xenial
79 dist: xenial
78 sudo: true
80 sudo: true
79 - os: osx
81 - os: osx
80 language: generic
82 language: generic
81 python: 3.6
83 python: 3.6
82 env: TRAVIS_PYTHON_VERSION=3.6
84 env: TRAVIS_PYTHON_VERSION=3.6
83 - os: osx
85 - os: osx
84 language: generic
86 language: generic
85 python: 3.7
87 python: 3.7
86 env: TRAVIS_PYTHON_VERSION=3.7
88 env: TRAVIS_PYTHON_VERSION=3.7
87 allow_failures:
89 allow_failures:
88 - python: nightly
90 - python: nightly
89
91
90 before_deploy:
92 before_deploy:
91 - rm -rf dist/
93 - rm -rf dist/
92 - python setup.py sdist
94 - python setup.py sdist
93 - python setup.py bdist_wheel
95 - python setup.py bdist_wheel
94
96
95 deploy:
97 deploy:
96 provider: releases
98 provider: releases
97 api_key:
99 api_key:
98 secure: Y/Ae9tYs5aoBU8bDjN2YrwGG6tCbezj/h3Lcmtx8HQavSbBgXnhnZVRb2snOKD7auqnqjfT/7QMm4ZyKvaOEgyggGktKqEKYHC8KOZ7yp8I5/UMDtk6j9TnXpSqqBxPiud4MDV76SfRYEQiaDoG4tGGvSfPJ9KcNjKrNvSyyxns=
100 secure: Y/Ae9tYs5aoBU8bDjN2YrwGG6tCbezj/h3Lcmtx8HQavSbBgXnhnZVRb2snOKD7auqnqjfT/7QMm4ZyKvaOEgyggGktKqEKYHC8KOZ7yp8I5/UMDtk6j9TnXpSqqBxPiud4MDV76SfRYEQiaDoG4tGGvSfPJ9KcNjKrNvSyyxns=
99 file: dist/*
101 file: dist/*
100 file_glob: true
102 file_glob: true
101 skip_cleanup: true
103 skip_cleanup: true
102 on:
104 on:
103 repo: ipython/ipython
105 repo: ipython/ipython
104 all_branches: true # Backports are released from e.g. 5.x branch
106 all_branches: true # Backports are released from e.g. 5.x branch
105 tags: true
107 tags: true
106 python: 3.6 # Any version should work, but we only need one
108 python: 3.6 # Any version should work, but we only need one
107 condition: $TRAVIS_OS_NAME = "linux"
109 condition: $TRAVIS_OS_NAME = "linux"
@@ -1,62 +1,65 b''
1 from IPython.utils.capture import capture_output
1 from IPython.utils.capture import capture_output
2
2
3 import nose.tools as nt
3 import nose.tools as nt
4
4
5 def test_alias_lifecycle():
5 def test_alias_lifecycle():
6 name = 'test_alias1'
6 name = 'test_alias1'
7 cmd = 'echo "Hello"'
7 cmd = 'echo "Hello"'
8 am = _ip.alias_manager
8 am = _ip.alias_manager
9 am.clear_aliases()
9 am.clear_aliases()
10 am.define_alias(name, cmd)
10 am.define_alias(name, cmd)
11 assert am.is_alias(name)
11 assert am.is_alias(name)
12 nt.assert_equal(am.retrieve_alias(name), cmd)
12 nt.assert_equal(am.retrieve_alias(name), cmd)
13 nt.assert_in((name, cmd), am.aliases)
13 nt.assert_in((name, cmd), am.aliases)
14
14
15 # Test running the alias
15 # Test running the alias
16 orig_system = _ip.system
16 orig_system = _ip.system
17 result = []
17 result = []
18 _ip.system = result.append
18 _ip.system = result.append
19 try:
19 try:
20 _ip.run_cell('%{}'.format(name))
20 _ip.run_cell('%{}'.format(name))
21 result = [c.strip() for c in result]
21 result = [c.strip() for c in result]
22 nt.assert_equal(result, [cmd])
22 nt.assert_equal(result, [cmd])
23 finally:
23 finally:
24 _ip.system = orig_system
24 _ip.system = orig_system
25
25
26 # Test removing the alias
26 # Test removing the alias
27 am.undefine_alias(name)
27 am.undefine_alias(name)
28 assert not am.is_alias(name)
28 assert not am.is_alias(name)
29 with nt.assert_raises(ValueError):
29 with nt.assert_raises(ValueError):
30 am.retrieve_alias(name)
30 am.retrieve_alias(name)
31 nt.assert_not_in((name, cmd), am.aliases)
31 nt.assert_not_in((name, cmd), am.aliases)
32
32
33
33
34 def test_alias_args_error():
34 def test_alias_args_error():
35 """Error expanding with wrong number of arguments"""
35 """Error expanding with wrong number of arguments"""
36 _ip.alias_manager.define_alias('parts', 'echo first %s second %s')
36 _ip.alias_manager.define_alias('parts', 'echo first %s second %s')
37 # capture stderr:
37 # capture stderr:
38 with capture_output() as cap:
38 with capture_output() as cap:
39 _ip.run_cell('parts 1')
39 _ip.run_cell('parts 1')
40
40
41 nt.assert_equal(cap.stderr.split(':')[0], 'UsageError')
41 nt.assert_equal(cap.stderr.split(':')[0], 'UsageError')
42
42
43 def test_alias_args_commented():
43 def test_alias_args_commented():
44 """Check that alias correctly ignores 'commented out' args"""
44 """Check that alias correctly ignores 'commented out' args"""
45 _ip.magic('alias commetarg echo this is %%s a commented out arg')
45 _ip.magic('alias commetarg echo this is %%s a commented out arg')
46
46
47 with capture_output() as cap:
47 with capture_output() as cap:
48 _ip.run_cell('commetarg')
48 _ip.run_cell('commetarg')
49
49
50 nt.assert_equal(cap.stdout, 'this is %s a commented out arg')
50 # strip() is for pytest compat; testing via iptest patch IPython shell
51 # in testin.globalipapp and replace the system call which messed up the
52 # \r\n
53 assert cap.stdout.strip() == 'this is %s a commented out arg'
51
54
52 def test_alias_args_commented_nargs():
55 def test_alias_args_commented_nargs():
53 """Check that alias correctly counts args, excluding those commented out"""
56 """Check that alias correctly counts args, excluding those commented out"""
54 am = _ip.alias_manager
57 am = _ip.alias_manager
55 alias_name = 'comargcount'
58 alias_name = 'comargcount'
56 cmd = 'echo this is %%s a commented out arg and this is not %s'
59 cmd = 'echo this is %%s a commented out arg and this is not %s'
57
60
58 am.define_alias(alias_name, cmd)
61 am.define_alias(alias_name, cmd)
59 assert am.is_alias(alias_name)
62 assert am.is_alias(alias_name)
60
63
61 thealias = am.get_alias(alias_name)
64 thealias = am.get_alias(alias_name)
62 nt.assert_equal(thealias.nargs, 1) No newline at end of file
65 nt.assert_equal(thealias.nargs, 1)
@@ -1,1172 +1,1170 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for various magic functions.
2 """Tests for various magic functions.
3
3
4 Needs to be run by nose (to make ipython session available).
4 Needs to be run by nose (to make ipython session available).
5 """
5 """
6
6
7 import io
7 import io
8 import os
8 import os
9 import re
9 import re
10 import sys
10 import sys
11 import warnings
11 import warnings
12 from textwrap import dedent
12 from textwrap import dedent
13 from unittest import TestCase
13 from unittest import TestCase
14 from importlib import invalidate_caches
14 from importlib import invalidate_caches
15 from io import StringIO
15 from io import StringIO
16
16
17 import nose.tools as nt
17 import nose.tools as nt
18
18
19 import shlex
19 import shlex
20
20
21 from IPython import get_ipython
21 from IPython import get_ipython
22 from IPython.core import magic
22 from IPython.core import magic
23 from IPython.core.error import UsageError
23 from IPython.core.error import UsageError
24 from IPython.core.magic import (Magics, magics_class, line_magic,
24 from IPython.core.magic import (Magics, magics_class, line_magic,
25 cell_magic,
25 cell_magic,
26 register_line_magic, register_cell_magic)
26 register_line_magic, register_cell_magic)
27 from IPython.core.magics import execution, script, code, logging, osm
27 from IPython.core.magics import execution, script, code, logging, osm
28 from IPython.testing import decorators as dec
28 from IPython.testing import decorators as dec
29 from IPython.testing import tools as tt
29 from IPython.testing import tools as tt
30 from IPython.utils.io import capture_output
30 from IPython.utils.io import capture_output
31 from IPython.utils.tempdir import (TemporaryDirectory,
31 from IPython.utils.tempdir import (TemporaryDirectory,
32 TemporaryWorkingDirectory)
32 TemporaryWorkingDirectory)
33 from IPython.utils.process import find_cmd
33 from IPython.utils.process import find_cmd
34
34
35
35
36
37 _ip = get_ipython()
38
39 @magic.magics_class
36 @magic.magics_class
40 class DummyMagics(magic.Magics): pass
37 class DummyMagics(magic.Magics): pass
41
38
42 def test_extract_code_ranges():
39 def test_extract_code_ranges():
43 instr = "1 3 5-6 7-9 10:15 17: :10 10- -13 :"
40 instr = "1 3 5-6 7-9 10:15 17: :10 10- -13 :"
44 expected = [(0, 1),
41 expected = [(0, 1),
45 (2, 3),
42 (2, 3),
46 (4, 6),
43 (4, 6),
47 (6, 9),
44 (6, 9),
48 (9, 14),
45 (9, 14),
49 (16, None),
46 (16, None),
50 (None, 9),
47 (None, 9),
51 (9, None),
48 (9, None),
52 (None, 13),
49 (None, 13),
53 (None, None)]
50 (None, None)]
54 actual = list(code.extract_code_ranges(instr))
51 actual = list(code.extract_code_ranges(instr))
55 nt.assert_equal(actual, expected)
52 nt.assert_equal(actual, expected)
56
53
57 def test_extract_symbols():
54 def test_extract_symbols():
58 source = """import foo\na = 10\ndef b():\n return 42\n\n\nclass A: pass\n\n\n"""
55 source = """import foo\na = 10\ndef b():\n return 42\n\n\nclass A: pass\n\n\n"""
59 symbols_args = ["a", "b", "A", "A,b", "A,a", "z"]
56 symbols_args = ["a", "b", "A", "A,b", "A,a", "z"]
60 expected = [([], ['a']),
57 expected = [([], ['a']),
61 (["def b():\n return 42\n"], []),
58 (["def b():\n return 42\n"], []),
62 (["class A: pass\n"], []),
59 (["class A: pass\n"], []),
63 (["class A: pass\n", "def b():\n return 42\n"], []),
60 (["class A: pass\n", "def b():\n return 42\n"], []),
64 (["class A: pass\n"], ['a']),
61 (["class A: pass\n"], ['a']),
65 ([], ['z'])]
62 ([], ['z'])]
66 for symbols, exp in zip(symbols_args, expected):
63 for symbols, exp in zip(symbols_args, expected):
67 nt.assert_equal(code.extract_symbols(source, symbols), exp)
64 nt.assert_equal(code.extract_symbols(source, symbols), exp)
68
65
69
66
70 def test_extract_symbols_raises_exception_with_non_python_code():
67 def test_extract_symbols_raises_exception_with_non_python_code():
71 source = ("=begin A Ruby program :)=end\n"
68 source = ("=begin A Ruby program :)=end\n"
72 "def hello\n"
69 "def hello\n"
73 "puts 'Hello world'\n"
70 "puts 'Hello world'\n"
74 "end")
71 "end")
75 with nt.assert_raises(SyntaxError):
72 with nt.assert_raises(SyntaxError):
76 code.extract_symbols(source, "hello")
73 code.extract_symbols(source, "hello")
77
74
78
75
79 def test_magic_not_found():
76 def test_magic_not_found():
80 # magic not found raises UsageError
77 # magic not found raises UsageError
81 with nt.assert_raises(UsageError):
78 with nt.assert_raises(UsageError):
82 _ip.magic('doesntexist')
79 _ip.magic('doesntexist')
83
80
84 # ensure result isn't success when a magic isn't found
81 # ensure result isn't success when a magic isn't found
85 result = _ip.run_cell('%doesntexist')
82 result = _ip.run_cell('%doesntexist')
86 assert isinstance(result.error_in_exec, UsageError)
83 assert isinstance(result.error_in_exec, UsageError)
87
84
88
85
89 def test_cell_magic_not_found():
86 def test_cell_magic_not_found():
90 # magic not found raises UsageError
87 # magic not found raises UsageError
91 with nt.assert_raises(UsageError):
88 with nt.assert_raises(UsageError):
92 _ip.run_cell_magic('doesntexist', 'line', 'cell')
89 _ip.run_cell_magic('doesntexist', 'line', 'cell')
93
90
94 # ensure result isn't success when a magic isn't found
91 # ensure result isn't success when a magic isn't found
95 result = _ip.run_cell('%%doesntexist')
92 result = _ip.run_cell('%%doesntexist')
96 assert isinstance(result.error_in_exec, UsageError)
93 assert isinstance(result.error_in_exec, UsageError)
97
94
98
95
99 def test_magic_error_status():
96 def test_magic_error_status():
100 def fail(shell):
97 def fail(shell):
101 1/0
98 1/0
102 _ip.register_magic_function(fail)
99 _ip.register_magic_function(fail)
103 result = _ip.run_cell('%fail')
100 result = _ip.run_cell('%fail')
104 assert isinstance(result.error_in_exec, ZeroDivisionError)
101 assert isinstance(result.error_in_exec, ZeroDivisionError)
105
102
106
103
107 def test_config():
104 def test_config():
108 """ test that config magic does not raise
105 """ test that config magic does not raise
109 can happen if Configurable init is moved too early into
106 can happen if Configurable init is moved too early into
110 Magics.__init__ as then a Config object will be registered as a
107 Magics.__init__ as then a Config object will be registered as a
111 magic.
108 magic.
112 """
109 """
113 ## should not raise.
110 ## should not raise.
114 _ip.magic('config')
111 _ip.magic('config')
115
112
116 def test_config_available_configs():
113 def test_config_available_configs():
117 """ test that config magic prints available configs in unique and
114 """ test that config magic prints available configs in unique and
118 sorted order. """
115 sorted order. """
119 with capture_output() as captured:
116 with capture_output() as captured:
120 _ip.magic('config')
117 _ip.magic('config')
121
118
122 stdout = captured.stdout
119 stdout = captured.stdout
123 config_classes = stdout.strip().split('\n')[1:]
120 config_classes = stdout.strip().split('\n')[1:]
124 nt.assert_list_equal(config_classes, sorted(set(config_classes)))
121 nt.assert_list_equal(config_classes, sorted(set(config_classes)))
125
122
126 def test_config_print_class():
123 def test_config_print_class():
127 """ test that config with a classname prints the class's options. """
124 """ test that config with a classname prints the class's options. """
128 with capture_output() as captured:
125 with capture_output() as captured:
129 _ip.magic('config TerminalInteractiveShell')
126 _ip.magic('config TerminalInteractiveShell')
130
127
131 stdout = captured.stdout
128 stdout = captured.stdout
132 if not re.match("TerminalInteractiveShell.* options", stdout.splitlines()[0]):
129 if not re.match("TerminalInteractiveShell.* options", stdout.splitlines()[0]):
133 print(stdout)
130 print(stdout)
134 raise AssertionError("1st line of stdout not like "
131 raise AssertionError("1st line of stdout not like "
135 "'TerminalInteractiveShell.* options'")
132 "'TerminalInteractiveShell.* options'")
136
133
137 def test_rehashx():
134 def test_rehashx():
138 # clear up everything
135 # clear up everything
139 _ip.alias_manager.clear_aliases()
136 _ip.alias_manager.clear_aliases()
140 del _ip.db['syscmdlist']
137 del _ip.db['syscmdlist']
141
138
142 _ip.magic('rehashx')
139 _ip.magic('rehashx')
143 # Practically ALL ipython development systems will have more than 10 aliases
140 # Practically ALL ipython development systems will have more than 10 aliases
144
141
145 nt.assert_true(len(_ip.alias_manager.aliases) > 10)
142 nt.assert_true(len(_ip.alias_manager.aliases) > 10)
146 for name, cmd in _ip.alias_manager.aliases:
143 for name, cmd in _ip.alias_manager.aliases:
147 # we must strip dots from alias names
144 # we must strip dots from alias names
148 nt.assert_not_in('.', name)
145 nt.assert_not_in('.', name)
149
146
150 # rehashx must fill up syscmdlist
147 # rehashx must fill up syscmdlist
151 scoms = _ip.db['syscmdlist']
148 scoms = _ip.db['syscmdlist']
152 nt.assert_true(len(scoms) > 10)
149 nt.assert_true(len(scoms) > 10)
150
153
151
154
152
155 def test_magic_parse_options():
153 def test_magic_parse_options():
156 """Test that we don't mangle paths when parsing magic options."""
154 """Test that we don't mangle paths when parsing magic options."""
157 ip = get_ipython()
155 ip = get_ipython()
158 path = 'c:\\x'
156 path = 'c:\\x'
159 m = DummyMagics(ip)
157 m = DummyMagics(ip)
160 opts = m.parse_options('-f %s' % path,'f:')[0]
158 opts = m.parse_options('-f %s' % path,'f:')[0]
161 # argv splitting is os-dependent
159 # argv splitting is os-dependent
162 if os.name == 'posix':
160 if os.name == 'posix':
163 expected = 'c:x'
161 expected = 'c:x'
164 else:
162 else:
165 expected = path
163 expected = path
166 nt.assert_equal(opts['f'], expected)
164 nt.assert_equal(opts['f'], expected)
167
165
168 def test_magic_parse_long_options():
166 def test_magic_parse_long_options():
169 """Magic.parse_options can handle --foo=bar long options"""
167 """Magic.parse_options can handle --foo=bar long options"""
170 ip = get_ipython()
168 ip = get_ipython()
171 m = DummyMagics(ip)
169 m = DummyMagics(ip)
172 opts, _ = m.parse_options('--foo --bar=bubble', 'a', 'foo', 'bar=')
170 opts, _ = m.parse_options('--foo --bar=bubble', 'a', 'foo', 'bar=')
173 nt.assert_in('foo', opts)
171 nt.assert_in('foo', opts)
174 nt.assert_in('bar', opts)
172 nt.assert_in('bar', opts)
175 nt.assert_equal(opts['bar'], "bubble")
173 nt.assert_equal(opts['bar'], "bubble")
176
174
177
175
178 @dec.skip_without('sqlite3')
176 @dec.skip_without('sqlite3')
179 def doctest_hist_f():
177 def doctest_hist_f():
180 """Test %hist -f with temporary filename.
178 """Test %hist -f with temporary filename.
181
179
182 In [9]: import tempfile
180 In [9]: import tempfile
183
181
184 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
182 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
185
183
186 In [11]: %hist -nl -f $tfile 3
184 In [11]: %hist -nl -f $tfile 3
187
185
188 In [13]: import os; os.unlink(tfile)
186 In [13]: import os; os.unlink(tfile)
189 """
187 """
190
188
191
189
192 @dec.skip_without('sqlite3')
190 @dec.skip_without('sqlite3')
193 def doctest_hist_r():
191 def doctest_hist_r():
194 """Test %hist -r
192 """Test %hist -r
195
193
196 XXX - This test is not recording the output correctly. For some reason, in
194 XXX - This test is not recording the output correctly. For some reason, in
197 testing mode the raw history isn't getting populated. No idea why.
195 testing mode the raw history isn't getting populated. No idea why.
198 Disabling the output checking for now, though at least we do run it.
196 Disabling the output checking for now, though at least we do run it.
199
197
200 In [1]: 'hist' in _ip.lsmagic()
198 In [1]: 'hist' in _ip.lsmagic()
201 Out[1]: True
199 Out[1]: True
202
200
203 In [2]: x=1
201 In [2]: x=1
204
202
205 In [3]: %hist -rl 2
203 In [3]: %hist -rl 2
206 x=1 # random
204 x=1 # random
207 %hist -r 2
205 %hist -r 2
208 """
206 """
209
207
210
208
211 @dec.skip_without('sqlite3')
209 @dec.skip_without('sqlite3')
212 def doctest_hist_op():
210 def doctest_hist_op():
213 """Test %hist -op
211 """Test %hist -op
214
212
215 In [1]: class b(float):
213 In [1]: class b(float):
216 ...: pass
214 ...: pass
217 ...:
215 ...:
218
216
219 In [2]: class s(object):
217 In [2]: class s(object):
220 ...: def __str__(self):
218 ...: def __str__(self):
221 ...: return 's'
219 ...: return 's'
222 ...:
220 ...:
223
221
224 In [3]:
222 In [3]:
225
223
226 In [4]: class r(b):
224 In [4]: class r(b):
227 ...: def __repr__(self):
225 ...: def __repr__(self):
228 ...: return 'r'
226 ...: return 'r'
229 ...:
227 ...:
230
228
231 In [5]: class sr(s,r): pass
229 In [5]: class sr(s,r): pass
232 ...:
230 ...:
233
231
234 In [6]:
232 In [6]:
235
233
236 In [7]: bb=b()
234 In [7]: bb=b()
237
235
238 In [8]: ss=s()
236 In [8]: ss=s()
239
237
240 In [9]: rr=r()
238 In [9]: rr=r()
241
239
242 In [10]: ssrr=sr()
240 In [10]: ssrr=sr()
243
241
244 In [11]: 4.5
242 In [11]: 4.5
245 Out[11]: 4.5
243 Out[11]: 4.5
246
244
247 In [12]: str(ss)
245 In [12]: str(ss)
248 Out[12]: 's'
246 Out[12]: 's'
249
247
250 In [13]:
248 In [13]:
251
249
252 In [14]: %hist -op
250 In [14]: %hist -op
253 >>> class b:
251 >>> class b:
254 ... pass
252 ... pass
255 ...
253 ...
256 >>> class s(b):
254 >>> class s(b):
257 ... def __str__(self):
255 ... def __str__(self):
258 ... return 's'
256 ... return 's'
259 ...
257 ...
260 >>>
258 >>>
261 >>> class r(b):
259 >>> class r(b):
262 ... def __repr__(self):
260 ... def __repr__(self):
263 ... return 'r'
261 ... return 'r'
264 ...
262 ...
265 >>> class sr(s,r): pass
263 >>> class sr(s,r): pass
266 >>>
264 >>>
267 >>> bb=b()
265 >>> bb=b()
268 >>> ss=s()
266 >>> ss=s()
269 >>> rr=r()
267 >>> rr=r()
270 >>> ssrr=sr()
268 >>> ssrr=sr()
271 >>> 4.5
269 >>> 4.5
272 4.5
270 4.5
273 >>> str(ss)
271 >>> str(ss)
274 's'
272 's'
275 >>>
273 >>>
276 """
274 """
277
275
278 def test_hist_pof():
276 def test_hist_pof():
279 ip = get_ipython()
277 ip = get_ipython()
280 ip.run_cell(u"1+2", store_history=True)
278 ip.run_cell(u"1+2", store_history=True)
281 #raise Exception(ip.history_manager.session_number)
279 #raise Exception(ip.history_manager.session_number)
282 #raise Exception(list(ip.history_manager._get_range_session()))
280 #raise Exception(list(ip.history_manager._get_range_session()))
283 with TemporaryDirectory() as td:
281 with TemporaryDirectory() as td:
284 tf = os.path.join(td, 'hist.py')
282 tf = os.path.join(td, 'hist.py')
285 ip.run_line_magic('history', '-pof %s' % tf)
283 ip.run_line_magic('history', '-pof %s' % tf)
286 assert os.path.isfile(tf)
284 assert os.path.isfile(tf)
287
285
288
286
289 @dec.skip_without('sqlite3')
287 @dec.skip_without('sqlite3')
290 def test_macro():
288 def test_macro():
291 ip = get_ipython()
289 ip = get_ipython()
292 ip.history_manager.reset() # Clear any existing history.
290 ip.history_manager.reset() # Clear any existing history.
293 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
291 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
294 for i, cmd in enumerate(cmds, start=1):
292 for i, cmd in enumerate(cmds, start=1):
295 ip.history_manager.store_inputs(i, cmd)
293 ip.history_manager.store_inputs(i, cmd)
296 ip.magic("macro test 1-3")
294 ip.magic("macro test 1-3")
297 nt.assert_equal(ip.user_ns["test"].value, "\n".join(cmds)+"\n")
295 nt.assert_equal(ip.user_ns["test"].value, "\n".join(cmds)+"\n")
298
296
299 # List macros
297 # List macros
300 nt.assert_in("test", ip.magic("macro"))
298 nt.assert_in("test", ip.magic("macro"))
301
299
302
300
303 @dec.skip_without('sqlite3')
301 @dec.skip_without('sqlite3')
304 def test_macro_run():
302 def test_macro_run():
305 """Test that we can run a multi-line macro successfully."""
303 """Test that we can run a multi-line macro successfully."""
306 ip = get_ipython()
304 ip = get_ipython()
307 ip.history_manager.reset()
305 ip.history_manager.reset()
308 cmds = ["a=10", "a+=1", "print(a)", "%macro test 2-3"]
306 cmds = ["a=10", "a+=1", "print(a)", "%macro test 2-3"]
309 for cmd in cmds:
307 for cmd in cmds:
310 ip.run_cell(cmd, store_history=True)
308 ip.run_cell(cmd, store_history=True)
311 nt.assert_equal(ip.user_ns["test"].value, "a+=1\nprint(a)\n")
309 nt.assert_equal(ip.user_ns["test"].value, "a+=1\nprint(a)\n")
312 with tt.AssertPrints("12"):
310 with tt.AssertPrints("12"):
313 ip.run_cell("test")
311 ip.run_cell("test")
314 with tt.AssertPrints("13"):
312 with tt.AssertPrints("13"):
315 ip.run_cell("test")
313 ip.run_cell("test")
316
314
317
315
318 def test_magic_magic():
316 def test_magic_magic():
319 """Test %magic"""
317 """Test %magic"""
320 ip = get_ipython()
318 ip = get_ipython()
321 with capture_output() as captured:
319 with capture_output() as captured:
322 ip.magic("magic")
320 ip.magic("magic")
323
321
324 stdout = captured.stdout
322 stdout = captured.stdout
325 nt.assert_in('%magic', stdout)
323 nt.assert_in('%magic', stdout)
326 nt.assert_in('IPython', stdout)
324 nt.assert_in('IPython', stdout)
327 nt.assert_in('Available', stdout)
325 nt.assert_in('Available', stdout)
328
326
329
327
330 @dec.skipif_not_numpy
328 @dec.skipif_not_numpy
331 def test_numpy_reset_array_undec():
329 def test_numpy_reset_array_undec():
332 "Test '%reset array' functionality"
330 "Test '%reset array' functionality"
333 _ip.ex('import numpy as np')
331 _ip.ex('import numpy as np')
334 _ip.ex('a = np.empty(2)')
332 _ip.ex('a = np.empty(2)')
335 nt.assert_in('a', _ip.user_ns)
333 nt.assert_in('a', _ip.user_ns)
336 _ip.magic('reset -f array')
334 _ip.magic('reset -f array')
337 nt.assert_not_in('a', _ip.user_ns)
335 nt.assert_not_in('a', _ip.user_ns)
338
336
339 def test_reset_out():
337 def test_reset_out():
340 "Test '%reset out' magic"
338 "Test '%reset out' magic"
341 _ip.run_cell("parrot = 'dead'", store_history=True)
339 _ip.run_cell("parrot = 'dead'", store_history=True)
342 # test '%reset -f out', make an Out prompt
340 # test '%reset -f out', make an Out prompt
343 _ip.run_cell("parrot", store_history=True)
341 _ip.run_cell("parrot", store_history=True)
344 nt.assert_true('dead' in [_ip.user_ns[x] for x in ('_','__','___')])
342 nt.assert_true('dead' in [_ip.user_ns[x] for x in ('_','__','___')])
345 _ip.magic('reset -f out')
343 _ip.magic('reset -f out')
346 nt.assert_false('dead' in [_ip.user_ns[x] for x in ('_','__','___')])
344 nt.assert_false('dead' in [_ip.user_ns[x] for x in ('_','__','___')])
347 nt.assert_equal(len(_ip.user_ns['Out']), 0)
345 nt.assert_equal(len(_ip.user_ns['Out']), 0)
348
346
349 def test_reset_in():
347 def test_reset_in():
350 "Test '%reset in' magic"
348 "Test '%reset in' magic"
351 # test '%reset -f in'
349 # test '%reset -f in'
352 _ip.run_cell("parrot", store_history=True)
350 _ip.run_cell("parrot", store_history=True)
353 nt.assert_true('parrot' in [_ip.user_ns[x] for x in ('_i','_ii','_iii')])
351 nt.assert_true('parrot' in [_ip.user_ns[x] for x in ('_i','_ii','_iii')])
354 _ip.magic('%reset -f in')
352 _ip.magic('%reset -f in')
355 nt.assert_false('parrot' in [_ip.user_ns[x] for x in ('_i','_ii','_iii')])
353 nt.assert_false('parrot' in [_ip.user_ns[x] for x in ('_i','_ii','_iii')])
356 nt.assert_equal(len(set(_ip.user_ns['In'])), 1)
354 nt.assert_equal(len(set(_ip.user_ns['In'])), 1)
357
355
358 def test_reset_dhist():
356 def test_reset_dhist():
359 "Test '%reset dhist' magic"
357 "Test '%reset dhist' magic"
360 _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing
358 _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing
361 _ip.magic('cd ' + os.path.dirname(nt.__file__))
359 _ip.magic('cd ' + os.path.dirname(nt.__file__))
362 _ip.magic('cd -')
360 _ip.magic('cd -')
363 nt.assert_true(len(_ip.user_ns['_dh']) > 0)
361 nt.assert_true(len(_ip.user_ns['_dh']) > 0)
364 _ip.magic('reset -f dhist')
362 _ip.magic('reset -f dhist')
365 nt.assert_equal(len(_ip.user_ns['_dh']), 0)
363 nt.assert_equal(len(_ip.user_ns['_dh']), 0)
366 _ip.run_cell("_dh = [d for d in tmp]") #restore
364 _ip.run_cell("_dh = [d for d in tmp]") #restore
367
365
368 def test_reset_in_length():
366 def test_reset_in_length():
369 "Test that '%reset in' preserves In[] length"
367 "Test that '%reset in' preserves In[] length"
370 _ip.run_cell("print 'foo'")
368 _ip.run_cell("print 'foo'")
371 _ip.run_cell("reset -f in")
369 _ip.run_cell("reset -f in")
372 nt.assert_equal(len(_ip.user_ns['In']), _ip.displayhook.prompt_count+1)
370 nt.assert_equal(len(_ip.user_ns['In']), _ip.displayhook.prompt_count+1)
373
371
374 def test_tb_syntaxerror():
372 def test_tb_syntaxerror():
375 """test %tb after a SyntaxError"""
373 """test %tb after a SyntaxError"""
376 ip = get_ipython()
374 ip = get_ipython()
377 ip.run_cell("for")
375 ip.run_cell("for")
378
376
379 # trap and validate stdout
377 # trap and validate stdout
380 save_stdout = sys.stdout
378 save_stdout = sys.stdout
381 try:
379 try:
382 sys.stdout = StringIO()
380 sys.stdout = StringIO()
383 ip.run_cell("%tb")
381 ip.run_cell("%tb")
384 out = sys.stdout.getvalue()
382 out = sys.stdout.getvalue()
385 finally:
383 finally:
386 sys.stdout = save_stdout
384 sys.stdout = save_stdout
387 # trim output, and only check the last line
385 # trim output, and only check the last line
388 last_line = out.rstrip().splitlines()[-1].strip()
386 last_line = out.rstrip().splitlines()[-1].strip()
389 nt.assert_equal(last_line, "SyntaxError: invalid syntax")
387 nt.assert_equal(last_line, "SyntaxError: invalid syntax")
390
388
391
389
392 def test_time():
390 def test_time():
393 ip = get_ipython()
391 ip = get_ipython()
394
392
395 with tt.AssertPrints("Wall time: "):
393 with tt.AssertPrints("Wall time: "):
396 ip.run_cell("%time None")
394 ip.run_cell("%time None")
397
395
398 ip.run_cell("def f(kmjy):\n"
396 ip.run_cell("def f(kmjy):\n"
399 " %time print (2*kmjy)")
397 " %time print (2*kmjy)")
400
398
401 with tt.AssertPrints("Wall time: "):
399 with tt.AssertPrints("Wall time: "):
402 with tt.AssertPrints("hihi", suppress=False):
400 with tt.AssertPrints("hihi", suppress=False):
403 ip.run_cell("f('hi')")
401 ip.run_cell("f('hi')")
404
402
405
403
406 @dec.skip_win32
404 @dec.skip_win32
407 def test_time2():
405 def test_time2():
408 ip = get_ipython()
406 ip = get_ipython()
409
407
410 with tt.AssertPrints("CPU times: user "):
408 with tt.AssertPrints("CPU times: user "):
411 ip.run_cell("%time None")
409 ip.run_cell("%time None")
412
410
413 def test_time3():
411 def test_time3():
414 """Erroneous magic function calls, issue gh-3334"""
412 """Erroneous magic function calls, issue gh-3334"""
415 ip = get_ipython()
413 ip = get_ipython()
416 ip.user_ns.pop('run', None)
414 ip.user_ns.pop('run', None)
417
415
418 with tt.AssertNotPrints("not found", channel='stderr'):
416 with tt.AssertNotPrints("not found", channel='stderr'):
419 ip.run_cell("%%time\n"
417 ip.run_cell("%%time\n"
420 "run = 0\n"
418 "run = 0\n"
421 "run += 1")
419 "run += 1")
422
420
423 def test_multiline_time():
421 def test_multiline_time():
424 """Make sure last statement from time return a value."""
422 """Make sure last statement from time return a value."""
425 ip = get_ipython()
423 ip = get_ipython()
426 ip.user_ns.pop('run', None)
424 ip.user_ns.pop('run', None)
427
425
428 ip.run_cell(dedent("""\
426 ip.run_cell(dedent("""\
429 %%time
427 %%time
430 a = "ho"
428 a = "ho"
431 b = "hey"
429 b = "hey"
432 a+b
430 a+b
433 """))
431 """))
434 nt.assert_equal(ip.user_ns_hidden['_'], 'hohey')
432 nt.assert_equal(ip.user_ns_hidden['_'], 'hohey')
435
433
436 def test_time_local_ns():
434 def test_time_local_ns():
437 """
435 """
438 Test that local_ns is actually global_ns when running a cell magic
436 Test that local_ns is actually global_ns when running a cell magic
439 """
437 """
440 ip = get_ipython()
438 ip = get_ipython()
441 ip.run_cell("%%time\n"
439 ip.run_cell("%%time\n"
442 "myvar = 1")
440 "myvar = 1")
443 nt.assert_equal(ip.user_ns['myvar'], 1)
441 nt.assert_equal(ip.user_ns['myvar'], 1)
444 del ip.user_ns['myvar']
442 del ip.user_ns['myvar']
445
443
446 def test_doctest_mode():
444 def test_doctest_mode():
447 "Toggle doctest_mode twice, it should be a no-op and run without error"
445 "Toggle doctest_mode twice, it should be a no-op and run without error"
448 _ip.magic('doctest_mode')
446 _ip.magic('doctest_mode')
449 _ip.magic('doctest_mode')
447 _ip.magic('doctest_mode')
450
448
451
449
452 def test_parse_options():
450 def test_parse_options():
453 """Tests for basic options parsing in magics."""
451 """Tests for basic options parsing in magics."""
454 # These are only the most minimal of tests, more should be added later. At
452 # These are only the most minimal of tests, more should be added later. At
455 # the very least we check that basic text/unicode calls work OK.
453 # the very least we check that basic text/unicode calls work OK.
456 m = DummyMagics(_ip)
454 m = DummyMagics(_ip)
457 nt.assert_equal(m.parse_options('foo', '')[1], 'foo')
455 nt.assert_equal(m.parse_options('foo', '')[1], 'foo')
458 nt.assert_equal(m.parse_options(u'foo', '')[1], u'foo')
456 nt.assert_equal(m.parse_options(u'foo', '')[1], u'foo')
459
457
460
458
461 def test_dirops():
459 def test_dirops():
462 """Test various directory handling operations."""
460 """Test various directory handling operations."""
463 # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/')
461 # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/')
464 curpath = os.getcwd
462 curpath = os.getcwd
465 startdir = os.getcwd()
463 startdir = os.getcwd()
466 ipdir = os.path.realpath(_ip.ipython_dir)
464 ipdir = os.path.realpath(_ip.ipython_dir)
467 try:
465 try:
468 _ip.magic('cd "%s"' % ipdir)
466 _ip.magic('cd "%s"' % ipdir)
469 nt.assert_equal(curpath(), ipdir)
467 nt.assert_equal(curpath(), ipdir)
470 _ip.magic('cd -')
468 _ip.magic('cd -')
471 nt.assert_equal(curpath(), startdir)
469 nt.assert_equal(curpath(), startdir)
472 _ip.magic('pushd "%s"' % ipdir)
470 _ip.magic('pushd "%s"' % ipdir)
473 nt.assert_equal(curpath(), ipdir)
471 nt.assert_equal(curpath(), ipdir)
474 _ip.magic('popd')
472 _ip.magic('popd')
475 nt.assert_equal(curpath(), startdir)
473 nt.assert_equal(curpath(), startdir)
476 finally:
474 finally:
477 os.chdir(startdir)
475 os.chdir(startdir)
478
476
479
477
480 def test_cd_force_quiet():
478 def test_cd_force_quiet():
481 """Test OSMagics.cd_force_quiet option"""
479 """Test OSMagics.cd_force_quiet option"""
482 _ip.config.OSMagics.cd_force_quiet = True
480 _ip.config.OSMagics.cd_force_quiet = True
483 osmagics = osm.OSMagics(shell=_ip)
481 osmagics = osm.OSMagics(shell=_ip)
484
482
485 startdir = os.getcwd()
483 startdir = os.getcwd()
486 ipdir = os.path.realpath(_ip.ipython_dir)
484 ipdir = os.path.realpath(_ip.ipython_dir)
487
485
488 try:
486 try:
489 with tt.AssertNotPrints(ipdir):
487 with tt.AssertNotPrints(ipdir):
490 osmagics.cd('"%s"' % ipdir)
488 osmagics.cd('"%s"' % ipdir)
491 with tt.AssertNotPrints(startdir):
489 with tt.AssertNotPrints(startdir):
492 osmagics.cd('-')
490 osmagics.cd('-')
493 finally:
491 finally:
494 os.chdir(startdir)
492 os.chdir(startdir)
495
493
496
494
497 def test_xmode():
495 def test_xmode():
498 # Calling xmode three times should be a no-op
496 # Calling xmode three times should be a no-op
499 xmode = _ip.InteractiveTB.mode
497 xmode = _ip.InteractiveTB.mode
500 for i in range(4):
498 for i in range(4):
501 _ip.magic("xmode")
499 _ip.magic("xmode")
502 nt.assert_equal(_ip.InteractiveTB.mode, xmode)
500 nt.assert_equal(_ip.InteractiveTB.mode, xmode)
503
501
504 def test_reset_hard():
502 def test_reset_hard():
505 monitor = []
503 monitor = []
506 class A(object):
504 class A(object):
507 def __del__(self):
505 def __del__(self):
508 monitor.append(1)
506 monitor.append(1)
509 def __repr__(self):
507 def __repr__(self):
510 return "<A instance>"
508 return "<A instance>"
511
509
512 _ip.user_ns["a"] = A()
510 _ip.user_ns["a"] = A()
513 _ip.run_cell("a")
511 _ip.run_cell("a")
514
512
515 nt.assert_equal(monitor, [])
513 nt.assert_equal(monitor, [])
516 _ip.magic("reset -f")
514 _ip.magic("reset -f")
517 nt.assert_equal(monitor, [1])
515 nt.assert_equal(monitor, [1])
518
516
519 class TestXdel(tt.TempFileMixin):
517 class TestXdel(tt.TempFileMixin):
520 def test_xdel(self):
518 def test_xdel(self):
521 """Test that references from %run are cleared by xdel."""
519 """Test that references from %run are cleared by xdel."""
522 src = ("class A(object):\n"
520 src = ("class A(object):\n"
523 " monitor = []\n"
521 " monitor = []\n"
524 " def __del__(self):\n"
522 " def __del__(self):\n"
525 " self.monitor.append(1)\n"
523 " self.monitor.append(1)\n"
526 "a = A()\n")
524 "a = A()\n")
527 self.mktmp(src)
525 self.mktmp(src)
528 # %run creates some hidden references...
526 # %run creates some hidden references...
529 _ip.magic("run %s" % self.fname)
527 _ip.magic("run %s" % self.fname)
530 # ... as does the displayhook.
528 # ... as does the displayhook.
531 _ip.run_cell("a")
529 _ip.run_cell("a")
532
530
533 monitor = _ip.user_ns["A"].monitor
531 monitor = _ip.user_ns["A"].monitor
534 nt.assert_equal(monitor, [])
532 nt.assert_equal(monitor, [])
535
533
536 _ip.magic("xdel a")
534 _ip.magic("xdel a")
537
535
538 # Check that a's __del__ method has been called.
536 # Check that a's __del__ method has been called.
539 nt.assert_equal(monitor, [1])
537 nt.assert_equal(monitor, [1])
540
538
541 def doctest_who():
539 def doctest_who():
542 """doctest for %who
540 """doctest for %who
543
541
544 In [1]: %reset -f
542 In [1]: %reset -f
545
543
546 In [2]: alpha = 123
544 In [2]: alpha = 123
547
545
548 In [3]: beta = 'beta'
546 In [3]: beta = 'beta'
549
547
550 In [4]: %who int
548 In [4]: %who int
551 alpha
549 alpha
552
550
553 In [5]: %who str
551 In [5]: %who str
554 beta
552 beta
555
553
556 In [6]: %whos
554 In [6]: %whos
557 Variable Type Data/Info
555 Variable Type Data/Info
558 ----------------------------
556 ----------------------------
559 alpha int 123
557 alpha int 123
560 beta str beta
558 beta str beta
561
559
562 In [7]: %who_ls
560 In [7]: %who_ls
563 Out[7]: ['alpha', 'beta']
561 Out[7]: ['alpha', 'beta']
564 """
562 """
565
563
566 def test_whos():
564 def test_whos():
567 """Check that whos is protected against objects where repr() fails."""
565 """Check that whos is protected against objects where repr() fails."""
568 class A(object):
566 class A(object):
569 def __repr__(self):
567 def __repr__(self):
570 raise Exception()
568 raise Exception()
571 _ip.user_ns['a'] = A()
569 _ip.user_ns['a'] = A()
572 _ip.magic("whos")
570 _ip.magic("whos")
573
571
574 def doctest_precision():
572 def doctest_precision():
575 """doctest for %precision
573 """doctest for %precision
576
574
577 In [1]: f = get_ipython().display_formatter.formatters['text/plain']
575 In [1]: f = get_ipython().display_formatter.formatters['text/plain']
578
576
579 In [2]: %precision 5
577 In [2]: %precision 5
580 Out[2]: '%.5f'
578 Out[2]: '%.5f'
581
579
582 In [3]: f.float_format
580 In [3]: f.float_format
583 Out[3]: '%.5f'
581 Out[3]: '%.5f'
584
582
585 In [4]: %precision %e
583 In [4]: %precision %e
586 Out[4]: '%e'
584 Out[4]: '%e'
587
585
588 In [5]: f(3.1415927)
586 In [5]: f(3.1415927)
589 Out[5]: '3.141593e+00'
587 Out[5]: '3.141593e+00'
590 """
588 """
591
589
592 def test_psearch():
590 def test_psearch():
593 with tt.AssertPrints("dict.fromkeys"):
591 with tt.AssertPrints("dict.fromkeys"):
594 _ip.run_cell("dict.fr*?")
592 _ip.run_cell("dict.fr*?")
595
593
596 def test_timeit_shlex():
594 def test_timeit_shlex():
597 """test shlex issues with timeit (#1109)"""
595 """test shlex issues with timeit (#1109)"""
598 _ip.ex("def f(*a,**kw): pass")
596 _ip.ex("def f(*a,**kw): pass")
599 _ip.magic('timeit -n1 "this is a bug".count(" ")')
597 _ip.magic('timeit -n1 "this is a bug".count(" ")')
600 _ip.magic('timeit -r1 -n1 f(" ", 1)')
598 _ip.magic('timeit -r1 -n1 f(" ", 1)')
601 _ip.magic('timeit -r1 -n1 f(" ", 1, " ", 2, " ")')
599 _ip.magic('timeit -r1 -n1 f(" ", 1, " ", 2, " ")')
602 _ip.magic('timeit -r1 -n1 ("a " + "b")')
600 _ip.magic('timeit -r1 -n1 ("a " + "b")')
603 _ip.magic('timeit -r1 -n1 f("a " + "b")')
601 _ip.magic('timeit -r1 -n1 f("a " + "b")')
604 _ip.magic('timeit -r1 -n1 f("a " + "b ")')
602 _ip.magic('timeit -r1 -n1 f("a " + "b ")')
605
603
606
604
607 def test_timeit_special_syntax():
605 def test_timeit_special_syntax():
608 "Test %%timeit with IPython special syntax"
606 "Test %%timeit with IPython special syntax"
609 @register_line_magic
607 @register_line_magic
610 def lmagic(line):
608 def lmagic(line):
611 ip = get_ipython()
609 ip = get_ipython()
612 ip.user_ns['lmagic_out'] = line
610 ip.user_ns['lmagic_out'] = line
613
611
614 # line mode test
612 # line mode test
615 _ip.run_line_magic('timeit', '-n1 -r1 %lmagic my line')
613 _ip.run_line_magic('timeit', '-n1 -r1 %lmagic my line')
616 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line')
614 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line')
617 # cell mode test
615 # cell mode test
618 _ip.run_cell_magic('timeit', '-n1 -r1', '%lmagic my line2')
616 _ip.run_cell_magic('timeit', '-n1 -r1', '%lmagic my line2')
619 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2')
617 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2')
620
618
621 def test_timeit_return():
619 def test_timeit_return():
622 """
620 """
623 test whether timeit -o return object
621 test whether timeit -o return object
624 """
622 """
625
623
626 res = _ip.run_line_magic('timeit','-n10 -r10 -o 1')
624 res = _ip.run_line_magic('timeit','-n10 -r10 -o 1')
627 assert(res is not None)
625 assert(res is not None)
628
626
629 def test_timeit_quiet():
627 def test_timeit_quiet():
630 """
628 """
631 test quiet option of timeit magic
629 test quiet option of timeit magic
632 """
630 """
633 with tt.AssertNotPrints("loops"):
631 with tt.AssertNotPrints("loops"):
634 _ip.run_cell("%timeit -n1 -r1 -q 1")
632 _ip.run_cell("%timeit -n1 -r1 -q 1")
635
633
636 def test_timeit_return_quiet():
634 def test_timeit_return_quiet():
637 with tt.AssertNotPrints("loops"):
635 with tt.AssertNotPrints("loops"):
638 res = _ip.run_line_magic('timeit', '-n1 -r1 -q -o 1')
636 res = _ip.run_line_magic('timeit', '-n1 -r1 -q -o 1')
639 assert (res is not None)
637 assert (res is not None)
640
638
641 def test_timeit_invalid_return():
639 def test_timeit_invalid_return():
642 with nt.assert_raises_regex(SyntaxError, "outside function"):
640 with nt.assert_raises_regex(SyntaxError, "outside function"):
643 _ip.run_line_magic('timeit', 'return')
641 _ip.run_line_magic('timeit', 'return')
644
642
645 @dec.skipif(execution.profile is None)
643 @dec.skipif(execution.profile is None)
646 def test_prun_special_syntax():
644 def test_prun_special_syntax():
647 "Test %%prun with IPython special syntax"
645 "Test %%prun with IPython special syntax"
648 @register_line_magic
646 @register_line_magic
649 def lmagic(line):
647 def lmagic(line):
650 ip = get_ipython()
648 ip = get_ipython()
651 ip.user_ns['lmagic_out'] = line
649 ip.user_ns['lmagic_out'] = line
652
650
653 # line mode test
651 # line mode test
654 _ip.run_line_magic('prun', '-q %lmagic my line')
652 _ip.run_line_magic('prun', '-q %lmagic my line')
655 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line')
653 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line')
656 # cell mode test
654 # cell mode test
657 _ip.run_cell_magic('prun', '-q', '%lmagic my line2')
655 _ip.run_cell_magic('prun', '-q', '%lmagic my line2')
658 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2')
656 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2')
659
657
660 @dec.skipif(execution.profile is None)
658 @dec.skipif(execution.profile is None)
661 def test_prun_quotes():
659 def test_prun_quotes():
662 "Test that prun does not clobber string escapes (GH #1302)"
660 "Test that prun does not clobber string escapes (GH #1302)"
663 _ip.magic(r"prun -q x = '\t'")
661 _ip.magic(r"prun -q x = '\t'")
664 nt.assert_equal(_ip.user_ns['x'], '\t')
662 nt.assert_equal(_ip.user_ns['x'], '\t')
665
663
666 def test_extension():
664 def test_extension():
667 # Debugging information for failures of this test
665 # Debugging information for failures of this test
668 print('sys.path:')
666 print('sys.path:')
669 for p in sys.path:
667 for p in sys.path:
670 print(' ', p)
668 print(' ', p)
671 print('CWD', os.getcwd())
669 print('CWD', os.getcwd())
672
670
673 nt.assert_raises(ImportError, _ip.magic, "load_ext daft_extension")
671 nt.assert_raises(ImportError, _ip.magic, "load_ext daft_extension")
674 daft_path = os.path.join(os.path.dirname(__file__), "daft_extension")
672 daft_path = os.path.join(os.path.dirname(__file__), "daft_extension")
675 sys.path.insert(0, daft_path)
673 sys.path.insert(0, daft_path)
676 try:
674 try:
677 _ip.user_ns.pop('arq', None)
675 _ip.user_ns.pop('arq', None)
678 invalidate_caches() # Clear import caches
676 invalidate_caches() # Clear import caches
679 _ip.magic("load_ext daft_extension")
677 _ip.magic("load_ext daft_extension")
680 nt.assert_equal(_ip.user_ns['arq'], 185)
678 nt.assert_equal(_ip.user_ns['arq'], 185)
681 _ip.magic("unload_ext daft_extension")
679 _ip.magic("unload_ext daft_extension")
682 assert 'arq' not in _ip.user_ns
680 assert 'arq' not in _ip.user_ns
683 finally:
681 finally:
684 sys.path.remove(daft_path)
682 sys.path.remove(daft_path)
685
683
686
684
687 def test_notebook_export_json():
685 def test_notebook_export_json():
688 _ip = get_ipython()
686 _ip = get_ipython()
689 _ip.history_manager.reset() # Clear any existing history.
687 _ip.history_manager.reset() # Clear any existing history.
690 cmds = [u"a=1", u"def b():\n return a**2", u"print('noΓ«l, Γ©tΓ©', b())"]
688 cmds = [u"a=1", u"def b():\n return a**2", u"print('noΓ«l, Γ©tΓ©', b())"]
691 for i, cmd in enumerate(cmds, start=1):
689 for i, cmd in enumerate(cmds, start=1):
692 _ip.history_manager.store_inputs(i, cmd)
690 _ip.history_manager.store_inputs(i, cmd)
693 with TemporaryDirectory() as td:
691 with TemporaryDirectory() as td:
694 outfile = os.path.join(td, "nb.ipynb")
692 outfile = os.path.join(td, "nb.ipynb")
695 _ip.magic("notebook -e %s" % outfile)
693 _ip.magic("notebook -e %s" % outfile)
696
694
697
695
698 class TestEnv(TestCase):
696 class TestEnv(TestCase):
699
697
700 def test_env(self):
698 def test_env(self):
701 env = _ip.magic("env")
699 env = _ip.magic("env")
702 self.assertTrue(isinstance(env, dict))
700 self.assertTrue(isinstance(env, dict))
703
701
704 def test_env_get_set_simple(self):
702 def test_env_get_set_simple(self):
705 env = _ip.magic("env var val1")
703 env = _ip.magic("env var val1")
706 self.assertEqual(env, None)
704 self.assertEqual(env, None)
707 self.assertEqual(os.environ['var'], 'val1')
705 self.assertEqual(os.environ['var'], 'val1')
708 self.assertEqual(_ip.magic("env var"), 'val1')
706 self.assertEqual(_ip.magic("env var"), 'val1')
709 env = _ip.magic("env var=val2")
707 env = _ip.magic("env var=val2")
710 self.assertEqual(env, None)
708 self.assertEqual(env, None)
711 self.assertEqual(os.environ['var'], 'val2')
709 self.assertEqual(os.environ['var'], 'val2')
712
710
713 def test_env_get_set_complex(self):
711 def test_env_get_set_complex(self):
714 env = _ip.magic("env var 'val1 '' 'val2")
712 env = _ip.magic("env var 'val1 '' 'val2")
715 self.assertEqual(env, None)
713 self.assertEqual(env, None)
716 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
714 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
717 self.assertEqual(_ip.magic("env var"), "'val1 '' 'val2")
715 self.assertEqual(_ip.magic("env var"), "'val1 '' 'val2")
718 env = _ip.magic('env var=val2 val3="val4')
716 env = _ip.magic('env var=val2 val3="val4')
719 self.assertEqual(env, None)
717 self.assertEqual(env, None)
720 self.assertEqual(os.environ['var'], 'val2 val3="val4')
718 self.assertEqual(os.environ['var'], 'val2 val3="val4')
721
719
722 def test_env_set_bad_input(self):
720 def test_env_set_bad_input(self):
723 self.assertRaises(UsageError, lambda: _ip.magic("set_env var"))
721 self.assertRaises(UsageError, lambda: _ip.magic("set_env var"))
724
722
725 def test_env_set_whitespace(self):
723 def test_env_set_whitespace(self):
726 self.assertRaises(UsageError, lambda: _ip.magic("env var A=B"))
724 self.assertRaises(UsageError, lambda: _ip.magic("env var A=B"))
727
725
728
726
729 class CellMagicTestCase(TestCase):
727 class CellMagicTestCase(TestCase):
730
728
731 def check_ident(self, magic):
729 def check_ident(self, magic):
732 # Manually called, we get the result
730 # Manually called, we get the result
733 out = _ip.run_cell_magic(magic, 'a', 'b')
731 out = _ip.run_cell_magic(magic, 'a', 'b')
734 nt.assert_equal(out, ('a','b'))
732 nt.assert_equal(out, ('a','b'))
735 # Via run_cell, it goes into the user's namespace via displayhook
733 # Via run_cell, it goes into the user's namespace via displayhook
736 _ip.run_cell('%%' + magic +' c\nd\n')
734 _ip.run_cell('%%' + magic +' c\nd\n')
737 nt.assert_equal(_ip.user_ns['_'], ('c','d\n'))
735 nt.assert_equal(_ip.user_ns['_'], ('c','d\n'))
738
736
739 def test_cell_magic_func_deco(self):
737 def test_cell_magic_func_deco(self):
740 "Cell magic using simple decorator"
738 "Cell magic using simple decorator"
741 @register_cell_magic
739 @register_cell_magic
742 def cellm(line, cell):
740 def cellm(line, cell):
743 return line, cell
741 return line, cell
744
742
745 self.check_ident('cellm')
743 self.check_ident('cellm')
746
744
747 def test_cell_magic_reg(self):
745 def test_cell_magic_reg(self):
748 "Cell magic manually registered"
746 "Cell magic manually registered"
749 def cellm(line, cell):
747 def cellm(line, cell):
750 return line, cell
748 return line, cell
751
749
752 _ip.register_magic_function(cellm, 'cell', 'cellm2')
750 _ip.register_magic_function(cellm, 'cell', 'cellm2')
753 self.check_ident('cellm2')
751 self.check_ident('cellm2')
754
752
755 def test_cell_magic_class(self):
753 def test_cell_magic_class(self):
756 "Cell magics declared via a class"
754 "Cell magics declared via a class"
757 @magics_class
755 @magics_class
758 class MyMagics(Magics):
756 class MyMagics(Magics):
759
757
760 @cell_magic
758 @cell_magic
761 def cellm3(self, line, cell):
759 def cellm3(self, line, cell):
762 return line, cell
760 return line, cell
763
761
764 _ip.register_magics(MyMagics)
762 _ip.register_magics(MyMagics)
765 self.check_ident('cellm3')
763 self.check_ident('cellm3')
766
764
767 def test_cell_magic_class2(self):
765 def test_cell_magic_class2(self):
768 "Cell magics declared via a class, #2"
766 "Cell magics declared via a class, #2"
769 @magics_class
767 @magics_class
770 class MyMagics2(Magics):
768 class MyMagics2(Magics):
771
769
772 @cell_magic('cellm4')
770 @cell_magic('cellm4')
773 def cellm33(self, line, cell):
771 def cellm33(self, line, cell):
774 return line, cell
772 return line, cell
775
773
776 _ip.register_magics(MyMagics2)
774 _ip.register_magics(MyMagics2)
777 self.check_ident('cellm4')
775 self.check_ident('cellm4')
778 # Check that nothing is registered as 'cellm33'
776 # Check that nothing is registered as 'cellm33'
779 c33 = _ip.find_cell_magic('cellm33')
777 c33 = _ip.find_cell_magic('cellm33')
780 nt.assert_equal(c33, None)
778 nt.assert_equal(c33, None)
781
779
782 def test_file():
780 def test_file():
783 """Basic %%writefile"""
781 """Basic %%writefile"""
784 ip = get_ipython()
782 ip = get_ipython()
785 with TemporaryDirectory() as td:
783 with TemporaryDirectory() as td:
786 fname = os.path.join(td, 'file1')
784 fname = os.path.join(td, 'file1')
787 ip.run_cell_magic("writefile", fname, u'\n'.join([
785 ip.run_cell_magic("writefile", fname, u'\n'.join([
788 'line1',
786 'line1',
789 'line2',
787 'line2',
790 ]))
788 ]))
791 with open(fname) as f:
789 with open(fname) as f:
792 s = f.read()
790 s = f.read()
793 nt.assert_in('line1\n', s)
791 nt.assert_in('line1\n', s)
794 nt.assert_in('line2', s)
792 nt.assert_in('line2', s)
795
793
796 @dec.skip_win32
794 @dec.skip_win32
797 def test_file_single_quote():
795 def test_file_single_quote():
798 """Basic %%writefile with embedded single quotes"""
796 """Basic %%writefile with embedded single quotes"""
799 ip = get_ipython()
797 ip = get_ipython()
800 with TemporaryDirectory() as td:
798 with TemporaryDirectory() as td:
801 fname = os.path.join(td, '\'file1\'')
799 fname = os.path.join(td, '\'file1\'')
802 ip.run_cell_magic("writefile", fname, u'\n'.join([
800 ip.run_cell_magic("writefile", fname, u'\n'.join([
803 'line1',
801 'line1',
804 'line2',
802 'line2',
805 ]))
803 ]))
806 with open(fname) as f:
804 with open(fname) as f:
807 s = f.read()
805 s = f.read()
808 nt.assert_in('line1\n', s)
806 nt.assert_in('line1\n', s)
809 nt.assert_in('line2', s)
807 nt.assert_in('line2', s)
810
808
811 @dec.skip_win32
809 @dec.skip_win32
812 def test_file_double_quote():
810 def test_file_double_quote():
813 """Basic %%writefile with embedded double quotes"""
811 """Basic %%writefile with embedded double quotes"""
814 ip = get_ipython()
812 ip = get_ipython()
815 with TemporaryDirectory() as td:
813 with TemporaryDirectory() as td:
816 fname = os.path.join(td, '"file1"')
814 fname = os.path.join(td, '"file1"')
817 ip.run_cell_magic("writefile", fname, u'\n'.join([
815 ip.run_cell_magic("writefile", fname, u'\n'.join([
818 'line1',
816 'line1',
819 'line2',
817 'line2',
820 ]))
818 ]))
821 with open(fname) as f:
819 with open(fname) as f:
822 s = f.read()
820 s = f.read()
823 nt.assert_in('line1\n', s)
821 nt.assert_in('line1\n', s)
824 nt.assert_in('line2', s)
822 nt.assert_in('line2', s)
825
823
826 def test_file_var_expand():
824 def test_file_var_expand():
827 """%%writefile $filename"""
825 """%%writefile $filename"""
828 ip = get_ipython()
826 ip = get_ipython()
829 with TemporaryDirectory() as td:
827 with TemporaryDirectory() as td:
830 fname = os.path.join(td, 'file1')
828 fname = os.path.join(td, 'file1')
831 ip.user_ns['filename'] = fname
829 ip.user_ns['filename'] = fname
832 ip.run_cell_magic("writefile", '$filename', u'\n'.join([
830 ip.run_cell_magic("writefile", '$filename', u'\n'.join([
833 'line1',
831 'line1',
834 'line2',
832 'line2',
835 ]))
833 ]))
836 with open(fname) as f:
834 with open(fname) as f:
837 s = f.read()
835 s = f.read()
838 nt.assert_in('line1\n', s)
836 nt.assert_in('line1\n', s)
839 nt.assert_in('line2', s)
837 nt.assert_in('line2', s)
840
838
841 def test_file_unicode():
839 def test_file_unicode():
842 """%%writefile with unicode cell"""
840 """%%writefile with unicode cell"""
843 ip = get_ipython()
841 ip = get_ipython()
844 with TemporaryDirectory() as td:
842 with TemporaryDirectory() as td:
845 fname = os.path.join(td, 'file1')
843 fname = os.path.join(td, 'file1')
846 ip.run_cell_magic("writefile", fname, u'\n'.join([
844 ip.run_cell_magic("writefile", fname, u'\n'.join([
847 u'linΓ©1',
845 u'linΓ©1',
848 u'linΓ©2',
846 u'linΓ©2',
849 ]))
847 ]))
850 with io.open(fname, encoding='utf-8') as f:
848 with io.open(fname, encoding='utf-8') as f:
851 s = f.read()
849 s = f.read()
852 nt.assert_in(u'linΓ©1\n', s)
850 nt.assert_in(u'linΓ©1\n', s)
853 nt.assert_in(u'linΓ©2', s)
851 nt.assert_in(u'linΓ©2', s)
854
852
855 def test_file_amend():
853 def test_file_amend():
856 """%%writefile -a amends files"""
854 """%%writefile -a amends files"""
857 ip = get_ipython()
855 ip = get_ipython()
858 with TemporaryDirectory() as td:
856 with TemporaryDirectory() as td:
859 fname = os.path.join(td, 'file2')
857 fname = os.path.join(td, 'file2')
860 ip.run_cell_magic("writefile", fname, u'\n'.join([
858 ip.run_cell_magic("writefile", fname, u'\n'.join([
861 'line1',
859 'line1',
862 'line2',
860 'line2',
863 ]))
861 ]))
864 ip.run_cell_magic("writefile", "-a %s" % fname, u'\n'.join([
862 ip.run_cell_magic("writefile", "-a %s" % fname, u'\n'.join([
865 'line3',
863 'line3',
866 'line4',
864 'line4',
867 ]))
865 ]))
868 with open(fname) as f:
866 with open(fname) as f:
869 s = f.read()
867 s = f.read()
870 nt.assert_in('line1\n', s)
868 nt.assert_in('line1\n', s)
871 nt.assert_in('line3\n', s)
869 nt.assert_in('line3\n', s)
872
870
873 def test_file_spaces():
871 def test_file_spaces():
874 """%%file with spaces in filename"""
872 """%%file with spaces in filename"""
875 ip = get_ipython()
873 ip = get_ipython()
876 with TemporaryWorkingDirectory() as td:
874 with TemporaryWorkingDirectory() as td:
877 fname = "file name"
875 fname = "file name"
878 ip.run_cell_magic("file", '"%s"'%fname, u'\n'.join([
876 ip.run_cell_magic("file", '"%s"'%fname, u'\n'.join([
879 'line1',
877 'line1',
880 'line2',
878 'line2',
881 ]))
879 ]))
882 with open(fname) as f:
880 with open(fname) as f:
883 s = f.read()
881 s = f.read()
884 nt.assert_in('line1\n', s)
882 nt.assert_in('line1\n', s)
885 nt.assert_in('line2', s)
883 nt.assert_in('line2', s)
886
884
887 def test_script_config():
885 def test_script_config():
888 ip = get_ipython()
886 ip = get_ipython()
889 ip.config.ScriptMagics.script_magics = ['whoda']
887 ip.config.ScriptMagics.script_magics = ['whoda']
890 sm = script.ScriptMagics(shell=ip)
888 sm = script.ScriptMagics(shell=ip)
891 nt.assert_in('whoda', sm.magics['cell'])
889 nt.assert_in('whoda', sm.magics['cell'])
892
890
893 @dec.skip_win32
891 @dec.skip_win32
894 def test_script_out():
892 def test_script_out():
895 ip = get_ipython()
893 ip = get_ipython()
896 ip.run_cell_magic("script", "--out output sh", "echo 'hi'")
894 ip.run_cell_magic("script", "--out output sh", "echo 'hi'")
897 nt.assert_equal(ip.user_ns['output'], 'hi\n')
895 nt.assert_equal(ip.user_ns['output'], 'hi\n')
898
896
899 @dec.skip_win32
897 @dec.skip_win32
900 def test_script_err():
898 def test_script_err():
901 ip = get_ipython()
899 ip = get_ipython()
902 ip.run_cell_magic("script", "--err error sh", "echo 'hello' >&2")
900 ip.run_cell_magic("script", "--err error sh", "echo 'hello' >&2")
903 nt.assert_equal(ip.user_ns['error'], 'hello\n')
901 nt.assert_equal(ip.user_ns['error'], 'hello\n')
904
902
905 @dec.skip_win32
903 @dec.skip_win32
906 def test_script_out_err():
904 def test_script_out_err():
907 ip = get_ipython()
905 ip = get_ipython()
908 ip.run_cell_magic("script", "--out output --err error sh", "echo 'hi'\necho 'hello' >&2")
906 ip.run_cell_magic("script", "--out output --err error sh", "echo 'hi'\necho 'hello' >&2")
909 nt.assert_equal(ip.user_ns['output'], 'hi\n')
907 nt.assert_equal(ip.user_ns['output'], 'hi\n')
910 nt.assert_equal(ip.user_ns['error'], 'hello\n')
908 nt.assert_equal(ip.user_ns['error'], 'hello\n')
911
909
912 @dec.skip_win32
910 @dec.skip_win32
913 def test_script_bg_out():
911 def test_script_bg_out():
914 ip = get_ipython()
912 ip = get_ipython()
915 ip.run_cell_magic("script", "--bg --out output sh", "echo 'hi'")
913 ip.run_cell_magic("script", "--bg --out output sh", "echo 'hi'")
916
914
917 nt.assert_equal(ip.user_ns['output'].read(), b'hi\n')
915 nt.assert_equal(ip.user_ns['output'].read(), b'hi\n')
918 ip.user_ns['output'].close()
916 ip.user_ns['output'].close()
919
917
920 @dec.skip_win32
918 @dec.skip_win32
921 def test_script_bg_err():
919 def test_script_bg_err():
922 ip = get_ipython()
920 ip = get_ipython()
923 ip.run_cell_magic("script", "--bg --err error sh", "echo 'hello' >&2")
921 ip.run_cell_magic("script", "--bg --err error sh", "echo 'hello' >&2")
924 nt.assert_equal(ip.user_ns['error'].read(), b'hello\n')
922 nt.assert_equal(ip.user_ns['error'].read(), b'hello\n')
925 ip.user_ns['error'].close()
923 ip.user_ns['error'].close()
926
924
927 @dec.skip_win32
925 @dec.skip_win32
928 def test_script_bg_out_err():
926 def test_script_bg_out_err():
929 ip = get_ipython()
927 ip = get_ipython()
930 ip.run_cell_magic("script", "--bg --out output --err error sh", "echo 'hi'\necho 'hello' >&2")
928 ip.run_cell_magic("script", "--bg --out output --err error sh", "echo 'hi'\necho 'hello' >&2")
931 nt.assert_equal(ip.user_ns['output'].read(), b'hi\n')
929 nt.assert_equal(ip.user_ns['output'].read(), b'hi\n')
932 nt.assert_equal(ip.user_ns['error'].read(), b'hello\n')
930 nt.assert_equal(ip.user_ns['error'].read(), b'hello\n')
933 ip.user_ns['output'].close()
931 ip.user_ns['output'].close()
934 ip.user_ns['error'].close()
932 ip.user_ns['error'].close()
935
933
936 def test_script_defaults():
934 def test_script_defaults():
937 ip = get_ipython()
935 ip = get_ipython()
938 for cmd in ['sh', 'bash', 'perl', 'ruby']:
936 for cmd in ['sh', 'bash', 'perl', 'ruby']:
939 try:
937 try:
940 find_cmd(cmd)
938 find_cmd(cmd)
941 except Exception:
939 except Exception:
942 pass
940 pass
943 else:
941 else:
944 nt.assert_in(cmd, ip.magics_manager.magics['cell'])
942 nt.assert_in(cmd, ip.magics_manager.magics['cell'])
945
943
946
944
947 @magics_class
945 @magics_class
948 class FooFoo(Magics):
946 class FooFoo(Magics):
949 """class with both %foo and %%foo magics"""
947 """class with both %foo and %%foo magics"""
950 @line_magic('foo')
948 @line_magic('foo')
951 def line_foo(self, line):
949 def line_foo(self, line):
952 "I am line foo"
950 "I am line foo"
953 pass
951 pass
954
952
955 @cell_magic("foo")
953 @cell_magic("foo")
956 def cell_foo(self, line, cell):
954 def cell_foo(self, line, cell):
957 "I am cell foo, not line foo"
955 "I am cell foo, not line foo"
958 pass
956 pass
959
957
960 def test_line_cell_info():
958 def test_line_cell_info():
961 """%%foo and %foo magics are distinguishable to inspect"""
959 """%%foo and %foo magics are distinguishable to inspect"""
962 ip = get_ipython()
960 ip = get_ipython()
963 ip.magics_manager.register(FooFoo)
961 ip.magics_manager.register(FooFoo)
964 oinfo = ip.object_inspect('foo')
962 oinfo = ip.object_inspect('foo')
965 nt.assert_true(oinfo['found'])
963 nt.assert_true(oinfo['found'])
966 nt.assert_true(oinfo['ismagic'])
964 nt.assert_true(oinfo['ismagic'])
967
965
968 oinfo = ip.object_inspect('%%foo')
966 oinfo = ip.object_inspect('%%foo')
969 nt.assert_true(oinfo['found'])
967 nt.assert_true(oinfo['found'])
970 nt.assert_true(oinfo['ismagic'])
968 nt.assert_true(oinfo['ismagic'])
971 nt.assert_equal(oinfo['docstring'], FooFoo.cell_foo.__doc__)
969 nt.assert_equal(oinfo['docstring'], FooFoo.cell_foo.__doc__)
972
970
973 oinfo = ip.object_inspect('%foo')
971 oinfo = ip.object_inspect('%foo')
974 nt.assert_true(oinfo['found'])
972 nt.assert_true(oinfo['found'])
975 nt.assert_true(oinfo['ismagic'])
973 nt.assert_true(oinfo['ismagic'])
976 nt.assert_equal(oinfo['docstring'], FooFoo.line_foo.__doc__)
974 nt.assert_equal(oinfo['docstring'], FooFoo.line_foo.__doc__)
977
975
978 def test_multiple_magics():
976 def test_multiple_magics():
979 ip = get_ipython()
977 ip = get_ipython()
980 foo1 = FooFoo(ip)
978 foo1 = FooFoo(ip)
981 foo2 = FooFoo(ip)
979 foo2 = FooFoo(ip)
982 mm = ip.magics_manager
980 mm = ip.magics_manager
983 mm.register(foo1)
981 mm.register(foo1)
984 nt.assert_true(mm.magics['line']['foo'].__self__ is foo1)
982 nt.assert_true(mm.magics['line']['foo'].__self__ is foo1)
985 mm.register(foo2)
983 mm.register(foo2)
986 nt.assert_true(mm.magics['line']['foo'].__self__ is foo2)
984 nt.assert_true(mm.magics['line']['foo'].__self__ is foo2)
987
985
988 def test_alias_magic():
986 def test_alias_magic():
989 """Test %alias_magic."""
987 """Test %alias_magic."""
990 ip = get_ipython()
988 ip = get_ipython()
991 mm = ip.magics_manager
989 mm = ip.magics_manager
992
990
993 # Basic operation: both cell and line magics are created, if possible.
991 # Basic operation: both cell and line magics are created, if possible.
994 ip.run_line_magic('alias_magic', 'timeit_alias timeit')
992 ip.run_line_magic('alias_magic', 'timeit_alias timeit')
995 nt.assert_in('timeit_alias', mm.magics['line'])
993 nt.assert_in('timeit_alias', mm.magics['line'])
996 nt.assert_in('timeit_alias', mm.magics['cell'])
994 nt.assert_in('timeit_alias', mm.magics['cell'])
997
995
998 # --cell is specified, line magic not created.
996 # --cell is specified, line magic not created.
999 ip.run_line_magic('alias_magic', '--cell timeit_cell_alias timeit')
997 ip.run_line_magic('alias_magic', '--cell timeit_cell_alias timeit')
1000 nt.assert_not_in('timeit_cell_alias', mm.magics['line'])
998 nt.assert_not_in('timeit_cell_alias', mm.magics['line'])
1001 nt.assert_in('timeit_cell_alias', mm.magics['cell'])
999 nt.assert_in('timeit_cell_alias', mm.magics['cell'])
1002
1000
1003 # Test that line alias is created successfully.
1001 # Test that line alias is created successfully.
1004 ip.run_line_magic('alias_magic', '--line env_alias env')
1002 ip.run_line_magic('alias_magic', '--line env_alias env')
1005 nt.assert_equal(ip.run_line_magic('env', ''),
1003 nt.assert_equal(ip.run_line_magic('env', ''),
1006 ip.run_line_magic('env_alias', ''))
1004 ip.run_line_magic('env_alias', ''))
1007
1005
1008 # Test that line alias with parameters passed in is created successfully.
1006 # Test that line alias with parameters passed in is created successfully.
1009 ip.run_line_magic('alias_magic', '--line history_alias history --params ' + shlex.quote('3'))
1007 ip.run_line_magic('alias_magic', '--line history_alias history --params ' + shlex.quote('3'))
1010 nt.assert_in('history_alias', mm.magics['line'])
1008 nt.assert_in('history_alias', mm.magics['line'])
1011
1009
1012
1010
1013 def test_save():
1011 def test_save():
1014 """Test %save."""
1012 """Test %save."""
1015 ip = get_ipython()
1013 ip = get_ipython()
1016 ip.history_manager.reset() # Clear any existing history.
1014 ip.history_manager.reset() # Clear any existing history.
1017 cmds = [u"a=1", u"def b():\n return a**2", u"print(a, b())"]
1015 cmds = [u"a=1", u"def b():\n return a**2", u"print(a, b())"]
1018 for i, cmd in enumerate(cmds, start=1):
1016 for i, cmd in enumerate(cmds, start=1):
1019 ip.history_manager.store_inputs(i, cmd)
1017 ip.history_manager.store_inputs(i, cmd)
1020 with TemporaryDirectory() as tmpdir:
1018 with TemporaryDirectory() as tmpdir:
1021 file = os.path.join(tmpdir, "testsave.py")
1019 file = os.path.join(tmpdir, "testsave.py")
1022 ip.run_line_magic("save", "%s 1-10" % file)
1020 ip.run_line_magic("save", "%s 1-10" % file)
1023 with open(file) as f:
1021 with open(file) as f:
1024 content = f.read()
1022 content = f.read()
1025 nt.assert_equal(content.count(cmds[0]), 1)
1023 nt.assert_equal(content.count(cmds[0]), 1)
1026 nt.assert_in('coding: utf-8', content)
1024 nt.assert_in('coding: utf-8', content)
1027 ip.run_line_magic("save", "-a %s 1-10" % file)
1025 ip.run_line_magic("save", "-a %s 1-10" % file)
1028 with open(file) as f:
1026 with open(file) as f:
1029 content = f.read()
1027 content = f.read()
1030 nt.assert_equal(content.count(cmds[0]), 2)
1028 nt.assert_equal(content.count(cmds[0]), 2)
1031 nt.assert_in('coding: utf-8', content)
1029 nt.assert_in('coding: utf-8', content)
1032
1030
1033
1031
1034 def test_store():
1032 def test_store():
1035 """Test %store."""
1033 """Test %store."""
1036 ip = get_ipython()
1034 ip = get_ipython()
1037 ip.run_line_magic('load_ext', 'storemagic')
1035 ip.run_line_magic('load_ext', 'storemagic')
1038
1036
1039 # make sure the storage is empty
1037 # make sure the storage is empty
1040 ip.run_line_magic('store', '-z')
1038 ip.run_line_magic('store', '-z')
1041 ip.user_ns['var'] = 42
1039 ip.user_ns['var'] = 42
1042 ip.run_line_magic('store', 'var')
1040 ip.run_line_magic('store', 'var')
1043 ip.user_ns['var'] = 39
1041 ip.user_ns['var'] = 39
1044 ip.run_line_magic('store', '-r')
1042 ip.run_line_magic('store', '-r')
1045 nt.assert_equal(ip.user_ns['var'], 42)
1043 nt.assert_equal(ip.user_ns['var'], 42)
1046
1044
1047 ip.run_line_magic('store', '-d var')
1045 ip.run_line_magic('store', '-d var')
1048 ip.user_ns['var'] = 39
1046 ip.user_ns['var'] = 39
1049 ip.run_line_magic('store' , '-r')
1047 ip.run_line_magic('store' , '-r')
1050 nt.assert_equal(ip.user_ns['var'], 39)
1048 nt.assert_equal(ip.user_ns['var'], 39)
1051
1049
1052
1050
1053 def _run_edit_test(arg_s, exp_filename=None,
1051 def _run_edit_test(arg_s, exp_filename=None,
1054 exp_lineno=-1,
1052 exp_lineno=-1,
1055 exp_contents=None,
1053 exp_contents=None,
1056 exp_is_temp=None):
1054 exp_is_temp=None):
1057 ip = get_ipython()
1055 ip = get_ipython()
1058 M = code.CodeMagics(ip)
1056 M = code.CodeMagics(ip)
1059 last_call = ['','']
1057 last_call = ['','']
1060 opts,args = M.parse_options(arg_s,'prxn:')
1058 opts,args = M.parse_options(arg_s,'prxn:')
1061 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
1059 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
1062
1060
1063 if exp_filename is not None:
1061 if exp_filename is not None:
1064 nt.assert_equal(exp_filename, filename)
1062 nt.assert_equal(exp_filename, filename)
1065 if exp_contents is not None:
1063 if exp_contents is not None:
1066 with io.open(filename, 'r', encoding='utf-8') as f:
1064 with io.open(filename, 'r', encoding='utf-8') as f:
1067 contents = f.read()
1065 contents = f.read()
1068 nt.assert_equal(exp_contents, contents)
1066 nt.assert_equal(exp_contents, contents)
1069 if exp_lineno != -1:
1067 if exp_lineno != -1:
1070 nt.assert_equal(exp_lineno, lineno)
1068 nt.assert_equal(exp_lineno, lineno)
1071 if exp_is_temp is not None:
1069 if exp_is_temp is not None:
1072 nt.assert_equal(exp_is_temp, is_temp)
1070 nt.assert_equal(exp_is_temp, is_temp)
1073
1071
1074
1072
1075 def test_edit_interactive():
1073 def test_edit_interactive():
1076 """%edit on interactively defined objects"""
1074 """%edit on interactively defined objects"""
1077 ip = get_ipython()
1075 ip = get_ipython()
1078 n = ip.execution_count
1076 n = ip.execution_count
1079 ip.run_cell(u"def foo(): return 1", store_history=True)
1077 ip.run_cell(u"def foo(): return 1", store_history=True)
1080
1078
1081 try:
1079 try:
1082 _run_edit_test("foo")
1080 _run_edit_test("foo")
1083 except code.InteractivelyDefined as e:
1081 except code.InteractivelyDefined as e:
1084 nt.assert_equal(e.index, n)
1082 nt.assert_equal(e.index, n)
1085 else:
1083 else:
1086 raise AssertionError("Should have raised InteractivelyDefined")
1084 raise AssertionError("Should have raised InteractivelyDefined")
1087
1085
1088
1086
1089 def test_edit_cell():
1087 def test_edit_cell():
1090 """%edit [cell id]"""
1088 """%edit [cell id]"""
1091 ip = get_ipython()
1089 ip = get_ipython()
1092
1090
1093 ip.run_cell(u"def foo(): return 1", store_history=True)
1091 ip.run_cell(u"def foo(): return 1", store_history=True)
1094
1092
1095 # test
1093 # test
1096 _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)
1094 _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)
1097
1095
1098 def test_bookmark():
1096 def test_bookmark():
1099 ip = get_ipython()
1097 ip = get_ipython()
1100 ip.run_line_magic('bookmark', 'bmname')
1098 ip.run_line_magic('bookmark', 'bmname')
1101 with tt.AssertPrints('bmname'):
1099 with tt.AssertPrints('bmname'):
1102 ip.run_line_magic('bookmark', '-l')
1100 ip.run_line_magic('bookmark', '-l')
1103 ip.run_line_magic('bookmark', '-d bmname')
1101 ip.run_line_magic('bookmark', '-d bmname')
1104
1102
1105 def test_ls_magic():
1103 def test_ls_magic():
1106 ip = get_ipython()
1104 ip = get_ipython()
1107 json_formatter = ip.display_formatter.formatters['application/json']
1105 json_formatter = ip.display_formatter.formatters['application/json']
1108 json_formatter.enabled = True
1106 json_formatter.enabled = True
1109 lsmagic = ip.magic('lsmagic')
1107 lsmagic = ip.magic('lsmagic')
1110 with warnings.catch_warnings(record=True) as w:
1108 with warnings.catch_warnings(record=True) as w:
1111 j = json_formatter(lsmagic)
1109 j = json_formatter(lsmagic)
1112 nt.assert_equal(sorted(j), ['cell', 'line'])
1110 nt.assert_equal(sorted(j), ['cell', 'line'])
1113 nt.assert_equal(w, []) # no warnings
1111 nt.assert_equal(w, []) # no warnings
1114
1112
1115 def test_strip_initial_indent():
1113 def test_strip_initial_indent():
1116 def sii(s):
1114 def sii(s):
1117 lines = s.splitlines()
1115 lines = s.splitlines()
1118 return '\n'.join(code.strip_initial_indent(lines))
1116 return '\n'.join(code.strip_initial_indent(lines))
1119
1117
1120 nt.assert_equal(sii(" a = 1\nb = 2"), "a = 1\nb = 2")
1118 nt.assert_equal(sii(" a = 1\nb = 2"), "a = 1\nb = 2")
1121 nt.assert_equal(sii(" a\n b\nc"), "a\n b\nc")
1119 nt.assert_equal(sii(" a\n b\nc"), "a\n b\nc")
1122 nt.assert_equal(sii("a\n b"), "a\n b")
1120 nt.assert_equal(sii("a\n b"), "a\n b")
1123
1121
1124 def test_logging_magic_quiet_from_arg():
1122 def test_logging_magic_quiet_from_arg():
1125 _ip.config.LoggingMagics.quiet = False
1123 _ip.config.LoggingMagics.quiet = False
1126 lm = logging.LoggingMagics(shell=_ip)
1124 lm = logging.LoggingMagics(shell=_ip)
1127 with TemporaryDirectory() as td:
1125 with TemporaryDirectory() as td:
1128 try:
1126 try:
1129 with tt.AssertNotPrints(re.compile("Activating.*")):
1127 with tt.AssertNotPrints(re.compile("Activating.*")):
1130 lm.logstart('-q {}'.format(
1128 lm.logstart('-q {}'.format(
1131 os.path.join(td, "quiet_from_arg.log")))
1129 os.path.join(td, "quiet_from_arg.log")))
1132 finally:
1130 finally:
1133 _ip.logger.logstop()
1131 _ip.logger.logstop()
1134
1132
1135 def test_logging_magic_quiet_from_config():
1133 def test_logging_magic_quiet_from_config():
1136 _ip.config.LoggingMagics.quiet = True
1134 _ip.config.LoggingMagics.quiet = True
1137 lm = logging.LoggingMagics(shell=_ip)
1135 lm = logging.LoggingMagics(shell=_ip)
1138 with TemporaryDirectory() as td:
1136 with TemporaryDirectory() as td:
1139 try:
1137 try:
1140 with tt.AssertNotPrints(re.compile("Activating.*")):
1138 with tt.AssertNotPrints(re.compile("Activating.*")):
1141 lm.logstart(os.path.join(td, "quiet_from_config.log"))
1139 lm.logstart(os.path.join(td, "quiet_from_config.log"))
1142 finally:
1140 finally:
1143 _ip.logger.logstop()
1141 _ip.logger.logstop()
1144
1142
1145
1143
1146 def test_logging_magic_not_quiet():
1144 def test_logging_magic_not_quiet():
1147 _ip.config.LoggingMagics.quiet = False
1145 _ip.config.LoggingMagics.quiet = False
1148 lm = logging.LoggingMagics(shell=_ip)
1146 lm = logging.LoggingMagics(shell=_ip)
1149 with TemporaryDirectory() as td:
1147 with TemporaryDirectory() as td:
1150 try:
1148 try:
1151 with tt.AssertPrints(re.compile("Activating.*")):
1149 with tt.AssertPrints(re.compile("Activating.*")):
1152 lm.logstart(os.path.join(td, "not_quiet.log"))
1150 lm.logstart(os.path.join(td, "not_quiet.log"))
1153 finally:
1151 finally:
1154 _ip.logger.logstop()
1152 _ip.logger.logstop()
1155
1153
1156
1154
1157 def test_time_no_var_expand():
1155 def test_time_no_var_expand():
1158 _ip.user_ns['a'] = 5
1156 _ip.user_ns['a'] = 5
1159 _ip.user_ns['b'] = []
1157 _ip.user_ns['b'] = []
1160 _ip.magic('time b.append("{a}")')
1158 _ip.magic('time b.append("{a}")')
1161 assert _ip.user_ns['b'] == ['{a}']
1159 assert _ip.user_ns['b'] == ['{a}']
1162
1160
1163
1161
1164 # this is slow, put at the end for local testing.
1162 # this is slow, put at the end for local testing.
1165 def test_timeit_arguments():
1163 def test_timeit_arguments():
1166 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
1164 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
1167 if sys.version_info < (3,7):
1165 if sys.version_info < (3,7):
1168 _ip.magic("timeit -n1 -r1 ('#')")
1166 _ip.magic("timeit -n1 -r1 ('#')")
1169 else:
1167 else:
1170 # 3.7 optimize no-op statement like above out, and complain there is
1168 # 3.7 optimize no-op statement like above out, and complain there is
1171 # nothing in the for loop.
1169 # nothing in the for loop.
1172 _ip.magic("timeit -n1 -r1 a=('#')")
1170 _ip.magic("timeit -n1 -r1 a=('#')")
@@ -1,196 +1,192 b''
1 """Tests for various magic functions specific to the terminal frontend.
1 """Tests for various magic functions specific to the terminal frontend.
2
2
3 Needs to be run by nose (to make ipython session available).
3 Needs to be run by nose (to make ipython session available).
4 """
4 """
5
5
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Imports
7 # Imports
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9
9
10 import sys
10 import sys
11 from io import StringIO
11 from io import StringIO
12 from unittest import TestCase
12 from unittest import TestCase
13
13
14 import nose.tools as nt
14 import nose.tools as nt
15
15
16 from IPython.testing import tools as tt
16 from IPython.testing import tools as tt
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Globals
20 #-----------------------------------------------------------------------------
21
22 #-----------------------------------------------------------------------------
23 # Test functions begin
19 # Test functions begin
24 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
25
21
26 def check_cpaste(code, should_fail=False):
22 def check_cpaste(code, should_fail=False):
27 """Execute code via 'cpaste' and ensure it was executed, unless
23 """Execute code via 'cpaste' and ensure it was executed, unless
28 should_fail is set.
24 should_fail is set.
29 """
25 """
30 ip.user_ns['code_ran'] = False
26 ip.user_ns['code_ran'] = False
31
27
32 src = StringIO()
28 src = StringIO()
33 if not hasattr(src, 'encoding'):
29 if not hasattr(src, 'encoding'):
34 # IPython expects stdin to have an encoding attribute
30 # IPython expects stdin to have an encoding attribute
35 src.encoding = None
31 src.encoding = None
36 src.write(code)
32 src.write(code)
37 src.write('\n--\n')
33 src.write('\n--\n')
38 src.seek(0)
34 src.seek(0)
39
35
40 stdin_save = sys.stdin
36 stdin_save = sys.stdin
41 sys.stdin = src
37 sys.stdin = src
42
38
43 try:
39 try:
44 context = tt.AssertPrints if should_fail else tt.AssertNotPrints
40 context = tt.AssertPrints if should_fail else tt.AssertNotPrints
45 with context("Traceback (most recent call last)"):
41 with context("Traceback (most recent call last)"):
46 ip.magic('cpaste')
42 ip.magic('cpaste')
47
43
48 if not should_fail:
44 if not should_fail:
49 assert ip.user_ns['code_ran'], "%r failed" % code
45 assert ip.user_ns['code_ran'], "%r failed" % code
50 finally:
46 finally:
51 sys.stdin = stdin_save
47 sys.stdin = stdin_save
52
48
53 def test_cpaste():
49 def test_cpaste():
54 """Test cpaste magic"""
50 """Test cpaste magic"""
55
51
56 def runf():
52 def runf():
57 """Marker function: sets a flag when executed.
53 """Marker function: sets a flag when executed.
58 """
54 """
59 ip.user_ns['code_ran'] = True
55 ip.user_ns['code_ran'] = True
60 return 'runf' # return string so '+ runf()' doesn't result in success
56 return 'runf' # return string so '+ runf()' doesn't result in success
61
57
62 tests = {'pass': ["runf()",
58 tests = {'pass': ["runf()",
63 "In [1]: runf()",
59 "In [1]: runf()",
64 "In [1]: if 1:\n ...: runf()",
60 "In [1]: if 1:\n ...: runf()",
65 "> > > runf()",
61 "> > > runf()",
66 ">>> runf()",
62 ">>> runf()",
67 " >>> runf()",
63 " >>> runf()",
68 ],
64 ],
69
65
70 'fail': ["1 + runf()",
66 'fail': ["1 + runf()",
71 "++ runf()",
67 "++ runf()",
72 ]}
68 ]}
73
69
74 ip.user_ns['runf'] = runf
70 ip.user_ns['runf'] = runf
75
71
76 for code in tests['pass']:
72 for code in tests['pass']:
77 check_cpaste(code)
73 check_cpaste(code)
78
74
79 for code in tests['fail']:
75 for code in tests['fail']:
80 check_cpaste(code, should_fail=True)
76 check_cpaste(code, should_fail=True)
81
77
82
78
83 class PasteTestCase(TestCase):
79 class PasteTestCase(TestCase):
84 """Multiple tests for clipboard pasting"""
80 """Multiple tests for clipboard pasting"""
85
81
86 def paste(self, txt, flags='-q'):
82 def paste(self, txt, flags='-q'):
87 """Paste input text, by default in quiet mode"""
83 """Paste input text, by default in quiet mode"""
88 ip.hooks.clipboard_get = lambda : txt
84 ip.hooks.clipboard_get = lambda : txt
89 ip.magic('paste '+flags)
85 ip.magic('paste '+flags)
90
86
91 def setUp(self):
87 def setUp(self):
92 # Inject fake clipboard hook but save original so we can restore it later
88 # Inject fake clipboard hook but save original so we can restore it later
93 self.original_clip = ip.hooks.clipboard_get
89 self.original_clip = ip.hooks.clipboard_get
94
90
95 def tearDown(self):
91 def tearDown(self):
96 # Restore original hook
92 # Restore original hook
97 ip.hooks.clipboard_get = self.original_clip
93 ip.hooks.clipboard_get = self.original_clip
98
94
99 def test_paste(self):
95 def test_paste(self):
100 ip.user_ns.pop('x', None)
96 ip.user_ns.pop('x', None)
101 self.paste('x = 1')
97 self.paste('x = 1')
102 nt.assert_equal(ip.user_ns['x'], 1)
98 nt.assert_equal(ip.user_ns['x'], 1)
103 ip.user_ns.pop('x')
99 ip.user_ns.pop('x')
104
100
105 def test_paste_pyprompt(self):
101 def test_paste_pyprompt(self):
106 ip.user_ns.pop('x', None)
102 ip.user_ns.pop('x', None)
107 self.paste('>>> x=2')
103 self.paste('>>> x=2')
108 nt.assert_equal(ip.user_ns['x'], 2)
104 nt.assert_equal(ip.user_ns['x'], 2)
109 ip.user_ns.pop('x')
105 ip.user_ns.pop('x')
110
106
111 def test_paste_py_multi(self):
107 def test_paste_py_multi(self):
112 self.paste("""
108 self.paste("""
113 >>> x = [1,2,3]
109 >>> x = [1,2,3]
114 >>> y = []
110 >>> y = []
115 >>> for i in x:
111 >>> for i in x:
116 ... y.append(i**2)
112 ... y.append(i**2)
117 ...
113 ...
118 """)
114 """)
119 nt.assert_equal(ip.user_ns['x'], [1,2,3])
115 nt.assert_equal(ip.user_ns['x'], [1,2,3])
120 nt.assert_equal(ip.user_ns['y'], [1,4,9])
116 nt.assert_equal(ip.user_ns['y'], [1,4,9])
121
117
122 def test_paste_py_multi_r(self):
118 def test_paste_py_multi_r(self):
123 "Now, test that self.paste -r works"
119 "Now, test that self.paste -r works"
124 self.test_paste_py_multi()
120 self.test_paste_py_multi()
125 nt.assert_equal(ip.user_ns.pop('x'), [1,2,3])
121 nt.assert_equal(ip.user_ns.pop('x'), [1,2,3])
126 nt.assert_equal(ip.user_ns.pop('y'), [1,4,9])
122 nt.assert_equal(ip.user_ns.pop('y'), [1,4,9])
127 nt.assert_false('x' in ip.user_ns)
123 nt.assert_false('x' in ip.user_ns)
128 ip.magic('paste -r')
124 ip.magic('paste -r')
129 nt.assert_equal(ip.user_ns['x'], [1,2,3])
125 nt.assert_equal(ip.user_ns['x'], [1,2,3])
130 nt.assert_equal(ip.user_ns['y'], [1,4,9])
126 nt.assert_equal(ip.user_ns['y'], [1,4,9])
131
127
132 def test_paste_email(self):
128 def test_paste_email(self):
133 "Test pasting of email-quoted contents"
129 "Test pasting of email-quoted contents"
134 self.paste("""\
130 self.paste("""\
135 >> def foo(x):
131 >> def foo(x):
136 >> return x + 1
132 >> return x + 1
137 >> xx = foo(1.1)""")
133 >> xx = foo(1.1)""")
138 nt.assert_equal(ip.user_ns['xx'], 2.1)
134 nt.assert_equal(ip.user_ns['xx'], 2.1)
139
135
140 def test_paste_email2(self):
136 def test_paste_email2(self):
141 "Email again; some programs add a space also at each quoting level"
137 "Email again; some programs add a space also at each quoting level"
142 self.paste("""\
138 self.paste("""\
143 > > def foo(x):
139 > > def foo(x):
144 > > return x + 1
140 > > return x + 1
145 > > yy = foo(2.1) """)
141 > > yy = foo(2.1) """)
146 nt.assert_equal(ip.user_ns['yy'], 3.1)
142 nt.assert_equal(ip.user_ns['yy'], 3.1)
147
143
148 def test_paste_email_py(self):
144 def test_paste_email_py(self):
149 "Email quoting of interactive input"
145 "Email quoting of interactive input"
150 self.paste("""\
146 self.paste("""\
151 >> >>> def f(x):
147 >> >>> def f(x):
152 >> ... return x+1
148 >> ... return x+1
153 >> ...
149 >> ...
154 >> >>> zz = f(2.5) """)
150 >> >>> zz = f(2.5) """)
155 nt.assert_equal(ip.user_ns['zz'], 3.5)
151 nt.assert_equal(ip.user_ns['zz'], 3.5)
156
152
157 def test_paste_echo(self):
153 def test_paste_echo(self):
158 "Also test self.paste echoing, by temporarily faking the writer"
154 "Also test self.paste echoing, by temporarily faking the writer"
159 w = StringIO()
155 w = StringIO()
160 writer = ip.write
156 writer = ip.write
161 ip.write = w.write
157 ip.write = w.write
162 code = """
158 code = """
163 a = 100
159 a = 100
164 b = 200"""
160 b = 200"""
165 try:
161 try:
166 self.paste(code,'')
162 self.paste(code,'')
167 out = w.getvalue()
163 out = w.getvalue()
168 finally:
164 finally:
169 ip.write = writer
165 ip.write = writer
170 nt.assert_equal(ip.user_ns['a'], 100)
166 nt.assert_equal(ip.user_ns['a'], 100)
171 nt.assert_equal(ip.user_ns['b'], 200)
167 nt.assert_equal(ip.user_ns['b'], 200)
172 assert out == code+"\n## -- End pasted text --\n"
168 assert out == code+"\n## -- End pasted text --\n"
173
169
174 def test_paste_leading_commas(self):
170 def test_paste_leading_commas(self):
175 "Test multiline strings with leading commas"
171 "Test multiline strings with leading commas"
176 tm = ip.magics_manager.registry['TerminalMagics']
172 tm = ip.magics_manager.registry['TerminalMagics']
177 s = '''\
173 s = '''\
178 a = """
174 a = """
179 ,1,2,3
175 ,1,2,3
180 """'''
176 """'''
181 ip.user_ns.pop('foo', None)
177 ip.user_ns.pop('foo', None)
182 tm.store_or_execute(s, 'foo')
178 tm.store_or_execute(s, 'foo')
183 nt.assert_in('foo', ip.user_ns)
179 nt.assert_in('foo', ip.user_ns)
184
180
185
181
186 def test_paste_trailing_question(self):
182 def test_paste_trailing_question(self):
187 "Test pasting sources with trailing question marks"
183 "Test pasting sources with trailing question marks"
188 tm = ip.magics_manager.registry['TerminalMagics']
184 tm = ip.magics_manager.registry['TerminalMagics']
189 s = '''\
185 s = '''\
190 def funcfoo():
186 def funcfoo():
191 if True: #am i true?
187 if True: #am i true?
192 return 'fooresult'
188 return 'fooresult'
193 '''
189 '''
194 ip.user_ns.pop('funcfoo', None)
190 ip.user_ns.pop('funcfoo', None)
195 self.paste(s)
191 self.paste(s)
196 nt.assert_equal(ip.user_ns['funcfoo'](), 'fooresult')
192 nt.assert_equal(ip.user_ns['funcfoo'](), 'fooresult')
@@ -1,437 +1,439 b''
1 """Tests for the object inspection functionality.
1 """Tests for the object inspection functionality.
2 """
2 """
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7
7
8 from inspect import signature, Signature, Parameter
8 from inspect import signature, Signature, Parameter
9 import os
9 import os
10 import re
10 import re
11
11
12 import nose.tools as nt
12 import nose.tools as nt
13
13
14 from .. import oinspect
14 from .. import oinspect
15
15
16 from decorator import decorator
16 from decorator import decorator
17
17
18 from IPython.testing.tools import AssertPrints, AssertNotPrints
18 from IPython.testing.tools import AssertPrints, AssertNotPrints
19 from IPython.utils.path import compress_user
19 from IPython.utils.path import compress_user
20
20
21
21
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 # Globals and constants
23 # Globals and constants
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 inspector = oinspect.Inspector()
26 inspector = None
27 ip = get_ipython()
27
28 def setup_module():
29 global inspector
30 inspector = oinspect.Inspector()
31
28
32
29 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
30 # Local utilities
34 # Local utilities
31 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
32
36
33 # WARNING: since this test checks the line number where a function is
37 # WARNING: since this test checks the line number where a function is
34 # defined, if any code is inserted above, the following line will need to be
38 # defined, if any code is inserted above, the following line will need to be
35 # updated. Do NOT insert any whitespace between the next line and the function
39 # updated. Do NOT insert any whitespace between the next line and the function
36 # definition below.
40 # definition below.
37 THIS_LINE_NUMBER = 37 # Put here the actual number of this line
41 THIS_LINE_NUMBER = 41 # Put here the actual number of this line
38
42
39 from unittest import TestCase
43 from unittest import TestCase
40
44
41 class Test(TestCase):
45 class Test(TestCase):
42
46
43 def test_find_source_lines(self):
47 def test_find_source_lines(self):
44 self.assertEqual(oinspect.find_source_lines(Test.test_find_source_lines),
48 self.assertEqual(oinspect.find_source_lines(Test.test_find_source_lines),
45 THIS_LINE_NUMBER+6)
49 THIS_LINE_NUMBER+6)
46
50
47
51
48 # A couple of utilities to ensure these tests work the same from a source or a
52 # A couple of utilities to ensure these tests work the same from a source or a
49 # binary install
53 # binary install
50 def pyfile(fname):
54 def pyfile(fname):
51 return os.path.normcase(re.sub('.py[co]$', '.py', fname))
55 return os.path.normcase(re.sub('.py[co]$', '.py', fname))
52
56
53
57
54 def match_pyfiles(f1, f2):
58 def match_pyfiles(f1, f2):
55 nt.assert_equal(pyfile(f1), pyfile(f2))
59 nt.assert_equal(pyfile(f1), pyfile(f2))
56
60
57
61
58 def test_find_file():
62 def test_find_file():
59 match_pyfiles(oinspect.find_file(test_find_file), os.path.abspath(__file__))
63 match_pyfiles(oinspect.find_file(test_find_file), os.path.abspath(__file__))
60
64
61
65
62 def test_find_file_decorated1():
66 def test_find_file_decorated1():
63
67
64 @decorator
68 @decorator
65 def noop1(f):
69 def noop1(f):
66 def wrapper(*a, **kw):
70 def wrapper(*a, **kw):
67 return f(*a, **kw)
71 return f(*a, **kw)
68 return wrapper
72 return wrapper
69
73
70 @noop1
74 @noop1
71 def f(x):
75 def f(x):
72 "My docstring"
76 "My docstring"
73
77
74 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
78 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
75 nt.assert_equal(f.__doc__, "My docstring")
79 nt.assert_equal(f.__doc__, "My docstring")
76
80
77
81
78 def test_find_file_decorated2():
82 def test_find_file_decorated2():
79
83
80 @decorator
84 @decorator
81 def noop2(f, *a, **kw):
85 def noop2(f, *a, **kw):
82 return f(*a, **kw)
86 return f(*a, **kw)
83
87
84 @noop2
88 @noop2
85 @noop2
89 @noop2
86 @noop2
90 @noop2
87 def f(x):
91 def f(x):
88 "My docstring 2"
92 "My docstring 2"
89
93
90 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
94 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
91 nt.assert_equal(f.__doc__, "My docstring 2")
95 nt.assert_equal(f.__doc__, "My docstring 2")
92
96
93
97
94 def test_find_file_magic():
98 def test_find_file_magic():
95 run = ip.find_line_magic('run')
99 run = ip.find_line_magic('run')
96 nt.assert_not_equal(oinspect.find_file(run), None)
100 nt.assert_not_equal(oinspect.find_file(run), None)
97
101
98
102
99 # A few generic objects we can then inspect in the tests below
103 # A few generic objects we can then inspect in the tests below
100
104
101 class Call(object):
105 class Call(object):
102 """This is the class docstring."""
106 """This is the class docstring."""
103
107
104 def __init__(self, x, y=1):
108 def __init__(self, x, y=1):
105 """This is the constructor docstring."""
109 """This is the constructor docstring."""
106
110
107 def __call__(self, *a, **kw):
111 def __call__(self, *a, **kw):
108 """This is the call docstring."""
112 """This is the call docstring."""
109
113
110 def method(self, x, z=2):
114 def method(self, x, z=2):
111 """Some method's docstring"""
115 """Some method's docstring"""
112
116
113 class HasSignature(object):
117 class HasSignature(object):
114 """This is the class docstring."""
118 """This is the class docstring."""
115 __signature__ = Signature([Parameter('test', Parameter.POSITIONAL_OR_KEYWORD)])
119 __signature__ = Signature([Parameter('test', Parameter.POSITIONAL_OR_KEYWORD)])
116
120
117 def __init__(self, *args):
121 def __init__(self, *args):
118 """This is the init docstring"""
122 """This is the init docstring"""
119
123
120
124
121 class SimpleClass(object):
125 class SimpleClass(object):
122 def method(self, x, z=2):
126 def method(self, x, z=2):
123 """Some method's docstring"""
127 """Some method's docstring"""
124
128
125
129
126
127 class Awkward(object):
130 class Awkward(object):
128 def __getattr__(self, name):
131 def __getattr__(self, name):
129 raise Exception(name)
132 raise Exception(name)
130
133
131 class NoBoolCall:
134 class NoBoolCall:
132 """
135 """
133 callable with `__bool__` raising should still be inspect-able.
136 callable with `__bool__` raising should still be inspect-able.
134 """
137 """
135
138
136 def __call__(self):
139 def __call__(self):
137 """does nothing"""
140 """does nothing"""
138 pass
141 pass
139
142
140 def __bool__(self):
143 def __bool__(self):
141 """just raise NotImplemented"""
144 """just raise NotImplemented"""
142 raise NotImplementedError('Must be implemented')
145 raise NotImplementedError('Must be implemented')
143
146
144
147
145 class SerialLiar(object):
148 class SerialLiar(object):
146 """Attribute accesses always get another copy of the same class.
149 """Attribute accesses always get another copy of the same class.
147
150
148 unittest.mock.call does something similar, but it's not ideal for testing
151 unittest.mock.call does something similar, but it's not ideal for testing
149 as the failure mode is to eat all your RAM. This gives up after 10k levels.
152 as the failure mode is to eat all your RAM. This gives up after 10k levels.
150 """
153 """
151 def __init__(self, max_fibbing_twig, lies_told=0):
154 def __init__(self, max_fibbing_twig, lies_told=0):
152 if lies_told > 10000:
155 if lies_told > 10000:
153 raise RuntimeError('Nose too long, honesty is the best policy')
156 raise RuntimeError('Nose too long, honesty is the best policy')
154 self.max_fibbing_twig = max_fibbing_twig
157 self.max_fibbing_twig = max_fibbing_twig
155 self.lies_told = lies_told
158 self.lies_told = lies_told
156 max_fibbing_twig[0] = max(max_fibbing_twig[0], lies_told)
159 max_fibbing_twig[0] = max(max_fibbing_twig[0], lies_told)
157
160
158 def __getattr__(self, item):
161 def __getattr__(self, item):
159 return SerialLiar(self.max_fibbing_twig, self.lies_told + 1)
162 return SerialLiar(self.max_fibbing_twig, self.lies_told + 1)
160
163
161 #-----------------------------------------------------------------------------
164 #-----------------------------------------------------------------------------
162 # Tests
165 # Tests
163 #-----------------------------------------------------------------------------
166 #-----------------------------------------------------------------------------
164
167
165 def test_info():
168 def test_info():
166 "Check that Inspector.info fills out various fields as expected."
169 "Check that Inspector.info fills out various fields as expected."
167 i = inspector.info(Call, oname='Call')
170 i = inspector.info(Call, oname='Call')
168 nt.assert_equal(i['type_name'], 'type')
171 nt.assert_equal(i['type_name'], 'type')
169 expted_class = str(type(type)) # <class 'type'> (Python 3) or <type 'type'>
172 expted_class = str(type(type)) # <class 'type'> (Python 3) or <type 'type'>
170 nt.assert_equal(i['base_class'], expted_class)
173 nt.assert_equal(i['base_class'], expted_class)
171 nt.assert_regex(i['string_form'], "<class 'IPython.core.tests.test_oinspect.Call'( at 0x[0-9a-f]{1,9})?>")
174 nt.assert_regex(i['string_form'], "<class 'IPython.core.tests.test_oinspect.Call'( at 0x[0-9a-f]{1,9})?>")
172 fname = __file__
175 fname = __file__
173 if fname.endswith(".pyc"):
176 if fname.endswith(".pyc"):
174 fname = fname[:-1]
177 fname = fname[:-1]
175 # case-insensitive comparison needed on some filesystems
178 # case-insensitive comparison needed on some filesystems
176 # e.g. Windows:
179 # e.g. Windows:
177 nt.assert_equal(i['file'].lower(), compress_user(fname).lower())
180 nt.assert_equal(i['file'].lower(), compress_user(fname).lower())
178 nt.assert_equal(i['definition'], None)
181 nt.assert_equal(i['definition'], None)
179 nt.assert_equal(i['docstring'], Call.__doc__)
182 nt.assert_equal(i['docstring'], Call.__doc__)
180 nt.assert_equal(i['source'], None)
183 nt.assert_equal(i['source'], None)
181 nt.assert_true(i['isclass'])
184 nt.assert_true(i['isclass'])
182 nt.assert_equal(i['init_definition'], "Call(x, y=1)")
185 nt.assert_equal(i['init_definition'], "Call(x, y=1)")
183 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
186 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
184
187
185 i = inspector.info(Call, detail_level=1)
188 i = inspector.info(Call, detail_level=1)
186 nt.assert_not_equal(i['source'], None)
189 nt.assert_not_equal(i['source'], None)
187 nt.assert_equal(i['docstring'], None)
190 nt.assert_equal(i['docstring'], None)
188
191
189 c = Call(1)
192 c = Call(1)
190 c.__doc__ = "Modified instance docstring"
193 c.__doc__ = "Modified instance docstring"
191 i = inspector.info(c)
194 i = inspector.info(c)
192 nt.assert_equal(i['type_name'], 'Call')
195 nt.assert_equal(i['type_name'], 'Call')
193 nt.assert_equal(i['docstring'], "Modified instance docstring")
196 nt.assert_equal(i['docstring'], "Modified instance docstring")
194 nt.assert_equal(i['class_docstring'], Call.__doc__)
197 nt.assert_equal(i['class_docstring'], Call.__doc__)
195 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
198 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
196 nt.assert_equal(i['call_docstring'], Call.__call__.__doc__)
199 nt.assert_equal(i['call_docstring'], Call.__call__.__doc__)
197
200
198 def test_class_signature():
201 def test_class_signature():
199 info = inspector.info(HasSignature, 'HasSignature')
202 info = inspector.info(HasSignature, 'HasSignature')
200 nt.assert_equal(info['init_definition'], "HasSignature(test)")
203 nt.assert_equal(info['init_definition'], "HasSignature(test)")
201 nt.assert_equal(info['init_docstring'], HasSignature.__init__.__doc__)
204 nt.assert_equal(info['init_docstring'], HasSignature.__init__.__doc__)
202
205
203 def test_info_awkward():
206 def test_info_awkward():
204 # Just test that this doesn't throw an error.
207 # Just test that this doesn't throw an error.
205 inspector.info(Awkward())
208 inspector.info(Awkward())
206
209
207 def test_bool_raise():
210 def test_bool_raise():
208 inspector.info(NoBoolCall())
211 inspector.info(NoBoolCall())
209
212
210 def test_info_serialliar():
213 def test_info_serialliar():
211 fib_tracker = [0]
214 fib_tracker = [0]
212 inspector.info(SerialLiar(fib_tracker))
215 inspector.info(SerialLiar(fib_tracker))
213
216
214 # Nested attribute access should be cut off at 100 levels deep to avoid
217 # Nested attribute access should be cut off at 100 levels deep to avoid
215 # infinite loops: https://github.com/ipython/ipython/issues/9122
218 # infinite loops: https://github.com/ipython/ipython/issues/9122
216 nt.assert_less(fib_tracker[0], 9000)
219 nt.assert_less(fib_tracker[0], 9000)
217
220
218 def support_function_one(x, y=2, *a, **kw):
221 def support_function_one(x, y=2, *a, **kw):
219 """A simple function."""
222 """A simple function."""
220
223
221 def test_calldef_none():
224 def test_calldef_none():
222 # We should ignore __call__ for all of these.
225 # We should ignore __call__ for all of these.
223 for obj in [support_function_one, SimpleClass().method, any, str.upper]:
226 for obj in [support_function_one, SimpleClass().method, any, str.upper]:
224 print(obj)
225 i = inspector.info(obj)
227 i = inspector.info(obj)
226 nt.assert_is(i['call_def'], None)
228 nt.assert_is(i['call_def'], None)
227
229
228 def f_kwarg(pos, *, kwonly):
230 def f_kwarg(pos, *, kwonly):
229 pass
231 pass
230
232
231 def test_definition_kwonlyargs():
233 def test_definition_kwonlyargs():
232 i = inspector.info(f_kwarg, oname='f_kwarg') # analysis:ignore
234 i = inspector.info(f_kwarg, oname='f_kwarg') # analysis:ignore
233 nt.assert_equal(i['definition'], "f_kwarg(pos, *, kwonly)")
235 nt.assert_equal(i['definition'], "f_kwarg(pos, *, kwonly)")
234
236
235 def test_getdoc():
237 def test_getdoc():
236 class A(object):
238 class A(object):
237 """standard docstring"""
239 """standard docstring"""
238 pass
240 pass
239
241
240 class B(object):
242 class B(object):
241 """standard docstring"""
243 """standard docstring"""
242 def getdoc(self):
244 def getdoc(self):
243 return "custom docstring"
245 return "custom docstring"
244
246
245 class C(object):
247 class C(object):
246 """standard docstring"""
248 """standard docstring"""
247 def getdoc(self):
249 def getdoc(self):
248 return None
250 return None
249
251
250 a = A()
252 a = A()
251 b = B()
253 b = B()
252 c = C()
254 c = C()
253
255
254 nt.assert_equal(oinspect.getdoc(a), "standard docstring")
256 nt.assert_equal(oinspect.getdoc(a), "standard docstring")
255 nt.assert_equal(oinspect.getdoc(b), "custom docstring")
257 nt.assert_equal(oinspect.getdoc(b), "custom docstring")
256 nt.assert_equal(oinspect.getdoc(c), "standard docstring")
258 nt.assert_equal(oinspect.getdoc(c), "standard docstring")
257
259
258
260
259 def test_empty_property_has_no_source():
261 def test_empty_property_has_no_source():
260 i = inspector.info(property(), detail_level=1)
262 i = inspector.info(property(), detail_level=1)
261 nt.assert_is(i['source'], None)
263 nt.assert_is(i['source'], None)
262
264
263
265
264 def test_property_sources():
266 def test_property_sources():
265 import posixpath
267 import posixpath
266 # A simple adder whose source and signature stays
268 # A simple adder whose source and signature stays
267 # the same across Python distributions
269 # the same across Python distributions
268 def simple_add(a, b):
270 def simple_add(a, b):
269 "Adds two numbers"
271 "Adds two numbers"
270 return a + b
272 return a + b
271
273
272 class A(object):
274 class A(object):
273 @property
275 @property
274 def foo(self):
276 def foo(self):
275 return 'bar'
277 return 'bar'
276
278
277 foo = foo.setter(lambda self, v: setattr(self, 'bar', v))
279 foo = foo.setter(lambda self, v: setattr(self, 'bar', v))
278
280
279 dname = property(posixpath.dirname)
281 dname = property(posixpath.dirname)
280 adder = property(simple_add)
282 adder = property(simple_add)
281
283
282 i = inspector.info(A.foo, detail_level=1)
284 i = inspector.info(A.foo, detail_level=1)
283 nt.assert_in('def foo(self):', i['source'])
285 nt.assert_in('def foo(self):', i['source'])
284 nt.assert_in('lambda self, v:', i['source'])
286 nt.assert_in('lambda self, v:', i['source'])
285
287
286 i = inspector.info(A.dname, detail_level=1)
288 i = inspector.info(A.dname, detail_level=1)
287 nt.assert_in('def dirname(p)', i['source'])
289 nt.assert_in('def dirname(p)', i['source'])
288
290
289 i = inspector.info(A.adder, detail_level=1)
291 i = inspector.info(A.adder, detail_level=1)
290 nt.assert_in('def simple_add(a, b)', i['source'])
292 nt.assert_in('def simple_add(a, b)', i['source'])
291
293
292
294
293 def test_property_docstring_is_in_info_for_detail_level_0():
295 def test_property_docstring_is_in_info_for_detail_level_0():
294 class A(object):
296 class A(object):
295 @property
297 @property
296 def foobar(self):
298 def foobar(self):
297 """This is `foobar` property."""
299 """This is `foobar` property."""
298 pass
300 pass
299
301
300 ip.user_ns['a_obj'] = A()
302 ip.user_ns['a_obj'] = A()
301 nt.assert_equal(
303 nt.assert_equal(
302 'This is `foobar` property.',
304 'This is `foobar` property.',
303 ip.object_inspect('a_obj.foobar', detail_level=0)['docstring'])
305 ip.object_inspect('a_obj.foobar', detail_level=0)['docstring'])
304
306
305 ip.user_ns['a_cls'] = A
307 ip.user_ns['a_cls'] = A
306 nt.assert_equal(
308 nt.assert_equal(
307 'This is `foobar` property.',
309 'This is `foobar` property.',
308 ip.object_inspect('a_cls.foobar', detail_level=0)['docstring'])
310 ip.object_inspect('a_cls.foobar', detail_level=0)['docstring'])
309
311
310
312
311 def test_pdef():
313 def test_pdef():
312 # See gh-1914
314 # See gh-1914
313 def foo(): pass
315 def foo(): pass
314 inspector.pdef(foo, 'foo')
316 inspector.pdef(foo, 'foo')
315
317
316
318
317 def test_pinfo_nonascii():
319 def test_pinfo_nonascii():
318 # See gh-1177
320 # See gh-1177
319 from . import nonascii2
321 from . import nonascii2
320 ip.user_ns['nonascii2'] = nonascii2
322 ip.user_ns['nonascii2'] = nonascii2
321 ip._inspect('pinfo', 'nonascii2', detail_level=1)
323 ip._inspect('pinfo', 'nonascii2', detail_level=1)
322
324
323 def test_pinfo_type():
325 def test_pinfo_type():
324 """
326 """
325 type can fail in various edge case, for example `type.__subclass__()`
327 type can fail in various edge case, for example `type.__subclass__()`
326 """
328 """
327 ip._inspect('pinfo', 'type')
329 ip._inspect('pinfo', 'type')
328
330
329
331
330 def test_pinfo_docstring_no_source():
332 def test_pinfo_docstring_no_source():
331 """Docstring should be included with detail_level=1 if there is no source"""
333 """Docstring should be included with detail_level=1 if there is no source"""
332 with AssertPrints('Docstring:'):
334 with AssertPrints('Docstring:'):
333 ip._inspect('pinfo', 'str.format', detail_level=0)
335 ip._inspect('pinfo', 'str.format', detail_level=0)
334 with AssertPrints('Docstring:'):
336 with AssertPrints('Docstring:'):
335 ip._inspect('pinfo', 'str.format', detail_level=1)
337 ip._inspect('pinfo', 'str.format', detail_level=1)
336
338
337
339
338 def test_pinfo_no_docstring_if_source():
340 def test_pinfo_no_docstring_if_source():
339 """Docstring should not be included with detail_level=1 if source is found"""
341 """Docstring should not be included with detail_level=1 if source is found"""
340 def foo():
342 def foo():
341 """foo has a docstring"""
343 """foo has a docstring"""
342
344
343 ip.user_ns['foo'] = foo
345 ip.user_ns['foo'] = foo
344
346
345 with AssertPrints('Docstring:'):
347 with AssertPrints('Docstring:'):
346 ip._inspect('pinfo', 'foo', detail_level=0)
348 ip._inspect('pinfo', 'foo', detail_level=0)
347 with AssertPrints('Source:'):
349 with AssertPrints('Source:'):
348 ip._inspect('pinfo', 'foo', detail_level=1)
350 ip._inspect('pinfo', 'foo', detail_level=1)
349 with AssertNotPrints('Docstring:'):
351 with AssertNotPrints('Docstring:'):
350 ip._inspect('pinfo', 'foo', detail_level=1)
352 ip._inspect('pinfo', 'foo', detail_level=1)
351
353
352
354
353 def test_pinfo_docstring_if_detail_and_no_source():
355 def test_pinfo_docstring_if_detail_and_no_source():
354 """ Docstring should be displayed if source info not available """
356 """ Docstring should be displayed if source info not available """
355 obj_def = '''class Foo(object):
357 obj_def = '''class Foo(object):
356 """ This is a docstring for Foo """
358 """ This is a docstring for Foo """
357 def bar(self):
359 def bar(self):
358 """ This is a docstring for Foo.bar """
360 """ This is a docstring for Foo.bar """
359 pass
361 pass
360 '''
362 '''
361
363
362 ip.run_cell(obj_def)
364 ip.run_cell(obj_def)
363 ip.run_cell('foo = Foo()')
365 ip.run_cell('foo = Foo()')
364
366
365 with AssertNotPrints("Source:"):
367 with AssertNotPrints("Source:"):
366 with AssertPrints('Docstring:'):
368 with AssertPrints('Docstring:'):
367 ip._inspect('pinfo', 'foo', detail_level=0)
369 ip._inspect('pinfo', 'foo', detail_level=0)
368 with AssertPrints('Docstring:'):
370 with AssertPrints('Docstring:'):
369 ip._inspect('pinfo', 'foo', detail_level=1)
371 ip._inspect('pinfo', 'foo', detail_level=1)
370 with AssertPrints('Docstring:'):
372 with AssertPrints('Docstring:'):
371 ip._inspect('pinfo', 'foo.bar', detail_level=0)
373 ip._inspect('pinfo', 'foo.bar', detail_level=0)
372
374
373 with AssertNotPrints('Docstring:'):
375 with AssertNotPrints('Docstring:'):
374 with AssertPrints('Source:'):
376 with AssertPrints('Source:'):
375 ip._inspect('pinfo', 'foo.bar', detail_level=1)
377 ip._inspect('pinfo', 'foo.bar', detail_level=1)
376
378
377
379
378 def test_pinfo_magic():
380 def test_pinfo_magic():
379 with AssertPrints('Docstring:'):
381 with AssertPrints('Docstring:'):
380 ip._inspect('pinfo', 'lsmagic', detail_level=0)
382 ip._inspect('pinfo', 'lsmagic', detail_level=0)
381
383
382 with AssertPrints('Source:'):
384 with AssertPrints('Source:'):
383 ip._inspect('pinfo', 'lsmagic', detail_level=1)
385 ip._inspect('pinfo', 'lsmagic', detail_level=1)
384
386
385
387
386 def test_init_colors():
388 def test_init_colors():
387 # ensure colors are not present in signature info
389 # ensure colors are not present in signature info
388 info = inspector.info(HasSignature)
390 info = inspector.info(HasSignature)
389 init_def = info['init_definition']
391 init_def = info['init_definition']
390 nt.assert_not_in('[0m', init_def)
392 nt.assert_not_in('[0m', init_def)
391
393
392
394
393 def test_builtin_init():
395 def test_builtin_init():
394 info = inspector.info(list)
396 info = inspector.info(list)
395 init_def = info['init_definition']
397 init_def = info['init_definition']
396 nt.assert_is_not_none(init_def)
398 nt.assert_is_not_none(init_def)
397
399
398
400
399 def test_render_signature_short():
401 def test_render_signature_short():
400 def short_fun(a=1): pass
402 def short_fun(a=1): pass
401 sig = oinspect._render_signature(
403 sig = oinspect._render_signature(
402 signature(short_fun),
404 signature(short_fun),
403 short_fun.__name__,
405 short_fun.__name__,
404 )
406 )
405 nt.assert_equal(sig, 'short_fun(a=1)')
407 nt.assert_equal(sig, 'short_fun(a=1)')
406
408
407
409
408 def test_render_signature_long():
410 def test_render_signature_long():
409 from typing import Optional
411 from typing import Optional
410
412
411 def long_function(
413 def long_function(
412 a_really_long_parameter: int,
414 a_really_long_parameter: int,
413 and_another_long_one: bool = False,
415 and_another_long_one: bool = False,
414 let_us_make_sure_this_is_looong: Optional[str] = None,
416 let_us_make_sure_this_is_looong: Optional[str] = None,
415 ) -> bool: pass
417 ) -> bool: pass
416
418
417 sig = oinspect._render_signature(
419 sig = oinspect._render_signature(
418 signature(long_function),
420 signature(long_function),
419 long_function.__name__,
421 long_function.__name__,
420 )
422 )
421 nt.assert_in(sig, [
423 nt.assert_in(sig, [
422 # Python >=3.7
424 # Python >=3.7
423 '''\
425 '''\
424 long_function(
426 long_function(
425 a_really_long_parameter: int,
427 a_really_long_parameter: int,
426 and_another_long_one: bool = False,
428 and_another_long_one: bool = False,
427 let_us_make_sure_this_is_looong: Union[str, NoneType] = None,
429 let_us_make_sure_this_is_looong: Union[str, NoneType] = None,
428 ) -> bool\
430 ) -> bool\
429 ''', # Python <=3.6
431 ''', # Python <=3.6
430 '''\
432 '''\
431 long_function(
433 long_function(
432 a_really_long_parameter:int,
434 a_really_long_parameter:int,
433 and_another_long_one:bool=False,
435 and_another_long_one:bool=False,
434 let_us_make_sure_this_is_looong:Union[str, NoneType]=None,
436 let_us_make_sure_this_is_looong:Union[str, NoneType]=None,
435 ) -> bool\
437 ) -> bool\
436 ''',
438 ''',
437 ])
439 ])
@@ -1,8 +1,8 b''
1 try:
1 try:
2 from numpy.testing.noseclasses import KnownFailure, knownfailureif
2 from numpy.testing import KnownFailure, knownfailureif
3 except ImportError:
3 except ImportError:
4 from ._decorators import knownfailureif
4 from ._decorators import knownfailureif
5 try:
5 try:
6 from ._numpy_testing_noseclasses import KnownFailure
6 from ._numpy_testing_noseclasses import KnownFailure
7 except ImportError:
7 except ImportError:
8 pass
8 pass
@@ -1,492 +1,491 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """IPython Test Process Controller
2 """IPython Test Process Controller
3
3
4 This module runs one or more subprocesses which will actually run the IPython
4 This module runs one or more subprocesses which will actually run the IPython
5 test suite.
5 test suite.
6
6
7 """
7 """
8
8
9 # Copyright (c) IPython Development Team.
9 # Copyright (c) IPython Development Team.
10 # Distributed under the terms of the Modified BSD License.
10 # Distributed under the terms of the Modified BSD License.
11
11
12
12
13 import argparse
13 import argparse
14 import multiprocessing.pool
14 import multiprocessing.pool
15 import os
15 import os
16 import stat
16 import stat
17 import shutil
17 import shutil
18 import signal
18 import signal
19 import sys
19 import sys
20 import subprocess
20 import subprocess
21 import time
21 import time
22
22
23 from .iptest import (
23 from .iptest import (
24 have, test_group_names as py_test_group_names, test_sections, StreamCapturer,
24 have, test_group_names as py_test_group_names, test_sections, StreamCapturer,
25 )
25 )
26 from IPython.utils.path import compress_user
26 from IPython.utils.path import compress_user
27 from IPython.utils.py3compat import decode
27 from IPython.utils.py3compat import decode
28 from IPython.utils.sysinfo import get_sys_info
28 from IPython.utils.sysinfo import get_sys_info
29 from IPython.utils.tempdir import TemporaryDirectory
29 from IPython.utils.tempdir import TemporaryDirectory
30
30
31 class TestController:
31 class TestController:
32 """Run tests in a subprocess
32 """Run tests in a subprocess
33 """
33 """
34 #: str, IPython test suite to be executed.
34 #: str, IPython test suite to be executed.
35 section = None
35 section = None
36 #: list, command line arguments to be executed
36 #: list, command line arguments to be executed
37 cmd = None
37 cmd = None
38 #: dict, extra environment variables to set for the subprocess
38 #: dict, extra environment variables to set for the subprocess
39 env = None
39 env = None
40 #: list, TemporaryDirectory instances to clear up when the process finishes
40 #: list, TemporaryDirectory instances to clear up when the process finishes
41 dirs = None
41 dirs = None
42 #: subprocess.Popen instance
42 #: subprocess.Popen instance
43 process = None
43 process = None
44 #: str, process stdout+stderr
44 #: str, process stdout+stderr
45 stdout = None
45 stdout = None
46
46
47 def __init__(self):
47 def __init__(self):
48 self.cmd = []
48 self.cmd = []
49 self.env = {}
49 self.env = {}
50 self.dirs = []
50 self.dirs = []
51
51
52 def setup(self):
52 def setUp(self):
53 """Create temporary directories etc.
53 """Create temporary directories etc.
54
54
55 This is only called when we know the test group will be run. Things
55 This is only called when we know the test group will be run. Things
56 created here may be cleaned up by self.cleanup().
56 created here may be cleaned up by self.cleanup().
57 """
57 """
58 pass
58 pass
59
59
60 def launch(self, buffer_output=False, capture_output=False):
60 def launch(self, buffer_output=False, capture_output=False):
61 # print('*** ENV:', self.env) # dbg
61 # print('*** ENV:', self.env) # dbg
62 # print('*** CMD:', self.cmd) # dbg
62 # print('*** CMD:', self.cmd) # dbg
63 env = os.environ.copy()
63 env = os.environ.copy()
64 env.update(self.env)
64 env.update(self.env)
65 if buffer_output:
65 if buffer_output:
66 capture_output = True
66 capture_output = True
67 self.stdout_capturer = c = StreamCapturer(echo=not buffer_output)
67 self.stdout_capturer = c = StreamCapturer(echo=not buffer_output)
68 c.start()
68 c.start()
69 stdout = c.writefd if capture_output else None
69 stdout = c.writefd if capture_output else None
70 stderr = subprocess.STDOUT if capture_output else None
70 stderr = subprocess.STDOUT if capture_output else None
71 self.process = subprocess.Popen(self.cmd, stdout=stdout,
71 self.process = subprocess.Popen(self.cmd, stdout=stdout,
72 stderr=stderr, env=env)
72 stderr=stderr, env=env)
73
73
74 def wait(self):
74 def wait(self):
75 self.process.wait()
75 self.process.wait()
76 self.stdout_capturer.halt()
76 self.stdout_capturer.halt()
77 self.stdout = self.stdout_capturer.get_buffer()
77 self.stdout = self.stdout_capturer.get_buffer()
78 return self.process.returncode
78 return self.process.returncode
79
79
80 def cleanup_process(self):
80 def cleanup_process(self):
81 """Cleanup on exit by killing any leftover processes."""
81 """Cleanup on exit by killing any leftover processes."""
82 subp = self.process
82 subp = self.process
83 if subp is None or (subp.poll() is not None):
83 if subp is None or (subp.poll() is not None):
84 return # Process doesn't exist, or is already dead.
84 return # Process doesn't exist, or is already dead.
85
85
86 try:
86 try:
87 print('Cleaning up stale PID: %d' % subp.pid)
87 print('Cleaning up stale PID: %d' % subp.pid)
88 subp.kill()
88 subp.kill()
89 except: # (OSError, WindowsError) ?
89 except: # (OSError, WindowsError) ?
90 # This is just a best effort, if we fail or the process was
90 # This is just a best effort, if we fail or the process was
91 # really gone, ignore it.
91 # really gone, ignore it.
92 pass
92 pass
93 else:
93 else:
94 for i in range(10):
94 for i in range(10):
95 if subp.poll() is None:
95 if subp.poll() is None:
96 time.sleep(0.1)
96 time.sleep(0.1)
97 else:
97 else:
98 break
98 break
99
99
100 if subp.poll() is None:
100 if subp.poll() is None:
101 # The process did not die...
101 # The process did not die...
102 print('... failed. Manual cleanup may be required.')
102 print('... failed. Manual cleanup may be required.')
103
103
104 def cleanup(self):
104 def cleanup(self):
105 "Kill process if it's still alive, and clean up temporary directories"
105 "Kill process if it's still alive, and clean up temporary directories"
106 self.cleanup_process()
106 self.cleanup_process()
107 for td in self.dirs:
107 for td in self.dirs:
108 td.cleanup()
108 td.cleanup()
109
109
110 __del__ = cleanup
110 __del__ = cleanup
111
111
112
112
113 class PyTestController(TestController):
113 class PyTestController(TestController):
114 """Run Python tests using IPython.testing.iptest"""
114 """Run Python tests using IPython.testing.iptest"""
115 #: str, Python command to execute in subprocess
115 #: str, Python command to execute in subprocess
116 pycmd = None
116 pycmd = None
117
117
118 def __init__(self, section, options):
118 def __init__(self, section, options):
119 """Create new test runner."""
119 """Create new test runner."""
120 TestController.__init__(self)
120 TestController.__init__(self)
121 self.section = section
121 self.section = section
122 # pycmd is put into cmd[2] in PyTestController.launch()
122 # pycmd is put into cmd[2] in PyTestController.launch()
123 self.cmd = [sys.executable, '-c', None, section]
123 self.cmd = [sys.executable, '-c', None, section]
124 self.pycmd = "from IPython.testing.iptest import run_iptest; run_iptest()"
124 self.pycmd = "from IPython.testing.iptest import run_iptest; run_iptest()"
125 self.options = options
125 self.options = options
126
126
127 def setup(self):
127 def setup(self):
128 ipydir = TemporaryDirectory()
128 ipydir = TemporaryDirectory()
129 self.dirs.append(ipydir)
129 self.dirs.append(ipydir)
130 self.env['IPYTHONDIR'] = ipydir.name
130 self.env['IPYTHONDIR'] = ipydir.name
131 self.workingdir = workingdir = TemporaryDirectory()
131 self.workingdir = workingdir = TemporaryDirectory()
132 self.dirs.append(workingdir)
132 self.dirs.append(workingdir)
133 self.env['IPTEST_WORKING_DIR'] = workingdir.name
133 self.env['IPTEST_WORKING_DIR'] = workingdir.name
134 # This means we won't get odd effects from our own matplotlib config
134 # This means we won't get odd effects from our own matplotlib config
135 self.env['MPLCONFIGDIR'] = workingdir.name
135 self.env['MPLCONFIGDIR'] = workingdir.name
136 # For security reasons (http://bugs.python.org/issue16202), use
136 # For security reasons (http://bugs.python.org/issue16202), use
137 # a temporary directory to which other users have no access.
137 # a temporary directory to which other users have no access.
138 self.env['TMPDIR'] = workingdir.name
138 self.env['TMPDIR'] = workingdir.name
139
139
140 # Add a non-accessible directory to PATH (see gh-7053)
140 # Add a non-accessible directory to PATH (see gh-7053)
141 noaccess = os.path.join(self.workingdir.name, "_no_access_")
141 noaccess = os.path.join(self.workingdir.name, "_no_access_")
142 self.noaccess = noaccess
142 self.noaccess = noaccess
143 os.mkdir(noaccess, 0)
143 os.mkdir(noaccess, 0)
144
144
145 PATH = os.environ.get('PATH', '')
145 PATH = os.environ.get('PATH', '')
146 if PATH:
146 if PATH:
147 PATH = noaccess + os.pathsep + PATH
147 PATH = noaccess + os.pathsep + PATH
148 else:
148 else:
149 PATH = noaccess
149 PATH = noaccess
150 self.env['PATH'] = PATH
150 self.env['PATH'] = PATH
151
151
152 # From options:
152 # From options:
153 if self.options.xunit:
153 if self.options.xunit:
154 self.add_xunit()
154 self.add_xunit()
155 if self.options.coverage:
155 if self.options.coverage:
156 self.add_coverage()
156 self.add_coverage()
157 self.env['IPTEST_SUBPROC_STREAMS'] = self.options.subproc_streams
157 self.env['IPTEST_SUBPROC_STREAMS'] = self.options.subproc_streams
158 self.cmd.extend(self.options.extra_args)
158 self.cmd.extend(self.options.extra_args)
159
159
160 def cleanup(self):
160 def cleanup(self):
161 """
161 """
162 Make the non-accessible directory created in setup() accessible
162 Make the non-accessible directory created in setup() accessible
163 again, otherwise deleting the workingdir will fail.
163 again, otherwise deleting the workingdir will fail.
164 """
164 """
165 os.chmod(self.noaccess, stat.S_IRWXU)
165 os.chmod(self.noaccess, stat.S_IRWXU)
166 TestController.cleanup(self)
166 TestController.cleanup(self)
167
167
168 @property
168 @property
169 def will_run(self):
169 def will_run(self):
170 try:
170 try:
171 return test_sections[self.section].will_run
171 return test_sections[self.section].will_run
172 except KeyError:
172 except KeyError:
173 return True
173 return True
174
174
175 def add_xunit(self):
175 def add_xunit(self):
176 xunit_file = os.path.abspath(self.section + '.xunit.xml')
176 xunit_file = os.path.abspath(self.section + '.xunit.xml')
177 self.cmd.extend(['--with-xunit', '--xunit-file', xunit_file])
177 self.cmd.extend(['--with-xunit', '--xunit-file', xunit_file])
178
178
179 def add_coverage(self):
179 def add_coverage(self):
180 try:
180 try:
181 sources = test_sections[self.section].includes
181 sources = test_sections[self.section].includes
182 except KeyError:
182 except KeyError:
183 sources = ['IPython']
183 sources = ['IPython']
184
184
185 coverage_rc = ("[run]\n"
185 coverage_rc = ("[run]\n"
186 "data_file = {data_file}\n"
186 "data_file = {data_file}\n"
187 "source =\n"
187 "source =\n"
188 " {source}\n"
188 " {source}\n"
189 ).format(data_file=os.path.abspath('.coverage.'+self.section),
189 ).format(data_file=os.path.abspath('.coverage.'+self.section),
190 source="\n ".join(sources))
190 source="\n ".join(sources))
191 config_file = os.path.join(self.workingdir.name, '.coveragerc')
191 config_file = os.path.join(self.workingdir.name, '.coveragerc')
192 with open(config_file, 'w') as f:
192 with open(config_file, 'w') as f:
193 f.write(coverage_rc)
193 f.write(coverage_rc)
194
194
195 self.env['COVERAGE_PROCESS_START'] = config_file
195 self.env['COVERAGE_PROCESS_START'] = config_file
196 self.pycmd = "import coverage; coverage.process_startup(); " + self.pycmd
196 self.pycmd = "import coverage; coverage.process_startup(); " + self.pycmd
197
197
198 def launch(self, buffer_output=False):
198 def launch(self, buffer_output=False):
199 self.cmd[2] = self.pycmd
199 self.cmd[2] = self.pycmd
200 super(PyTestController, self).launch(buffer_output=buffer_output)
200 super(PyTestController, self).launch(buffer_output=buffer_output)
201
201
202
202
203 def prepare_controllers(options):
203 def prepare_controllers(options):
204 """Returns two lists of TestController instances, those to run, and those
204 """Returns two lists of TestController instances, those to run, and those
205 not to run."""
205 not to run."""
206 testgroups = options.testgroups
206 testgroups = options.testgroups
207 if not testgroups:
207 if not testgroups:
208 testgroups = py_test_group_names
208 testgroups = py_test_group_names
209
209
210 controllers = [PyTestController(name, options) for name in testgroups]
210 controllers = [PyTestController(name, options) for name in testgroups]
211
211
212 to_run = [c for c in controllers if c.will_run]
212 to_run = [c for c in controllers if c.will_run]
213 not_run = [c for c in controllers if not c.will_run]
213 not_run = [c for c in controllers if not c.will_run]
214 return to_run, not_run
214 return to_run, not_run
215
215
216 def do_run(controller, buffer_output=True):
216 def do_run(controller, buffer_output=True):
217 """Setup and run a test controller.
217 """Setup and run a test controller.
218
218
219 If buffer_output is True, no output is displayed, to avoid it appearing
219 If buffer_output is True, no output is displayed, to avoid it appearing
220 interleaved. In this case, the caller is responsible for displaying test
220 interleaved. In this case, the caller is responsible for displaying test
221 output on failure.
221 output on failure.
222
222
223 Returns
223 Returns
224 -------
224 -------
225 controller : TestController
225 controller : TestController
226 The same controller as passed in, as a convenience for using map() type
226 The same controller as passed in, as a convenience for using map() type
227 APIs.
227 APIs.
228 exitcode : int
228 exitcode : int
229 The exit code of the test subprocess. Non-zero indicates failure.
229 The exit code of the test subprocess. Non-zero indicates failure.
230 """
230 """
231 try:
231 try:
232 try:
232 try:
233 controller.setup()
233 controller.setup()
234 controller.launch(buffer_output=buffer_output)
234 controller.launch(buffer_output=buffer_output)
235 except Exception:
235 except Exception:
236 import traceback
236 import traceback
237 traceback.print_exc()
237 traceback.print_exc()
238 return controller, 1 # signal failure
238 return controller, 1 # signal failure
239
239
240 exitcode = controller.wait()
240 exitcode = controller.wait()
241 return controller, exitcode
241 return controller, exitcode
242
242
243 except KeyboardInterrupt:
243 except KeyboardInterrupt:
244 return controller, -signal.SIGINT
244 return controller, -signal.SIGINT
245 finally:
245 finally:
246 controller.cleanup()
246 controller.cleanup()
247
247
248 def report():
248 def report():
249 """Return a string with a summary report of test-related variables."""
249 """Return a string with a summary report of test-related variables."""
250 inf = get_sys_info()
250 inf = get_sys_info()
251 out = []
251 out = []
252 def _add(name, value):
252 def _add(name, value):
253 out.append((name, value))
253 out.append((name, value))
254
254
255 _add('IPython version', inf['ipython_version'])
255 _add('IPython version', inf['ipython_version'])
256 _add('IPython commit', "{} ({})".format(inf['commit_hash'], inf['commit_source']))
256 _add('IPython commit', "{} ({})".format(inf['commit_hash'], inf['commit_source']))
257 _add('IPython package', compress_user(inf['ipython_path']))
257 _add('IPython package', compress_user(inf['ipython_path']))
258 _add('Python version', inf['sys_version'].replace('\n',''))
258 _add('Python version', inf['sys_version'].replace('\n',''))
259 _add('sys.executable', compress_user(inf['sys_executable']))
259 _add('sys.executable', compress_user(inf['sys_executable']))
260 _add('Platform', inf['platform'])
260 _add('Platform', inf['platform'])
261
261
262 width = max(len(n) for (n,v) in out)
262 width = max(len(n) for (n,v) in out)
263 out = ["{:<{width}}: {}\n".format(n, v, width=width) for (n,v) in out]
263 out = ["{:<{width}}: {}\n".format(n, v, width=width) for (n,v) in out]
264
264
265 avail = []
265 avail = []
266 not_avail = []
266 not_avail = []
267
267
268 for k, is_avail in have.items():
268 for k, is_avail in have.items():
269 if is_avail:
269 if is_avail:
270 avail.append(k)
270 avail.append(k)
271 else:
271 else:
272 not_avail.append(k)
272 not_avail.append(k)
273
273
274 if avail:
274 if avail:
275 out.append('\nTools and libraries available at test time:\n')
275 out.append('\nTools and libraries available at test time:\n')
276 avail.sort()
276 avail.sort()
277 out.append(' ' + ' '.join(avail)+'\n')
277 out.append(' ' + ' '.join(avail)+'\n')
278
278
279 if not_avail:
279 if not_avail:
280 out.append('\nTools and libraries NOT available at test time:\n')
280 out.append('\nTools and libraries NOT available at test time:\n')
281 not_avail.sort()
281 not_avail.sort()
282 out.append(' ' + ' '.join(not_avail)+'\n')
282 out.append(' ' + ' '.join(not_avail)+'\n')
283
283
284 return ''.join(out)
284 return ''.join(out)
285
285
286 def run_iptestall(options):
286 def run_iptestall(options):
287 """Run the entire IPython test suite by calling nose and trial.
287 """Run the entire IPython test suite by calling nose and trial.
288
288
289 This function constructs :class:`IPTester` instances for all IPython
289 This function constructs :class:`IPTester` instances for all IPython
290 modules and package and then runs each of them. This causes the modules
290 modules and package and then runs each of them. This causes the modules
291 and packages of IPython to be tested each in their own subprocess using
291 and packages of IPython to be tested each in their own subprocess using
292 nose.
292 nose.
293
293
294 Parameters
294 Parameters
295 ----------
295 ----------
296
296
297 All parameters are passed as attributes of the options object.
297 All parameters are passed as attributes of the options object.
298
298
299 testgroups : list of str
299 testgroups : list of str
300 Run only these sections of the test suite. If empty, run all the available
300 Run only these sections of the test suite. If empty, run all the available
301 sections.
301 sections.
302
302
303 fast : int or None
303 fast : int or None
304 Run the test suite in parallel, using n simultaneous processes. If None
304 Run the test suite in parallel, using n simultaneous processes. If None
305 is passed, one process is used per CPU core. Default 1 (i.e. sequential)
305 is passed, one process is used per CPU core. Default 1 (i.e. sequential)
306
306
307 inc_slow : bool
307 inc_slow : bool
308 Include slow tests. By default, these tests aren't run.
308 Include slow tests. By default, these tests aren't run.
309
309
310 url : unicode
310 url : unicode
311 Address:port to use when running the JS tests.
311 Address:port to use when running the JS tests.
312
312
313 xunit : bool
313 xunit : bool
314 Produce Xunit XML output. This is written to multiple foo.xunit.xml files.
314 Produce Xunit XML output. This is written to multiple foo.xunit.xml files.
315
315
316 coverage : bool or str
316 coverage : bool or str
317 Measure code coverage from tests. True will store the raw coverage data,
317 Measure code coverage from tests. True will store the raw coverage data,
318 or pass 'html' or 'xml' to get reports.
318 or pass 'html' or 'xml' to get reports.
319
319
320 extra_args : list
320 extra_args : list
321 Extra arguments to pass to the test subprocesses, e.g. '-v'
321 Extra arguments to pass to the test subprocesses, e.g. '-v'
322 """
322 """
323 to_run, not_run = prepare_controllers(options)
323 to_run, not_run = prepare_controllers(options)
324
324
325 def justify(ltext, rtext, width=70, fill='-'):
325 def justify(ltext, rtext, width=70, fill='-'):
326 ltext += ' '
326 ltext += ' '
327 rtext = (' ' + rtext).rjust(width - len(ltext), fill)
327 rtext = (' ' + rtext).rjust(width - len(ltext), fill)
328 return ltext + rtext
328 return ltext + rtext
329
329
330 # Run all test runners, tracking execution time
330 # Run all test runners, tracking execution time
331 failed = []
331 failed = []
332 t_start = time.time()
332 t_start = time.time()
333
333
334 print()
334 print()
335 if options.fast == 1:
335 if options.fast == 1:
336 # This actually means sequential, i.e. with 1 job
336 # This actually means sequential, i.e. with 1 job
337 for controller in to_run:
337 for controller in to_run:
338 print('Test group:', controller.section)
338 print('Test group:', controller.section)
339 sys.stdout.flush() # Show in correct order when output is piped
339 sys.stdout.flush() # Show in correct order when output is piped
340 controller, res = do_run(controller, buffer_output=False)
340 controller, res = do_run(controller, buffer_output=False)
341 if res:
341 if res:
342 failed.append(controller)
342 failed.append(controller)
343 if res == -signal.SIGINT:
343 if res == -signal.SIGINT:
344 print("Interrupted")
344 print("Interrupted")
345 break
345 break
346 print()
346 print()
347
347
348 else:
348 else:
349 # Run tests concurrently
349 # Run tests concurrently
350 try:
350 try:
351 pool = multiprocessing.pool.ThreadPool(options.fast)
351 pool = multiprocessing.pool.ThreadPool(options.fast)
352 for (controller, res) in pool.imap_unordered(do_run, to_run):
352 for (controller, res) in pool.imap_unordered(do_run, to_run):
353 res_string = 'OK' if res == 0 else 'FAILED'
353 res_string = 'OK' if res == 0 else 'FAILED'
354 print(justify('Test group: ' + controller.section, res_string))
354 print(justify('Test group: ' + controller.section, res_string))
355 if res:
355 if res:
356 print(decode(controller.stdout))
356 print(decode(controller.stdout))
357 failed.append(controller)
357 failed.append(controller)
358 if res == -signal.SIGINT:
358 if res == -signal.SIGINT:
359 print("Interrupted")
359 print("Interrupted")
360 break
360 break
361 except KeyboardInterrupt:
361 except KeyboardInterrupt:
362 return
362 return
363
363
364 for controller in not_run:
364 for controller in not_run:
365 print(justify('Test group: ' + controller.section, 'NOT RUN'))
365 print(justify('Test group: ' + controller.section, 'NOT RUN'))
366
366
367 t_end = time.time()
367 t_end = time.time()
368 t_tests = t_end - t_start
368 t_tests = t_end - t_start
369 nrunners = len(to_run)
369 nrunners = len(to_run)
370 nfail = len(failed)
370 nfail = len(failed)
371 # summarize results
371 # summarize results
372 print('_'*70)
372 print('_'*70)
373 print('Test suite completed for system with the following information:')
373 print('Test suite completed for system with the following information:')
374 print(report())
374 print(report())
375 took = "Took %.3fs." % t_tests
375 took = "Took %.3fs." % t_tests
376 print('Status: ', end='')
376 print('Status: ', end='')
377 if not failed:
377 if not failed:
378 print('OK (%d test groups).' % nrunners, took)
378 print('OK (%d test groups).' % nrunners, took)
379 else:
379 else:
380 # If anything went wrong, point out what command to rerun manually to
380 # If anything went wrong, point out what command to rerun manually to
381 # see the actual errors and individual summary
381 # see the actual errors and individual summary
382 failed_sections = [c.section for c in failed]
382 failed_sections = [c.section for c in failed]
383 print('ERROR - {} out of {} test groups failed ({}).'.format(nfail,
383 print('ERROR - {} out of {} test groups failed ({}).'.format(nfail,
384 nrunners, ', '.join(failed_sections)), took)
384 nrunners, ', '.join(failed_sections)), took)
385 print()
385 print()
386 print('You may wish to rerun these, with:')
386 print('You may wish to rerun these, with:')
387 print(' iptest', *failed_sections)
387 print(' iptest', *failed_sections)
388 print()
388 print()
389
389
390 if options.coverage:
390 if options.coverage:
391 from coverage import coverage, CoverageException
391 from coverage import coverage, CoverageException
392 cov = coverage(data_file='.coverage')
392 cov = coverage(data_file='.coverage')
393 cov.combine()
393 cov.combine()
394 cov.save()
394 cov.save()
395
395
396 # Coverage HTML report
396 # Coverage HTML report
397 if options.coverage == 'html':
397 if options.coverage == 'html':
398 html_dir = 'ipy_htmlcov'
398 html_dir = 'ipy_htmlcov'
399 shutil.rmtree(html_dir, ignore_errors=True)
399 shutil.rmtree(html_dir, ignore_errors=True)
400 print("Writing HTML coverage report to %s/ ... " % html_dir, end="")
400 print("Writing HTML coverage report to %s/ ... " % html_dir, end="")
401 sys.stdout.flush()
401 sys.stdout.flush()
402
402
403 # Custom HTML reporter to clean up module names.
403 # Custom HTML reporter to clean up module names.
404 from coverage.html import HtmlReporter
404 from coverage.html import HtmlReporter
405 class CustomHtmlReporter(HtmlReporter):
405 class CustomHtmlReporter(HtmlReporter):
406 def find_code_units(self, morfs):
406 def find_code_units(self, morfs):
407 super(CustomHtmlReporter, self).find_code_units(morfs)
407 super(CustomHtmlReporter, self).find_code_units(morfs)
408 for cu in self.code_units:
408 for cu in self.code_units:
409 nameparts = cu.name.split(os.sep)
409 nameparts = cu.name.split(os.sep)
410 if 'IPython' not in nameparts:
410 if 'IPython' not in nameparts:
411 continue
411 continue
412 ix = nameparts.index('IPython')
412 ix = nameparts.index('IPython')
413 cu.name = '.'.join(nameparts[ix:])
413 cu.name = '.'.join(nameparts[ix:])
414
414
415 # Reimplement the html_report method with our custom reporter
415 # Reimplement the html_report method with our custom reporter
416 cov.get_data()
416 cov.get_data()
417 cov.config.from_args(omit='*{0}tests{0}*'.format(os.sep), html_dir=html_dir,
417 cov.config.from_args(omit='*{0}tests{0}*'.format(os.sep), html_dir=html_dir,
418 html_title='IPython test coverage',
418 html_title='IPython test coverage',
419 )
419 )
420 reporter = CustomHtmlReporter(cov, cov.config)
420 reporter = CustomHtmlReporter(cov, cov.config)
421 reporter.report(None)
421 reporter.report(None)
422 print('done.')
422 print('done.')
423
423
424 # Coverage XML report
424 # Coverage XML report
425 elif options.coverage == 'xml':
425 elif options.coverage == 'xml':
426 try:
426 try:
427 cov.xml_report(outfile='ipy_coverage.xml')
427 cov.xml_report(outfile='ipy_coverage.xml')
428 except CoverageException as e:
428 except CoverageException as e:
429 print('Generating coverage report failed. Are you running javascript tests only?')
429 print('Generating coverage report failed. Are you running javascript tests only?')
430 import traceback
430 import traceback
431 traceback.print_exc()
431 traceback.print_exc()
432
432
433 if failed:
433 if failed:
434 # Ensure that our exit code indicates failure
434 # Ensure that our exit code indicates failure
435 sys.exit(1)
435 sys.exit(1)
436
436
437 argparser = argparse.ArgumentParser(description='Run IPython test suite')
437 argparser = argparse.ArgumentParser(description='Run IPython test suite')
438 argparser.add_argument('testgroups', nargs='*',
438 argparser.add_argument('testgroups', nargs='*',
439 help='Run specified groups of tests. If omitted, run '
439 help='Run specified groups of tests. If omitted, run '
440 'all tests.')
440 'all tests.')
441 argparser.add_argument('--all', action='store_true',
441 argparser.add_argument('--all', action='store_true',
442 help='Include slow tests not run by default.')
442 help='Include slow tests not run by default.')
443 argparser.add_argument('--url', help="URL to use for the JS tests.")
444 argparser.add_argument('-j', '--fast', nargs='?', const=None, default=1, type=int,
443 argparser.add_argument('-j', '--fast', nargs='?', const=None, default=1, type=int,
445 help='Run test sections in parallel. This starts as many '
444 help='Run test sections in parallel. This starts as many '
446 'processes as you have cores, or you can specify a number.')
445 'processes as you have cores, or you can specify a number.')
447 argparser.add_argument('--xunit', action='store_true',
446 argparser.add_argument('--xunit', action='store_true',
448 help='Produce Xunit XML results')
447 help='Produce Xunit XML results')
449 argparser.add_argument('--coverage', nargs='?', const=True, default=False,
448 argparser.add_argument('--coverage', nargs='?', const=True, default=False,
450 help="Measure test coverage. Specify 'html' or "
449 help="Measure test coverage. Specify 'html' or "
451 "'xml' to get reports.")
450 "'xml' to get reports.")
452 argparser.add_argument('--subproc-streams', default='capture',
451 argparser.add_argument('--subproc-streams', default='capture',
453 help="What to do with stdout/stderr from subprocesses. "
452 help="What to do with stdout/stderr from subprocesses. "
454 "'capture' (default), 'show' and 'discard' are the options.")
453 "'capture' (default), 'show' and 'discard' are the options.")
455
454
456 def default_options():
455 def default_options():
457 """Get an argparse Namespace object with the default arguments, to pass to
456 """Get an argparse Namespace object with the default arguments, to pass to
458 :func:`run_iptestall`.
457 :func:`run_iptestall`.
459 """
458 """
460 options = argparser.parse_args([])
459 options = argparser.parse_args([])
461 options.extra_args = []
460 options.extra_args = []
462 return options
461 return options
463
462
464 def main():
463 def main():
465 # iptest doesn't work correctly if the working directory is the
464 # iptest doesn't work correctly if the working directory is the
466 # root of the IPython source tree. Tell the user to avoid
465 # root of the IPython source tree. Tell the user to avoid
467 # frustration.
466 # frustration.
468 if os.path.exists(os.path.join(os.getcwd(),
467 if os.path.exists(os.path.join(os.getcwd(),
469 'IPython', 'testing', '__main__.py')):
468 'IPython', 'testing', '__main__.py')):
470 print("Don't run iptest from the IPython source directory",
469 print("Don't run iptest from the IPython source directory",
471 file=sys.stderr)
470 file=sys.stderr)
472 sys.exit(1)
471 sys.exit(1)
473 # Arguments after -- should be passed through to nose. Argparse treats
472 # Arguments after -- should be passed through to nose. Argparse treats
474 # everything after -- as regular positional arguments, so we separate them
473 # everything after -- as regular positional arguments, so we separate them
475 # first.
474 # first.
476 try:
475 try:
477 ix = sys.argv.index('--')
476 ix = sys.argv.index('--')
478 except ValueError:
477 except ValueError:
479 to_parse = sys.argv[1:]
478 to_parse = sys.argv[1:]
480 extra_args = []
479 extra_args = []
481 else:
480 else:
482 to_parse = sys.argv[1:ix]
481 to_parse = sys.argv[1:ix]
483 extra_args = sys.argv[ix+1:]
482 extra_args = sys.argv[ix+1:]
484
483
485 options = argparser.parse_args(to_parse)
484 options = argparser.parse_args(to_parse)
486 options.extra_args = extra_args
485 options.extra_args = extra_args
487
486
488 run_iptestall(options)
487 run_iptestall(options)
489
488
490
489
491 if __name__ == '__main__':
490 if __name__ == '__main__':
492 main()
491 main()
@@ -1,177 +1,178 b''
1 """Experimental code for cleaner support of IPython syntax with unittest.
1 """Experimental code for cleaner support of IPython syntax with unittest.
2
2
3 In IPython up until 0.10, we've used very hacked up nose machinery for running
3 In IPython up until 0.10, we've used very hacked up nose machinery for running
4 tests with IPython special syntax, and this has proved to be extremely slow.
4 tests with IPython special syntax, and this has proved to be extremely slow.
5 This module provides decorators to try a different approach, stemming from a
5 This module provides decorators to try a different approach, stemming from a
6 conversation Brian and I (FP) had about this problem Sept/09.
6 conversation Brian and I (FP) had about this problem Sept/09.
7
7
8 The goal is to be able to easily write simple functions that can be seen by
8 The goal is to be able to easily write simple functions that can be seen by
9 unittest as tests, and ultimately for these to support doctests with full
9 unittest as tests, and ultimately for these to support doctests with full
10 IPython syntax. Nose already offers this based on naming conventions and our
10 IPython syntax. Nose already offers this based on naming conventions and our
11 hackish plugins, but we are seeking to move away from nose dependencies if
11 hackish plugins, but we are seeking to move away from nose dependencies if
12 possible.
12 possible.
13
13
14 This module follows a different approach, based on decorators.
14 This module follows a different approach, based on decorators.
15
15
16 - A decorator called @ipdoctest can mark any function as having a docstring
16 - A decorator called @ipdoctest can mark any function as having a docstring
17 that should be viewed as a doctest, but after syntax conversion.
17 that should be viewed as a doctest, but after syntax conversion.
18
18
19 Authors
19 Authors
20 -------
20 -------
21
21
22 - Fernando Perez <Fernando.Perez@berkeley.edu>
22 - Fernando Perez <Fernando.Perez@berkeley.edu>
23 """
23 """
24
24
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Copyright (C) 2009-2011 The IPython Development Team
27 # Copyright (C) 2009-2011 The IPython Development Team
28 #
28 #
29 # Distributed under the terms of the BSD License. The full license is in
29 # Distributed under the terms of the BSD License. The full license is in
30 # the file COPYING, distributed as part of this software.
30 # the file COPYING, distributed as part of this software.
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34 # Imports
34 # Imports
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36
36
37 # Stdlib
37 # Stdlib
38 import re
38 import re
39 import unittest
39 import unittest
40 from doctest import DocTestFinder, DocTestRunner, TestResults
40 from doctest import DocTestFinder, DocTestRunner, TestResults
41 from IPython.terminal.interactiveshell import InteractiveShell
41
42
42 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
43 # Classes and functions
44 # Classes and functions
44 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
45
46
46 def count_failures(runner):
47 def count_failures(runner):
47 """Count number of failures in a doctest runner.
48 """Count number of failures in a doctest runner.
48
49
49 Code modeled after the summarize() method in doctest.
50 Code modeled after the summarize() method in doctest.
50 """
51 """
51 return [TestResults(f, t) for f, t in runner._name2ft.values() if f > 0 ]
52 return [TestResults(f, t) for f, t in runner._name2ft.values() if f > 0 ]
52
53
53
54
54 class IPython2PythonConverter(object):
55 class IPython2PythonConverter(object):
55 """Convert IPython 'syntax' to valid Python.
56 """Convert IPython 'syntax' to valid Python.
56
57
57 Eventually this code may grow to be the full IPython syntax conversion
58 Eventually this code may grow to be the full IPython syntax conversion
58 implementation, but for now it only does prompt conversion."""
59 implementation, but for now it only does prompt conversion."""
59
60
60 def __init__(self):
61 def __init__(self):
61 self.rps1 = re.compile(r'In\ \[\d+\]: ')
62 self.rps1 = re.compile(r'In\ \[\d+\]: ')
62 self.rps2 = re.compile(r'\ \ \ \.\.\.+: ')
63 self.rps2 = re.compile(r'\ \ \ \.\.\.+: ')
63 self.rout = re.compile(r'Out\[\d+\]: \s*?\n?')
64 self.rout = re.compile(r'Out\[\d+\]: \s*?\n?')
64 self.pyps1 = '>>> '
65 self.pyps1 = '>>> '
65 self.pyps2 = '... '
66 self.pyps2 = '... '
66 self.rpyps1 = re.compile (r'(\s*%s)(.*)$' % self.pyps1)
67 self.rpyps1 = re.compile (r'(\s*%s)(.*)$' % self.pyps1)
67 self.rpyps2 = re.compile (r'(\s*%s)(.*)$' % self.pyps2)
68 self.rpyps2 = re.compile (r'(\s*%s)(.*)$' % self.pyps2)
68
69
69 def __call__(self, ds):
70 def __call__(self, ds):
70 """Convert IPython prompts to python ones in a string."""
71 """Convert IPython prompts to python ones in a string."""
71 from . import globalipapp
72 from . import globalipapp
72
73
73 pyps1 = '>>> '
74 pyps1 = '>>> '
74 pyps2 = '... '
75 pyps2 = '... '
75 pyout = ''
76 pyout = ''
76
77
77 dnew = ds
78 dnew = ds
78 dnew = self.rps1.sub(pyps1, dnew)
79 dnew = self.rps1.sub(pyps1, dnew)
79 dnew = self.rps2.sub(pyps2, dnew)
80 dnew = self.rps2.sub(pyps2, dnew)
80 dnew = self.rout.sub(pyout, dnew)
81 dnew = self.rout.sub(pyout, dnew)
81 ip = globalipapp.get_ipython()
82 ip = InteractiveShell.instance()
82
83
83 # Convert input IPython source into valid Python.
84 # Convert input IPython source into valid Python.
84 out = []
85 out = []
85 newline = out.append
86 newline = out.append
86 for line in dnew.splitlines():
87 for line in dnew.splitlines():
87
88
88 mps1 = self.rpyps1.match(line)
89 mps1 = self.rpyps1.match(line)
89 if mps1 is not None:
90 if mps1 is not None:
90 prompt, text = mps1.groups()
91 prompt, text = mps1.groups()
91 newline(prompt+ip.prefilter(text, False))
92 newline(prompt+ip.prefilter(text, False))
92 continue
93 continue
93
94
94 mps2 = self.rpyps2.match(line)
95 mps2 = self.rpyps2.match(line)
95 if mps2 is not None:
96 if mps2 is not None:
96 prompt, text = mps2.groups()
97 prompt, text = mps2.groups()
97 newline(prompt+ip.prefilter(text, True))
98 newline(prompt+ip.prefilter(text, True))
98 continue
99 continue
99
100
100 newline(line)
101 newline(line)
101 newline('') # ensure a closing newline, needed by doctest
102 newline('') # ensure a closing newline, needed by doctest
102 #print "PYSRC:", '\n'.join(out) # dbg
103 #print "PYSRC:", '\n'.join(out) # dbg
103 return '\n'.join(out)
104 return '\n'.join(out)
104
105
105 #return dnew
106 #return dnew
106
107
107
108
108 class Doc2UnitTester(object):
109 class Doc2UnitTester(object):
109 """Class whose instances act as a decorator for docstring testing.
110 """Class whose instances act as a decorator for docstring testing.
110
111
111 In practice we're only likely to need one instance ever, made below (though
112 In practice we're only likely to need one instance ever, made below (though
112 no attempt is made at turning it into a singleton, there is no need for
113 no attempt is made at turning it into a singleton, there is no need for
113 that).
114 that).
114 """
115 """
115 def __init__(self, verbose=False):
116 def __init__(self, verbose=False):
116 """New decorator.
117 """New decorator.
117
118
118 Parameters
119 Parameters
119 ----------
120 ----------
120
121
121 verbose : boolean, optional (False)
122 verbose : boolean, optional (False)
122 Passed to the doctest finder and runner to control verbosity.
123 Passed to the doctest finder and runner to control verbosity.
123 """
124 """
124 self.verbose = verbose
125 self.verbose = verbose
125 # We can reuse the same finder for all instances
126 # We can reuse the same finder for all instances
126 self.finder = DocTestFinder(verbose=verbose, recurse=False)
127 self.finder = DocTestFinder(verbose=verbose, recurse=False)
127
128
128 def __call__(self, func):
129 def __call__(self, func):
129 """Use as a decorator: doctest a function's docstring as a unittest.
130 """Use as a decorator: doctest a function's docstring as a unittest.
130
131
131 This version runs normal doctests, but the idea is to make it later run
132 This version runs normal doctests, but the idea is to make it later run
132 ipython syntax instead."""
133 ipython syntax instead."""
133
134
134 # Capture the enclosing instance with a different name, so the new
135 # Capture the enclosing instance with a different name, so the new
135 # class below can see it without confusion regarding its own 'self'
136 # class below can see it without confusion regarding its own 'self'
136 # that will point to the test instance at runtime
137 # that will point to the test instance at runtime
137 d2u = self
138 d2u = self
138
139
139 # Rewrite the function's docstring to have python syntax
140 # Rewrite the function's docstring to have python syntax
140 if func.__doc__ is not None:
141 if func.__doc__ is not None:
141 func.__doc__ = ip2py(func.__doc__)
142 func.__doc__ = ip2py(func.__doc__)
142
143
143 # Now, create a tester object that is a real unittest instance, so
144 # Now, create a tester object that is a real unittest instance, so
144 # normal unittest machinery (or Nose, or Trial) can find it.
145 # normal unittest machinery (or Nose, or Trial) can find it.
145 class Tester(unittest.TestCase):
146 class Tester(unittest.TestCase):
146 def test(self):
147 def test(self):
147 # Make a new runner per function to be tested
148 # Make a new runner per function to be tested
148 runner = DocTestRunner(verbose=d2u.verbose)
149 runner = DocTestRunner(verbose=d2u.verbose)
149 for the_test in d2u.finder.find(func, func.__name__):
150 for the_test in d2u.finder.find(func, func.__name__):
150 runner.run(the_test)
151 runner.run(the_test)
151 failed = count_failures(runner)
152 failed = count_failures(runner)
152 if failed:
153 if failed:
153 # Since we only looked at a single function's docstring,
154 # Since we only looked at a single function's docstring,
154 # failed should contain at most one item. More than that
155 # failed should contain at most one item. More than that
155 # is a case we can't handle and should error out on
156 # is a case we can't handle and should error out on
156 if len(failed) > 1:
157 if len(failed) > 1:
157 err = "Invalid number of test results:" % failed
158 err = "Invalid number of test results:" % failed
158 raise ValueError(err)
159 raise ValueError(err)
159 # Report a normal failure.
160 # Report a normal failure.
160 self.fail('failed doctests: %s' % str(failed[0]))
161 self.fail('failed doctests: %s' % str(failed[0]))
161
162
162 # Rename it so test reports have the original signature.
163 # Rename it so test reports have the original signature.
163 Tester.__name__ = func.__name__
164 Tester.__name__ = func.__name__
164 return Tester
165 return Tester
165
166
166
167
167 def ipdocstring(func):
168 def ipdocstring(func):
168 """Change the function docstring via ip2py.
169 """Change the function docstring via ip2py.
169 """
170 """
170 if func.__doc__ is not None:
171 if func.__doc__ is not None:
171 func.__doc__ = ip2py(func.__doc__)
172 func.__doc__ = ip2py(func.__doc__)
172 return func
173 return func
173
174
174
175
175 # Make an instance of the classes for public use
176 # Make an instance of the classes for public use
176 ipdoctest = Doc2UnitTester()
177 ipdoctest = Doc2UnitTester()
177 ip2py = IPython2PythonConverter()
178 ip2py = IPython2PythonConverter()
@@ -1,42 +1,43 b''
1 include README.rst
1 include README.rst
2 include COPYING.rst
2 include COPYING.rst
3 include LICENSE
3 include LICENSE
4 include setupbase.py
4 include setupbase.py
5 include setupegg.py
5 include setupegg.py
6 include MANIFEST.in
6 include MANIFEST.in
7 include pytest.ini
7 include .mailmap
8 include .mailmap
8
9
9 recursive-exclude tools *
10 recursive-exclude tools *
10 exclude tools
11 exclude tools
11 exclude CONTRIBUTING.md
12 exclude CONTRIBUTING.md
12 exclude .editorconfig
13 exclude .editorconfig
13
14
14 graft setupext
15 graft setupext
15
16
16 graft scripts
17 graft scripts
17
18
18 # Load main dir but exclude things we don't want in the distro
19 # Load main dir but exclude things we don't want in the distro
19 graft IPython
20 graft IPython
20
21
21 # Documentation
22 # Documentation
22 graft docs
23 graft docs
23 exclude docs/\#*
24 exclude docs/\#*
24 exclude docs/man/*.1.gz
25 exclude docs/man/*.1.gz
25
26
26 # Examples
27 # Examples
27 graft examples
28 graft examples
28
29
29 # docs subdirs we want to skip
30 # docs subdirs we want to skip
30 prune docs/build
31 prune docs/build
31 prune docs/gh-pages
32 prune docs/gh-pages
32 prune docs/dist
33 prune docs/dist
33
34
34 # Patterns to exclude from any directory
35 # Patterns to exclude from any directory
35 global-exclude *~
36 global-exclude *~
36 global-exclude *.flc
37 global-exclude *.flc
37 global-exclude *.yml
38 global-exclude *.yml
38 global-exclude *.pyc
39 global-exclude *.pyc
39 global-exclude *.pyo
40 global-exclude *.pyo
40 global-exclude .dircopy.log
41 global-exclude .dircopy.log
41 global-exclude .git
42 global-exclude .git
42 global-exclude .ipynb_checkpoints
43 global-exclude .ipynb_checkpoints
General Comments 0
You need to be logged in to leave comments. Login now