##// END OF EJS Templates
Remove yield test that are not support by pytest anymore...
Matthias Bussonnier -
Show More
@@ -1,113 +1,113 b''
1 1 # http://travis-ci.org/#!/ipython/ipython
2 2 language: python
3 3 os: linux
4 4
5 5 addons:
6 6 apt:
7 7 packages:
8 8 - graphviz
9 9
10 10 python:
11 11 - 3.8
12 12
13 13 env:
14 14 global:
15 15 - PATH=$TRAVIS_BUILD_DIR/pandoc:$PATH
16 16
17 17 group: edge
18 18
19 19 before_install:
20 20 - |
21 21 # install Python on macOS
22 22 if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
23 23 env | sort
24 24 if ! which python$TRAVIS_PYTHON_VERSION; then
25 25 HOMEBREW_NO_AUTO_UPDATE=1 brew tap minrk/homebrew-python-frameworks
26 26 HOMEBREW_NO_AUTO_UPDATE=1 brew cask install python-framework-${TRAVIS_PYTHON_VERSION/./}
27 27 fi
28 28 python3 -m pip install virtualenv
29 29 python3 -m virtualenv -p $(which python$TRAVIS_PYTHON_VERSION) ~/travis-env
30 30 source ~/travis-env/bin/activate
31 31 fi
32 32 - python --version
33 33
34 34 install:
35 35 - pip install pip --upgrade
36 36 - pip install setuptools --upgrade
37 37 - if [[ "$TRAVIS_PYTHON_VERSION" == "3.6" ]]; then
38 38 echo "for the time being still test on 3.6";
39 39 sed -ibkp s/7/6/g setup.py;
40 40 git diff;
41 41 fi
42 42 - pip install -e file://$PWD#egg=ipython[test] --upgrade
43 43 - pip install trio curio --upgrade --upgrade-strategy eager
44 - pip install 'pytest<6' 'matplotlib !=3.2.0'
44 - pip install 'pytest' 'matplotlib !=3.2.0'
45 45 - pip install codecov check-manifest pytest-cov --upgrade
46 46
47 47
48 48 script:
49 49 - check-manifest
50 50 - |
51 51 if [[ "$TRAVIS_PYTHON_VERSION" == "nightly" ]]; then
52 52 # on nightly fake parso known the grammar
53 53 cp /home/travis/virtualenv/python3.9-dev/lib/python3.9/site-packages/parso/python/grammar38.txt /home/travis/virtualenv/python3.9-dev/lib/python3.9/site-packages/parso/python/grammar39.txt
54 54 fi
55 55 - |
56 56 if [[ "$TRAVIS_PYTHON_VERSION" == "3.8" ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
57 57 cd /tmp && iptest --coverage xml && cd -
58 58 fi
59 59 - pytest --maxfail=10 IPython
60 60 # On the latest Python (on Linux) only, make sure that the docs build.
61 61 - |
62 62 if [[ "$TRAVIS_PYTHON_VERSION" == "3.8" ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
63 63 pip install -r docs/requirements.txt
64 64 python tools/fixup_whats_new_pr.py
65 65 make -C docs/ html SPHINXOPTS="-W"
66 66 fi
67 67
68 68 after_success:
69 69 - cp /tmp/ipy_coverage.xml ./
70 70 - cp /tmp/.coverage ./
71 71 - codecov
72 72
73 73 matrix:
74 74 include:
75 75 - arch: amd64
76 76 python: "3.6"
77 77 dist: xenial
78 78 - arch: amd64
79 79 python: "3.7"
80 80 dist: xenial
81 81 - arch: amd64
82 82 python: "3.8"
83 83 dist: xenial
84 84 - arch: amd64
85 85 python: "nightly"
86 86 dist: xenial
87 87 - arch: amd64
88 88 python: "3.9-dev"
89 89 - os: osx
90 90 language: generic
91 91 python: 3.7
92 92 env: TRAVIS_PYTHON_VERSION=3.7
93 93 allow_failures:
94 94 - python: nightly
95 95
96 96 before_deploy:
97 97 - rm -rf dist/
98 98 - python setup.py sdist
99 99 - python setup.py bdist_wheel
100 100
101 101 deploy:
102 102 provider: releases
103 103 api_key:
104 104 secure: Y/Ae9tYs5aoBU8bDjN2YrwGG6tCbezj/h3Lcmtx8HQavSbBgXnhnZVRb2snOKD7auqnqjfT/7QMm4ZyKvaOEgyggGktKqEKYHC8KOZ7yp8I5/UMDtk6j9TnXpSqqBxPiud4MDV76SfRYEQiaDoG4tGGvSfPJ9KcNjKrNvSyyxns=
105 105 file: dist/*
106 106 file_glob: true
107 107 cleanup: false
108 108 on:
109 109 repo: ipython/ipython
110 110 all_branches: true # Backports are released from e.g. 5.x branch
111 111 tags: true
112 112 python: 3.6 # Any version should work, but we only need one
113 113 condition: $TRAVIS_OS_NAME = "linux"
@@ -1,222 +1,222 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tools for handling LaTeX."""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7 from io import BytesIO, open
8 8 import os
9 9 import tempfile
10 10 import shutil
11 11 import subprocess
12 12 from base64 import encodebytes
13 13 import textwrap
14 14
15 15 from pathlib import Path, PurePath
16 16
17 17 from IPython.utils.process import find_cmd, FindCmdError
18 18 from traitlets.config import get_config
19 19 from traitlets.config.configurable import SingletonConfigurable
20 20 from traitlets import List, Bool, Unicode
21 21 from IPython.utils.py3compat import cast_unicode
22 22
23 23
24 24 class LaTeXTool(SingletonConfigurable):
25 25 """An object to store configuration of the LaTeX tool."""
26 26 def _config_default(self):
27 27 return get_config()
28 28
29 29 backends = List(
30 30 Unicode(), ["matplotlib", "dvipng"],
31 31 help="Preferred backend to draw LaTeX math equations. "
32 32 "Backends in the list are checked one by one and the first "
33 33 "usable one is used. Note that `matplotlib` backend "
34 34 "is usable only for inline style equations. To draw "
35 35 "display style equations, `dvipng` backend must be specified. ",
36 36 # It is a List instead of Enum, to make configuration more
37 37 # flexible. For example, to use matplotlib mainly but dvipng
38 38 # for display style, the default ["matplotlib", "dvipng"] can
39 39 # be used. To NOT use dvipng so that other repr such as
40 40 # unicode pretty printing is used, you can use ["matplotlib"].
41 41 ).tag(config=True)
42 42
43 43 use_breqn = Bool(
44 44 True,
45 45 help="Use breqn.sty to automatically break long equations. "
46 46 "This configuration takes effect only for dvipng backend.",
47 47 ).tag(config=True)
48 48
49 49 packages = List(
50 50 ['amsmath', 'amsthm', 'amssymb', 'bm'],
51 51 help="A list of packages to use for dvipng backend. "
52 52 "'breqn' will be automatically appended when use_breqn=True.",
53 53 ).tag(config=True)
54 54
55 55 preamble = Unicode(
56 56 help="Additional preamble to use when generating LaTeX source "
57 57 "for dvipng backend.",
58 58 ).tag(config=True)
59 59
60 60
61 61 def latex_to_png(s, encode=False, backend=None, wrap=False, color='Black',
62 62 scale=1.0):
63 63 """Render a LaTeX string to PNG.
64 64
65 65 Parameters
66 66 ----------
67 67 s : str
68 68 The raw string containing valid inline LaTeX.
69 69 encode : bool, optional
70 70 Should the PNG data base64 encoded to make it JSON'able.
71 71 backend : {matplotlib, dvipng}
72 72 Backend for producing PNG data.
73 73 wrap : bool
74 74 If true, Automatically wrap `s` as a LaTeX equation.
75 75 color : string
76 76 Foreground color name among dvipsnames, e.g. 'Maroon' or on hex RGB
77 77 format, e.g. '#AA20FA'.
78 78 scale : float
79 79 Scale factor for the resulting PNG.
80 80
81 81 None is returned when the backend cannot be used.
82 82
83 83 """
84 84 s = cast_unicode(s)
85 85 allowed_backends = LaTeXTool.instance().backends
86 86 if backend is None:
87 87 backend = allowed_backends[0]
88 88 if backend not in allowed_backends:
89 89 return None
90 90 if backend == 'matplotlib':
91 91 f = latex_to_png_mpl
92 92 elif backend == 'dvipng':
93 93 f = latex_to_png_dvipng
94 94 if color.startswith('#'):
95 95 # Convert hex RGB color to LaTeX RGB color.
96 96 if len(color) == 7:
97 97 try:
98 98 color = "RGB {}".format(" ".join([str(int(x, 16)) for x in
99 99 textwrap.wrap(color[1:], 2)]))
100 100 except ValueError as e:
101 101 raise ValueError('Invalid color specification {}.'.format(color)) from e
102 102 else:
103 103 raise ValueError('Invalid color specification {}.'.format(color))
104 104 else:
105 105 raise ValueError('No such backend {0}'.format(backend))
106 106 bin_data = f(s, wrap, color, scale)
107 107 if encode and bin_data:
108 108 bin_data = encodebytes(bin_data)
109 109 return bin_data
110 110
111 111
112 112 def latex_to_png_mpl(s, wrap, color='Black', scale=1.0):
113 113 try:
114 114 from matplotlib import mathtext
115 115 from pyparsing import ParseFatalException
116 116 except ImportError:
117 117 return None
118 118
119 119 # mpl mathtext doesn't support display math, force inline
120 120 s = s.replace('$$', '$')
121 121 if wrap:
122 122 s = u'${0}$'.format(s)
123 123
124 124 try:
125 125 mt = mathtext.MathTextParser('bitmap')
126 126 f = BytesIO()
127 127 dpi = 120*scale
128 128 mt.to_png(f, s, fontsize=12, dpi=dpi, color=color)
129 129 return f.getvalue()
130 130 except (ValueError, RuntimeError, ParseFatalException):
131 131 return None
132 132
133 133
134 134 def latex_to_png_dvipng(s, wrap, color='Black', scale=1.0):
135 135 try:
136 136 find_cmd('latex')
137 137 find_cmd('dvipng')
138 138 except FindCmdError:
139 139 return None
140 140 try:
141 workdir = PurePath(tempfile.mkdtemp())
141 workdir = Path(tempfile.mkdtemp())
142 142 tmpfile = workdir.joinpath("tmp.tex")
143 143 dvifile = workdir.joinpath("tmp.dvi")
144 144 outfile = workdir.joinpath("tmp.png")
145 145
146 146 with tmpfile.open("w", encoding="utf8") as f:
147 147 f.writelines(genelatex(s, wrap))
148 148
149 149 with open(os.devnull, 'wb') as devnull:
150 150 subprocess.check_call(
151 151 ["latex", "-halt-on-error", "-interaction", "batchmode", tmpfile],
152 152 cwd=workdir, stdout=devnull, stderr=devnull)
153 153
154 154 resolution = round(150*scale)
155 155 subprocess.check_call(
156 156 ["dvipng", "-T", "tight", "-D", str(resolution), "-z", "9",
157 157 "-bg", "transparent", "-o", outfile, dvifile, "-fg", color],
158 158 cwd=workdir, stdout=devnull, stderr=devnull)
159 159
160 160 with outfile.open("rb") as f:
161 161 return f.read()
162 162 except subprocess.CalledProcessError:
163 163 return None
164 164 finally:
165 165 shutil.rmtree(workdir)
166 166
167 167
168 168 def kpsewhich(filename):
169 169 """Invoke kpsewhich command with an argument `filename`."""
170 170 try:
171 171 find_cmd("kpsewhich")
172 172 proc = subprocess.Popen(
173 173 ["kpsewhich", filename],
174 174 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
175 175 (stdout, stderr) = proc.communicate()
176 176 return stdout.strip().decode('utf8', 'replace')
177 177 except FindCmdError:
178 178 pass
179 179
180 180
181 181 def genelatex(body, wrap):
182 182 """Generate LaTeX document for dvipng backend."""
183 183 lt = LaTeXTool.instance()
184 184 breqn = wrap and lt.use_breqn and kpsewhich("breqn.sty")
185 185 yield r'\documentclass{article}'
186 186 packages = lt.packages
187 187 if breqn:
188 188 packages = packages + ['breqn']
189 189 for pack in packages:
190 190 yield r'\usepackage{{{0}}}'.format(pack)
191 191 yield r'\pagestyle{empty}'
192 192 if lt.preamble:
193 193 yield lt.preamble
194 194 yield r'\begin{document}'
195 195 if breqn:
196 196 yield r'\begin{dmath*}'
197 197 yield body
198 198 yield r'\end{dmath*}'
199 199 elif wrap:
200 200 yield u'$${0}$$'.format(body)
201 201 else:
202 202 yield body
203 203 yield u'\\end{document}'
204 204
205 205
206 206 _data_uri_template_png = u"""<img src="data:image/png;base64,%s" alt=%s />"""
207 207
208 208 def latex_to_html(s, alt='image'):
209 209 """Render LaTeX to HTML with embedded PNG data using data URIs.
210 210
211 211 Parameters
212 212 ----------
213 213 s : str
214 214 The raw string containing valid inline LateX.
215 215 alt : str
216 216 The alt text to use for the HTML.
217 217 """
218 218 base64_data = latex_to_png(s, encode=True).decode('ascii')
219 219 if base64_data:
220 220 return _data_uri_template_png % (base64_data, alt)
221 221
222 222
@@ -1,183 +1,191 b''
1 1 """Tests for IPython.utils.path.py"""
2 2 # Copyright (c) IPython Development Team.
3 3 # Distributed under the terms of the Modified BSD License.
4 4
5 5 from contextlib import contextmanager
6 6 from unittest.mock import patch
7 7
8 8 import nose.tools as nt
9 9 import pytest
10 10
11 11 from IPython.lib import latextools
12 from IPython.testing.decorators import onlyif_cmds_exist, skipif_not_matplotlib
12 from IPython.testing.decorators import (
13 onlyif_cmds_exist,
14 skipif_not_matplotlib,
15 skip_iptest_but_not_pytest,
16 )
13 17 from IPython.utils.process import FindCmdError
14 18
15 19
16 20 @pytest.mark.parametrize('command', ['latex', 'dvipng'])
21 @skip_iptest_but_not_pytest
17 22 def test_check_latex_to_png_dvipng_fails_when_no_cmd(command):
18 23 def mock_find_cmd(arg):
19 24 if arg == command:
20 25 raise FindCmdError
21 26
22 27 with patch.object(latextools, "find_cmd", mock_find_cmd):
23 28 assert latextools.latex_to_png_dvipng("whatever", True) == None
24 29
25 30
26 @onlyif_cmds_exist('latex', 'dvipng')
27 def test_latex_to_png_dvipng_runs():
31 @contextmanager
32 def no_op(*args, **kwargs):
33 yield
34
35
36 @onlyif_cmds_exist("latex", "dvipng")
37 @pytest.mark.parametrize("s, wrap", [(u"$$x^2$$", False), (u"x^2", True)])
38 @skip_iptest_but_not_pytest
39 def test_latex_to_png_dvipng_runs(s, wrap):
28 40 """
29 41 Test that latex_to_png_dvipng just runs without error.
30 42 """
31 43 def mock_kpsewhich(filename):
32 44 assert filename == "breqn.sty"
33 45 return None
34 46
35 for (s, wrap) in [(u"$$x^2$$", False), (u"x^2", True)]:
36 yield (latextools.latex_to_png_dvipng, s, wrap)
37
38 with patch.object(latextools, "kpsewhich", mock_kpsewhich):
39 yield (latextools.latex_to_png_dvipng, s, wrap)
47 latextools.latex_to_png_dvipng(s, wrap)
40 48
49 with patch_latextool(mock_kpsewhich):
50 latextools.latex_to_png_dvipng(s, wrap)
41 51
42 @contextmanager
43 def no_op(*args, **kwargs):
44 yield
45 52
46 53 def mock_kpsewhich(filename):
47 54 assert filename == "breqn.sty"
48 55 return None
49 56
50 57 @contextmanager
51 def patch_latextool():
52 with patch.object(latextools, "kpsewhich", mock_kpsewhich):
58 def patch_latextool(mock=mock_kpsewhich):
59 with patch.object(latextools, "kpsewhich", mock):
53 60 yield
54 61
55 62 @pytest.mark.parametrize('context', [no_op, patch_latextool])
56 63 @pytest.mark.parametrize('s_wrap', [("$x^2$", False), ("x^2", True)])
64 @skip_iptest_but_not_pytest
57 65 def test_latex_to_png_mpl_runs(s_wrap, context):
58 66 """
59 67 Test that latex_to_png_mpl just runs without error.
60 68 """
61 69 try:
62 import matplotbli
70 import matplotlib
63 71 except ImportError:
64 pytest.skip("This needs matplotlib to be availlable")
72 pytest.skip("This needs matplotlib to be available")
65 73 return
66 74 s, wrap = s_wrap
67 75 with context():
68 76 latextools.latex_to_png_mpl(s, wrap)
69 77
70 78 @skipif_not_matplotlib
71 79 def test_latex_to_html():
72 80 img = latextools.latex_to_html("$x^2$")
73 81 assert "data:image/png;base64,iVBOR" in img
74 82
75 83
76 84 def test_genelatex_no_wrap():
77 85 """
78 86 Test genelatex with wrap=False.
79 87 """
80 88 def mock_kpsewhich(filename):
81 89 assert False, ("kpsewhich should not be called "
82 90 "(called with {0})".format(filename))
83 91
84 with patch.object(latextools, "kpsewhich", mock_kpsewhich):
92 with patch_latextool(mock_kpsewhich):
85 93 assert '\n'.join(latextools.genelatex("body text", False)) == r'''\documentclass{article}
86 94 \usepackage{amsmath}
87 95 \usepackage{amsthm}
88 96 \usepackage{amssymb}
89 97 \usepackage{bm}
90 98 \pagestyle{empty}
91 99 \begin{document}
92 100 body text
93 101 \end{document}'''
94 102
95 103
96 104 def test_genelatex_wrap_with_breqn():
97 105 """
98 106 Test genelatex with wrap=True for the case breqn.sty is installed.
99 107 """
100 108 def mock_kpsewhich(filename):
101 109 assert filename == "breqn.sty"
102 110 return "path/to/breqn.sty"
103 111
104 with patch.object(latextools, "kpsewhich", mock_kpsewhich):
112 with patch_latextool(mock_kpsewhich):
105 113 assert '\n'.join(latextools.genelatex("x^2", True)) == r'''\documentclass{article}
106 114 \usepackage{amsmath}
107 115 \usepackage{amsthm}
108 116 \usepackage{amssymb}
109 117 \usepackage{bm}
110 118 \usepackage{breqn}
111 119 \pagestyle{empty}
112 120 \begin{document}
113 121 \begin{dmath*}
114 122 x^2
115 123 \end{dmath*}
116 124 \end{document}'''
117 125
118 126
119 127 def test_genelatex_wrap_without_breqn():
120 128 """
121 129 Test genelatex with wrap=True for the case breqn.sty is not installed.
122 130 """
123 131 def mock_kpsewhich(filename):
124 132 assert filename == "breqn.sty"
125 133 return None
126 134
127 with patch.object(latextools, "kpsewhich", mock_kpsewhich):
135 with patch_latextool(mock_kpsewhich):
128 136 assert '\n'.join(latextools.genelatex("x^2", True)) == r'''\documentclass{article}
129 137 \usepackage{amsmath}
130 138 \usepackage{amsthm}
131 139 \usepackage{amssymb}
132 140 \usepackage{bm}
133 141 \pagestyle{empty}
134 142 \begin{document}
135 143 $$x^2$$
136 144 \end{document}'''
137 145
138 146
139 147 @skipif_not_matplotlib
140 148 @onlyif_cmds_exist('latex', 'dvipng')
141 149 def test_latex_to_png_color():
142 150 """
143 151 Test color settings for latex_to_png.
144 152 """
145 153 latex_string = "$x^2$"
146 154 default_value = latextools.latex_to_png(latex_string, wrap=False)
147 155 default_hexblack = latextools.latex_to_png(latex_string, wrap=False,
148 156 color='#000000')
149 157 dvipng_default = latextools.latex_to_png_dvipng(latex_string, False)
150 158 dvipng_black = latextools.latex_to_png_dvipng(latex_string, False, 'Black')
151 159 assert dvipng_default == dvipng_black
152 160 mpl_default = latextools.latex_to_png_mpl(latex_string, False)
153 161 mpl_black = latextools.latex_to_png_mpl(latex_string, False, 'Black')
154 162 assert mpl_default == mpl_black
155 163 assert default_value in [dvipng_black, mpl_black]
156 164 assert default_hexblack in [dvipng_black, mpl_black]
157 165
158 166 # Test that dvips name colors can be used without error
159 167 dvipng_maroon = latextools.latex_to_png_dvipng(latex_string, False,
160 168 'Maroon')
161 169 # And that it doesn't return the black one
162 170 assert dvipng_black != dvipng_maroon
163 171
164 172 mpl_maroon = latextools.latex_to_png_mpl(latex_string, False, 'Maroon')
165 173 assert mpl_black != mpl_maroon
166 174 mpl_white = latextools.latex_to_png_mpl(latex_string, False, 'White')
167 175 mpl_hexwhite = latextools.latex_to_png_mpl(latex_string, False, '#FFFFFF')
168 176 assert mpl_white == mpl_hexwhite
169 177
170 178 mpl_white_scale = latextools.latex_to_png_mpl(latex_string, False,
171 179 'White', 1.2)
172 180 assert mpl_white != mpl_white_scale
173 181
174 182
175 183 def test_latex_to_png_invalid_hex_colors():
176 184 """
177 185 Test that invalid hex colors provided to dvipng gives an exception.
178 186 """
179 187 latex_string = "$x^2$"
180 188 nt.assert_raises(ValueError, lambda: latextools.latex_to_png(latex_string,
181 189 backend='dvipng', color="#f00bar"))
182 190 nt.assert_raises(ValueError, lambda: latextools.latex_to_png(latex_string,
183 191 backend='dvipng', color="#f00"))
@@ -1,472 +1,492 b''
1 1 # coding: utf-8
2 2 """Tests for IPython.lib.pretty."""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7
8 8 from collections import Counter, defaultdict, deque, OrderedDict
9 9 import os
10 10 import types
11 11 import string
12 12 import unittest
13 13
14 14 import nose.tools as nt
15 import pytest
15 16
16 17 from IPython.lib import pretty
17 from IPython.testing.decorators import skip_without
18 from IPython.testing.decorators import skip_without, skip_iptest_but_not_pytest
18 19
19 20 from io import StringIO
20 21
21 22
22 23 class MyList(object):
23 24 def __init__(self, content):
24 25 self.content = content
25 26 def _repr_pretty_(self, p, cycle):
26 27 if cycle:
27 28 p.text("MyList(...)")
28 29 else:
29 30 with p.group(3, "MyList(", ")"):
30 31 for (i, child) in enumerate(self.content):
31 32 if i:
32 33 p.text(",")
33 34 p.breakable()
34 35 else:
35 36 p.breakable("")
36 37 p.pretty(child)
37 38
38 39
39 40 class MyDict(dict):
40 41 def _repr_pretty_(self, p, cycle):
41 42 p.text("MyDict(...)")
42 43
43 44 class MyObj(object):
44 45 def somemethod(self):
45 46 pass
46 47
47 48
48 49 class Dummy1(object):
49 50 def _repr_pretty_(self, p, cycle):
50 51 p.text("Dummy1(...)")
51 52
52 53 class Dummy2(Dummy1):
53 54 _repr_pretty_ = None
54 55
55 56 class NoModule(object):
56 57 pass
57 58
58 59 NoModule.__module__ = None
59 60
60 61 class Breaking(object):
61 62 def _repr_pretty_(self, p, cycle):
62 63 with p.group(4,"TG: ",":"):
63 64 p.text("Breaking(")
64 65 p.break_()
65 66 p.text(")")
66 67
67 68 class BreakingRepr(object):
68 69 def __repr__(self):
69 70 return "Breaking(\n)"
70 71
71 72 class BadRepr(object):
72 73
73 74 def __repr__(self):
74 75 return 1/0
75 76
76 77
77 78 def test_indentation():
78 79 """Test correct indentation in groups"""
79 80 count = 40
80 81 gotoutput = pretty.pretty(MyList(range(count)))
81 82 expectedoutput = "MyList(\n" + ",\n".join(" %d" % i for i in range(count)) + ")"
82 83
83 84 nt.assert_equal(gotoutput, expectedoutput)
84 85
85 86
86 87 def test_dispatch():
87 88 """
88 89 Test correct dispatching: The _repr_pretty_ method for MyDict
89 90 must be found before the registered printer for dict.
90 91 """
91 92 gotoutput = pretty.pretty(MyDict())
92 93 expectedoutput = "MyDict(...)"
93 94
94 95 nt.assert_equal(gotoutput, expectedoutput)
95 96
96 97
97 98 def test_callability_checking():
98 99 """
99 100 Test that the _repr_pretty_ method is tested for callability and skipped if
100 101 not.
101 102 """
102 103 gotoutput = pretty.pretty(Dummy2())
103 104 expectedoutput = "Dummy1(...)"
104 105
105 106 nt.assert_equal(gotoutput, expectedoutput)
106 107
107 108
108 def test_sets():
109 @pytest.mark.parametrize(
110 "obj,expected_output",
111 zip(
112 [
113 set(),
114 frozenset(),
115 set([1]),
116 frozenset([1]),
117 set([1, 2]),
118 frozenset([1, 2]),
119 set([-1, -2, -3]),
120 ],
121 [
122 "set()",
123 "frozenset()",
124 "{1}",
125 "frozenset({1})",
126 "{1, 2}",
127 "frozenset({1, 2})",
128 "{-3, -2, -1}",
129 ],
130 ),
131 )
132 @skip_iptest_but_not_pytest
133 def test_sets(obj, expected_output):
109 134 """
110 135 Test that set and frozenset use Python 3 formatting.
111 136 """
112 objects = [set(), frozenset(), set([1]), frozenset([1]), set([1, 2]),
113 frozenset([1, 2]), set([-1, -2, -3])]
114 expected = ['set()', 'frozenset()', '{1}', 'frozenset({1})', '{1, 2}',
115 'frozenset({1, 2})', '{-3, -2, -1}']
116 for obj, expected_output in zip(objects, expected):
117 137 got_output = pretty.pretty(obj)
118 yield nt.assert_equal, got_output, expected_output
138 nt.assert_equal(got_output, expected_output)
119 139
120 140
121 141 @skip_without('xxlimited')
122 142 def test_pprint_heap_allocated_type():
123 143 """
124 144 Test that pprint works for heap allocated types.
125 145 """
126 146 import xxlimited
127 147 output = pretty.pretty(xxlimited.Null)
128 148 nt.assert_equal(output, 'xxlimited.Null')
129 149
130 150 def test_pprint_nomod():
131 151 """
132 152 Test that pprint works for classes with no __module__.
133 153 """
134 154 output = pretty.pretty(NoModule)
135 155 nt.assert_equal(output, 'NoModule')
136 156
137 157 def test_pprint_break():
138 158 """
139 159 Test that p.break_ produces expected output
140 160 """
141 161 output = pretty.pretty(Breaking())
142 162 expected = "TG: Breaking(\n ):"
143 163 nt.assert_equal(output, expected)
144 164
145 165 def test_pprint_break_repr():
146 166 """
147 167 Test that p.break_ is used in repr
148 168 """
149 169 output = pretty.pretty([[BreakingRepr()]])
150 170 expected = "[[Breaking(\n )]]"
151 171 nt.assert_equal(output, expected)
152 172
153 173 output = pretty.pretty([[BreakingRepr()]*2])
154 174 expected = "[[Breaking(\n ),\n Breaking(\n )]]"
155 175 nt.assert_equal(output, expected)
156 176
157 177 def test_bad_repr():
158 178 """Don't catch bad repr errors"""
159 179 with nt.assert_raises(ZeroDivisionError):
160 180 pretty.pretty(BadRepr())
161 181
162 182 class BadException(Exception):
163 183 def __str__(self):
164 184 return -1
165 185
166 186 class ReallyBadRepr(object):
167 187 __module__ = 1
168 188 @property
169 189 def __class__(self):
170 190 raise ValueError("I am horrible")
171 191
172 192 def __repr__(self):
173 193 raise BadException()
174 194
175 195 def test_really_bad_repr():
176 196 with nt.assert_raises(BadException):
177 197 pretty.pretty(ReallyBadRepr())
178 198
179 199
180 200 class SA(object):
181 201 pass
182 202
183 203 class SB(SA):
184 204 pass
185 205
186 206 class TestsPretty(unittest.TestCase):
187 207
188 208 def test_super_repr(self):
189 209 # "<super: module_name.SA, None>"
190 210 output = pretty.pretty(super(SA))
191 211 self.assertRegex(output, r"<super: \S+.SA, None>")
192 212
193 213 # "<super: module_name.SA, <module_name.SB at 0x...>>"
194 214 sb = SB()
195 215 output = pretty.pretty(super(SA, sb))
196 216 self.assertRegex(output, r"<super: \S+.SA,\s+<\S+.SB at 0x\S+>>")
197 217
198 218
199 219 def test_long_list(self):
200 220 lis = list(range(10000))
201 221 p = pretty.pretty(lis)
202 222 last2 = p.rsplit('\n', 2)[-2:]
203 223 self.assertEqual(last2, [' 999,', ' ...]'])
204 224
205 225 def test_long_set(self):
206 226 s = set(range(10000))
207 227 p = pretty.pretty(s)
208 228 last2 = p.rsplit('\n', 2)[-2:]
209 229 self.assertEqual(last2, [' 999,', ' ...}'])
210 230
211 231 def test_long_tuple(self):
212 232 tup = tuple(range(10000))
213 233 p = pretty.pretty(tup)
214 234 last2 = p.rsplit('\n', 2)[-2:]
215 235 self.assertEqual(last2, [' 999,', ' ...)'])
216 236
217 237 def test_long_dict(self):
218 238 d = { n:n for n in range(10000) }
219 239 p = pretty.pretty(d)
220 240 last2 = p.rsplit('\n', 2)[-2:]
221 241 self.assertEqual(last2, [' 999: 999,', ' ...}'])
222 242
223 243 def test_unbound_method(self):
224 244 output = pretty.pretty(MyObj.somemethod)
225 245 self.assertIn('MyObj.somemethod', output)
226 246
227 247
228 248 class MetaClass(type):
229 249 def __new__(cls, name):
230 250 return type.__new__(cls, name, (object,), {'name': name})
231 251
232 252 def __repr__(self):
233 253 return "[CUSTOM REPR FOR CLASS %s]" % self.name
234 254
235 255
236 256 ClassWithMeta = MetaClass('ClassWithMeta')
237 257
238 258
239 259 def test_metaclass_repr():
240 260 output = pretty.pretty(ClassWithMeta)
241 261 nt.assert_equal(output, "[CUSTOM REPR FOR CLASS ClassWithMeta]")
242 262
243 263
244 264 def test_unicode_repr():
245 265 u = u"üniçodé"
246 266 ustr = u
247 267
248 268 class C(object):
249 269 def __repr__(self):
250 270 return ustr
251 271
252 272 c = C()
253 273 p = pretty.pretty(c)
254 274 nt.assert_equal(p, u)
255 275 p = pretty.pretty([c])
256 276 nt.assert_equal(p, u'[%s]' % u)
257 277
258 278
259 279 def test_basic_class():
260 280 def type_pprint_wrapper(obj, p, cycle):
261 281 if obj is MyObj:
262 282 type_pprint_wrapper.called = True
263 283 return pretty._type_pprint(obj, p, cycle)
264 284 type_pprint_wrapper.called = False
265 285
266 286 stream = StringIO()
267 287 printer = pretty.RepresentationPrinter(stream)
268 288 printer.type_pprinters[type] = type_pprint_wrapper
269 289 printer.pretty(MyObj)
270 290 printer.flush()
271 291 output = stream.getvalue()
272 292
273 293 nt.assert_equal(output, '%s.MyObj' % __name__)
274 294 nt.assert_true(type_pprint_wrapper.called)
275 295
276 296
277 297 def test_collections_defaultdict():
278 298 # Create defaultdicts with cycles
279 299 a = defaultdict()
280 300 a.default_factory = a
281 301 b = defaultdict(list)
282 302 b['key'] = b
283 303
284 304 # Dictionary order cannot be relied on, test against single keys.
285 305 cases = [
286 306 (defaultdict(list), 'defaultdict(list, {})'),
287 307 (defaultdict(list, {'key': '-' * 50}),
288 308 "defaultdict(list,\n"
289 309 " {'key': '--------------------------------------------------'})"),
290 310 (a, 'defaultdict(defaultdict(...), {})'),
291 311 (b, "defaultdict(list, {'key': defaultdict(...)})"),
292 312 ]
293 313 for obj, expected in cases:
294 314 nt.assert_equal(pretty.pretty(obj), expected)
295 315
296 316
297 317 def test_collections_ordereddict():
298 318 # Create OrderedDict with cycle
299 319 a = OrderedDict()
300 320 a['key'] = a
301 321
302 322 cases = [
303 323 (OrderedDict(), 'OrderedDict()'),
304 324 (OrderedDict((i, i) for i in range(1000, 1010)),
305 325 'OrderedDict([(1000, 1000),\n'
306 326 ' (1001, 1001),\n'
307 327 ' (1002, 1002),\n'
308 328 ' (1003, 1003),\n'
309 329 ' (1004, 1004),\n'
310 330 ' (1005, 1005),\n'
311 331 ' (1006, 1006),\n'
312 332 ' (1007, 1007),\n'
313 333 ' (1008, 1008),\n'
314 334 ' (1009, 1009)])'),
315 335 (a, "OrderedDict([('key', OrderedDict(...))])"),
316 336 ]
317 337 for obj, expected in cases:
318 338 nt.assert_equal(pretty.pretty(obj), expected)
319 339
320 340
321 341 def test_collections_deque():
322 342 # Create deque with cycle
323 343 a = deque()
324 344 a.append(a)
325 345
326 346 cases = [
327 347 (deque(), 'deque([])'),
328 348 (deque(i for i in range(1000, 1020)),
329 349 'deque([1000,\n'
330 350 ' 1001,\n'
331 351 ' 1002,\n'
332 352 ' 1003,\n'
333 353 ' 1004,\n'
334 354 ' 1005,\n'
335 355 ' 1006,\n'
336 356 ' 1007,\n'
337 357 ' 1008,\n'
338 358 ' 1009,\n'
339 359 ' 1010,\n'
340 360 ' 1011,\n'
341 361 ' 1012,\n'
342 362 ' 1013,\n'
343 363 ' 1014,\n'
344 364 ' 1015,\n'
345 365 ' 1016,\n'
346 366 ' 1017,\n'
347 367 ' 1018,\n'
348 368 ' 1019])'),
349 369 (a, 'deque([deque(...)])'),
350 370 ]
351 371 for obj, expected in cases:
352 372 nt.assert_equal(pretty.pretty(obj), expected)
353 373
354 374 def test_collections_counter():
355 375 class MyCounter(Counter):
356 376 pass
357 377 cases = [
358 378 (Counter(), 'Counter()'),
359 379 (Counter(a=1), "Counter({'a': 1})"),
360 380 (MyCounter(a=1), "MyCounter({'a': 1})"),
361 381 ]
362 382 for obj, expected in cases:
363 383 nt.assert_equal(pretty.pretty(obj), expected)
364 384
365 385 def test_mappingproxy():
366 386 MP = types.MappingProxyType
367 387 underlying_dict = {}
368 388 mp_recursive = MP(underlying_dict)
369 389 underlying_dict[2] = mp_recursive
370 390 underlying_dict[3] = underlying_dict
371 391
372 392 cases = [
373 393 (MP({}), "mappingproxy({})"),
374 394 (MP({None: MP({})}), "mappingproxy({None: mappingproxy({})})"),
375 395 (MP({k: k.upper() for k in string.ascii_lowercase}),
376 396 "mappingproxy({'a': 'A',\n"
377 397 " 'b': 'B',\n"
378 398 " 'c': 'C',\n"
379 399 " 'd': 'D',\n"
380 400 " 'e': 'E',\n"
381 401 " 'f': 'F',\n"
382 402 " 'g': 'G',\n"
383 403 " 'h': 'H',\n"
384 404 " 'i': 'I',\n"
385 405 " 'j': 'J',\n"
386 406 " 'k': 'K',\n"
387 407 " 'l': 'L',\n"
388 408 " 'm': 'M',\n"
389 409 " 'n': 'N',\n"
390 410 " 'o': 'O',\n"
391 411 " 'p': 'P',\n"
392 412 " 'q': 'Q',\n"
393 413 " 'r': 'R',\n"
394 414 " 's': 'S',\n"
395 415 " 't': 'T',\n"
396 416 " 'u': 'U',\n"
397 417 " 'v': 'V',\n"
398 418 " 'w': 'W',\n"
399 419 " 'x': 'X',\n"
400 420 " 'y': 'Y',\n"
401 421 " 'z': 'Z'})"),
402 422 (mp_recursive, "mappingproxy({2: {...}, 3: {2: {...}, 3: {...}}})"),
403 423 (underlying_dict,
404 424 "{2: mappingproxy({2: {...}, 3: {...}}), 3: {...}}"),
405 425 ]
406 426 for obj, expected in cases:
407 427 nt.assert_equal(pretty.pretty(obj), expected)
408 428
409 429
410 430 def test_simplenamespace():
411 431 SN = types.SimpleNamespace
412 432
413 433 sn_recursive = SN()
414 434 sn_recursive.first = sn_recursive
415 435 sn_recursive.second = sn_recursive
416 436 cases = [
417 437 (SN(), "namespace()"),
418 438 (SN(x=SN()), "namespace(x=namespace())"),
419 439 (SN(a_long_name=[SN(s=string.ascii_lowercase)]*3, a_short_name=None),
420 440 "namespace(a_long_name=[namespace(s='abcdefghijklmnopqrstuvwxyz'),\n"
421 441 " namespace(s='abcdefghijklmnopqrstuvwxyz'),\n"
422 442 " namespace(s='abcdefghijklmnopqrstuvwxyz')],\n"
423 443 " a_short_name=None)"),
424 444 (sn_recursive, "namespace(first=namespace(...), second=namespace(...))"),
425 445 ]
426 446 for obj, expected in cases:
427 447 nt.assert_equal(pretty.pretty(obj), expected)
428 448
429 449
430 450 def test_pretty_environ():
431 451 dict_repr = pretty.pretty(dict(os.environ))
432 452 # reindent to align with 'environ' prefix
433 453 dict_indented = dict_repr.replace('\n', '\n' + (' ' * len('environ')))
434 454 env_repr = pretty.pretty(os.environ)
435 455 nt.assert_equal(env_repr, 'environ' + dict_indented)
436 456
437 457
438 458 def test_function_pretty():
439 459 "Test pretty print of function"
440 460 # posixpath is a pure python module, its interface is consistent
441 461 # across Python distributions
442 462 import posixpath
443 463 nt.assert_equal(pretty.pretty(posixpath.join), '<function posixpath.join(a, *p)>')
444 464
445 465 # custom function
446 466 def meaning_of_life(question=None):
447 467 if question:
448 468 return 42
449 469 return "Don't panic"
450 470
451 471 nt.assert_in('meaning_of_life(question=None)', pretty.pretty(meaning_of_life))
452 472
453 473
454 474 class OrderedCounter(Counter, OrderedDict):
455 475 'Counter that remembers the order elements are first encountered'
456 476
457 477 def __repr__(self):
458 478 return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))
459 479
460 480 def __reduce__(self):
461 481 return self.__class__, (OrderedDict(self),)
462 482
463 483 class MySet(set): # Override repr of a basic type
464 484 def __repr__(self):
465 485 return 'mine'
466 486
467 487 def test_custom_repr():
468 488 """A custom repr should override a pretty printer for a parent type"""
469 489 oc = OrderedCounter("abracadabra")
470 490 nt.assert_in("OrderedCounter(OrderedDict", pretty.pretty(oc))
471 491
472 492 nt.assert_equal(pretty.pretty(MySet()), 'mine')
@@ -1,383 +1,394 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Decorators for labeling test objects.
3 3
4 4 Decorators that merely return a modified version of the original function
5 5 object are straightforward. Decorators that return a new function object need
6 6 to use nose.tools.make_decorator(original_function)(decorator) in returning the
7 7 decorator, in order to preserve metadata such as function name, setup and
8 8 teardown functions and so on - see nose.tools for more information.
9 9
10 10 This module provides a set of useful decorators meant to be ready to use in
11 11 your own tests. See the bottom of the file for the ready-made ones, and if you
12 12 find yourself writing a new one that may be of generic use, add it here.
13 13
14 14 Included decorators:
15 15
16 16
17 17 Lightweight testing that remains unittest-compatible.
18 18
19 19 - An @as_unittest decorator can be used to tag any normal parameter-less
20 20 function as a unittest TestCase. Then, both nose and normal unittest will
21 21 recognize it as such. This will make it easier to migrate away from Nose if
22 22 we ever need/want to while maintaining very lightweight tests.
23 23
24 24 NOTE: This file contains IPython-specific decorators. Using the machinery in
25 25 IPython.external.decorators, we import either numpy.testing.decorators if numpy is
26 26 available, OR use equivalent code in IPython.external._decorators, which
27 27 we've copied verbatim from numpy.
28 28
29 29 """
30 30
31 31 # Copyright (c) IPython Development Team.
32 32 # Distributed under the terms of the Modified BSD License.
33 33
34 34 import os
35 35 import shutil
36 36 import sys
37 37 import tempfile
38 38 import unittest
39 39 import warnings
40 40 from importlib import import_module
41 41
42 42 from decorator import decorator
43 43
44 44 # Expose the unittest-driven decorators
45 45 from .ipunittest import ipdoctest, ipdocstring
46 46
47 47 # Grab the numpy-specific decorators which we keep in a file that we
48 48 # occasionally update from upstream: decorators.py is a copy of
49 49 # numpy.testing.decorators, we expose all of it here.
50 50 from IPython.external.decorators import knownfailureif
51 51
52 52 #-----------------------------------------------------------------------------
53 53 # Classes and functions
54 54 #-----------------------------------------------------------------------------
55 55
56 56 # Simple example of the basic idea
57 57 def as_unittest(func):
58 58 """Decorator to make a simple function into a normal test via unittest."""
59 59 class Tester(unittest.TestCase):
60 60 def test(self):
61 61 func()
62 62
63 63 Tester.__name__ = func.__name__
64 64
65 65 return Tester
66 66
67 67 # Utility functions
68 68
69 69 def apply_wrapper(wrapper, func):
70 70 """Apply a wrapper to a function for decoration.
71 71
72 72 This mixes Michele Simionato's decorator tool with nose's make_decorator,
73 73 to apply a wrapper in a decorator so that all nose attributes, as well as
74 74 function signature and other properties, survive the decoration cleanly.
75 75 This will ensure that wrapped functions can still be well introspected via
76 76 IPython, for example.
77 77 """
78 78 warnings.warn("The function `apply_wrapper` is deprecated since IPython 4.0",
79 79 DeprecationWarning, stacklevel=2)
80 80 import nose.tools
81 81
82 82 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
83 83
84 84
85 85 def make_label_dec(label, ds=None):
86 86 """Factory function to create a decorator that applies one or more labels.
87 87
88 88 Parameters
89 89 ----------
90 90 label : string or sequence
91 91 One or more labels that will be applied by the decorator to the functions
92 92 it decorates. Labels are attributes of the decorated function with their
93 93 value set to True.
94 94
95 95 ds : string
96 96 An optional docstring for the resulting decorator. If not given, a
97 97 default docstring is auto-generated.
98 98
99 99 Returns
100 100 -------
101 101 A decorator.
102 102
103 103 Examples
104 104 --------
105 105
106 106 A simple labeling decorator:
107 107
108 108 >>> slow = make_label_dec('slow')
109 109 >>> slow.__doc__
110 110 "Labels a test as 'slow'."
111 111
112 112 And one that uses multiple labels and a custom docstring:
113 113
114 114 >>> rare = make_label_dec(['slow','hard'],
115 115 ... "Mix labels 'slow' and 'hard' for rare tests.")
116 116 >>> rare.__doc__
117 117 "Mix labels 'slow' and 'hard' for rare tests."
118 118
119 119 Now, let's test using this one:
120 120 >>> @rare
121 121 ... def f(): pass
122 122 ...
123 123 >>>
124 124 >>> f.slow
125 125 True
126 126 >>> f.hard
127 127 True
128 128 """
129 129
130 130 warnings.warn("The function `make_label_dec` is deprecated since IPython 4.0",
131 131 DeprecationWarning, stacklevel=2)
132 132 if isinstance(label, str):
133 133 labels = [label]
134 134 else:
135 135 labels = label
136 136
137 137 # Validate that the given label(s) are OK for use in setattr() by doing a
138 138 # dry run on a dummy function.
139 139 tmp = lambda : None
140 140 for label in labels:
141 141 setattr(tmp,label,True)
142 142
143 143 # This is the actual decorator we'll return
144 144 def decor(f):
145 145 for label in labels:
146 146 setattr(f,label,True)
147 147 return f
148 148
149 149 # Apply the user's docstring, or autogenerate a basic one
150 150 if ds is None:
151 151 ds = "Labels a test as %r." % label
152 152 decor.__doc__ = ds
153 153
154 154 return decor
155 155
156 156
157 def skip_iptest_but_not_pytest(f):
158 """
159 Warnign this will make the test invisible to iptest.
160 """
161 import os
162
163 if os.environ.get("IPTEST_WORKING_DIR", None) is not None:
164 f.__test__ = False
165 return f
166
167
157 168 # Inspired by numpy's skipif, but uses the full apply_wrapper utility to
158 169 # preserve function metadata better and allows the skip condition to be a
159 170 # callable.
160 171 def skipif(skip_condition, msg=None):
161 172 ''' Make function raise SkipTest exception if skip_condition is true
162 173
163 174 Parameters
164 175 ----------
165 176
166 177 skip_condition : bool or callable
167 178 Flag to determine whether to skip test. If the condition is a
168 179 callable, it is used at runtime to dynamically make the decision. This
169 180 is useful for tests that may require costly imports, to delay the cost
170 181 until the test suite is actually executed.
171 182 msg : string
172 183 Message to give on raising a SkipTest exception.
173 184
174 185 Returns
175 186 -------
176 187 decorator : function
177 188 Decorator, which, when applied to a function, causes SkipTest
178 189 to be raised when the skip_condition was True, and the function
179 190 to be called normally otherwise.
180 191
181 192 Notes
182 193 -----
183 194 You will see from the code that we had to further decorate the
184 195 decorator with the nose.tools.make_decorator function in order to
185 196 transmit function name, and various other metadata.
186 197 '''
187 198
188 199 def skip_decorator(f):
189 200 # Local import to avoid a hard nose dependency and only incur the
190 201 # import time overhead at actual test-time.
191 202 import nose
192 203
193 204 # Allow for both boolean or callable skip conditions.
194 205 if callable(skip_condition):
195 206 skip_val = skip_condition
196 207 else:
197 208 skip_val = lambda : skip_condition
198 209
199 210 def get_msg(func,msg=None):
200 211 """Skip message with information about function being skipped."""
201 212 if msg is None: out = 'Test skipped due to test condition.'
202 213 else: out = msg
203 214 return "Skipping test: %s. %s" % (func.__name__,out)
204 215
205 216 # We need to define *two* skippers because Python doesn't allow both
206 217 # return with value and yield inside the same function.
207 218 def skipper_func(*args, **kwargs):
208 219 """Skipper for normal test functions."""
209 220 if skip_val():
210 221 raise nose.SkipTest(get_msg(f,msg))
211 222 else:
212 223 return f(*args, **kwargs)
213 224
214 225 def skipper_gen(*args, **kwargs):
215 226 """Skipper for test generators."""
216 227 if skip_val():
217 228 raise nose.SkipTest(get_msg(f,msg))
218 229 else:
219 230 for x in f(*args, **kwargs):
220 231 yield x
221 232
222 233 # Choose the right skipper to use when building the actual generator.
223 234 if nose.util.isgenerator(f):
224 235 skipper = skipper_gen
225 236 else:
226 237 skipper = skipper_func
227 238
228 239 return nose.tools.make_decorator(f)(skipper)
229 240
230 241 return skip_decorator
231 242
232 243 # A version with the condition set to true, common case just to attach a message
233 244 # to a skip decorator
234 245 def skip(msg=None):
235 246 """Decorator factory - mark a test function for skipping from test suite.
236 247
237 248 Parameters
238 249 ----------
239 250 msg : string
240 251 Optional message to be added.
241 252
242 253 Returns
243 254 -------
244 255 decorator : function
245 256 Decorator, which, when applied to a function, causes SkipTest
246 257 to be raised, with the optional message added.
247 258 """
248 259 if msg and not isinstance(msg, str):
249 260 raise ValueError('invalid object passed to `@skip` decorator, did you '
250 261 'meant `@skip()` with brackets ?')
251 262 return skipif(True, msg)
252 263
253 264
254 265 def onlyif(condition, msg):
255 266 """The reverse from skipif, see skipif for details."""
256 267
257 268 if callable(condition):
258 269 skip_condition = lambda : not condition()
259 270 else:
260 271 skip_condition = lambda : not condition
261 272
262 273 return skipif(skip_condition, msg)
263 274
264 275 #-----------------------------------------------------------------------------
265 276 # Utility functions for decorators
266 277 def module_not_available(module):
267 278 """Can module be imported? Returns true if module does NOT import.
268 279
269 280 This is used to make a decorator to skip tests that require module to be
270 281 available, but delay the 'import numpy' to test execution time.
271 282 """
272 283 try:
273 284 mod = import_module(module)
274 285 mod_not_avail = False
275 286 except ImportError:
276 287 mod_not_avail = True
277 288
278 289 return mod_not_avail
279 290
280 291
281 292 def decorated_dummy(dec, name):
282 293 """Return a dummy function decorated with dec, with the given name.
283 294
284 295 Examples
285 296 --------
286 297 import IPython.testing.decorators as dec
287 298 setup = dec.decorated_dummy(dec.skip_if_no_x11, __name__)
288 299 """
289 300 warnings.warn("The function `decorated_dummy` is deprecated since IPython 4.0",
290 301 DeprecationWarning, stacklevel=2)
291 302 dummy = lambda: None
292 303 dummy.__name__ = name
293 304 return dec(dummy)
294 305
295 306 #-----------------------------------------------------------------------------
296 307 # Decorators for public use
297 308
298 309 # Decorators to skip certain tests on specific platforms.
299 310 skip_win32 = skipif(sys.platform == 'win32',
300 311 "This test does not run under Windows")
301 312 skip_linux = skipif(sys.platform.startswith('linux'),
302 313 "This test does not run under Linux")
303 314 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
304 315
305 316
306 317 # Decorators to skip tests if not on specific platforms.
307 318 skip_if_not_win32 = skipif(sys.platform != 'win32',
308 319 "This test only runs under Windows")
309 320 skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
310 321 "This test only runs under Linux")
311 322 skip_if_not_osx = skipif(sys.platform != 'darwin',
312 323 "This test only runs under OSX")
313 324
314 325
315 326 _x11_skip_cond = (sys.platform not in ('darwin', 'win32') and
316 327 os.environ.get('DISPLAY', '') == '')
317 328 _x11_skip_msg = "Skipped under *nix when X11/XOrg not available"
318 329
319 330 skip_if_no_x11 = skipif(_x11_skip_cond, _x11_skip_msg)
320 331
321 332
322 333 # Decorators to skip certain tests on specific platform/python combinations
323 334 skip_win32_py38 = skipif(sys.version_info > (3,8) and os.name == 'nt')
324 335
325 336
326 337 # not a decorator itself, returns a dummy function to be used as setup
327 338 def skip_file_no_x11(name):
328 339 warnings.warn("The function `skip_file_no_x11` is deprecated since IPython 4.0",
329 340 DeprecationWarning, stacklevel=2)
330 341 return decorated_dummy(skip_if_no_x11, name) if _x11_skip_cond else None
331 342
332 343 # Other skip decorators
333 344
334 345 # generic skip without module
335 346 skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod)
336 347
337 348 skipif_not_numpy = skip_without('numpy')
338 349
339 350 skipif_not_matplotlib = skip_without('matplotlib')
340 351
341 352 skipif_not_sympy = skip_without('sympy')
342 353
343 354 skip_known_failure = knownfailureif(True,'This test is known to fail')
344 355
345 356 # A null 'decorator', useful to make more readable code that needs to pick
346 357 # between different decorators based on OS or other conditions
347 358 null_deco = lambda f: f
348 359
349 360 # Some tests only run where we can use unicode paths. Note that we can't just
350 361 # check os.path.supports_unicode_filenames, which is always False on Linux.
351 362 try:
352 363 f = tempfile.NamedTemporaryFile(prefix=u"tmp€")
353 364 except UnicodeEncodeError:
354 365 unicode_paths = False
355 366 else:
356 367 unicode_paths = True
357 368 f.close()
358 369
359 370 onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable "
360 371 "where we can use unicode in filenames."))
361 372
362 373
363 374 def onlyif_cmds_exist(*commands):
364 375 """
365 376 Decorator to skip test when at least one of `commands` is not found.
366 377 """
367 378 for cmd in commands:
368 379 if not shutil.which(cmd):
369 380 return skip("This test runs only if command '{0}' "
370 381 "is installed".format(cmd))
371 382 return null_deco
372 383
373 384 def onlyif_any_cmd_exists(*commands):
374 385 """
375 386 Decorator to skip test unless at least one of `commands` is found.
376 387 """
377 388 warnings.warn("The function `onlyif_any_cmd_exists` is deprecated since IPython 4.0",
378 389 DeprecationWarning, stacklevel=2)
379 390 for cmd in commands:
380 391 if shutil.which(cmd):
381 392 return null_deco
382 393 return skip("This test runs only if one of the commands {0} "
383 394 "is installed".format(commands))
@@ -1,159 +1,172 b''
1 1 # encoding: utf-8
2 2 """Tests for IPython.utils.capture"""
3 3
4 4 #-----------------------------------------------------------------------------
5 5 # Copyright (C) 2013 The IPython Development Team
6 6 #
7 7 # Distributed under the terms of the BSD License. The full license is in
8 8 # the file COPYING, distributed as part of this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14
15 15
16 16 import sys
17 17
18 18 import nose.tools as nt
19 import pytest
20
21 from IPython.testing.decorators import skip_iptest_but_not_pytest
19 22
20 23 from IPython.utils import capture
21 24
22 25 #-----------------------------------------------------------------------------
23 26 # Globals
24 27 #-----------------------------------------------------------------------------
25 28
26 29 _mime_map = dict(
27 30 _repr_png_="image/png",
28 31 _repr_jpeg_="image/jpeg",
29 32 _repr_svg_="image/svg+xml",
30 33 _repr_html_="text/html",
31 34 _repr_json_="application/json",
32 35 _repr_javascript_="application/javascript",
33 36 )
34 37
35 38 basic_data = {
36 39 'image/png' : b'binarydata',
37 40 'text/html' : "<b>bold</b>",
38 41 }
39 42 basic_metadata = {
40 43 'image/png' : {
41 44 'width' : 10,
42 45 'height' : 20,
43 46 },
44 47 }
45 48
46 49 full_data = {
47 50 'image/png' : b'binarydata',
48 51 'image/jpeg' : b'binarydata',
49 52 'image/svg+xml' : "<svg>",
50 53 'text/html' : "<b>bold</b>",
51 54 'application/javascript' : "alert();",
52 55 'application/json' : "{}",
53 56 }
54 57 full_metadata = {
55 58 'image/png' : {"png" : "exists"},
56 59 'image/jpeg' : {"jpeg" : "exists"},
57 60 'image/svg+xml' : {"svg" : "exists"},
58 61 'text/html' : {"html" : "exists"},
59 62 'application/javascript' : {"js" : "exists"},
60 63 'application/json' : {"json" : "exists"},
61 64 }
62 65
63 66 hello_stdout = "hello, stdout"
64 67 hello_stderr = "hello, stderr"
65 68
66 69 #-----------------------------------------------------------------------------
67 70 # Test Functions
68 71 #-----------------------------------------------------------------------------
69
70 def test_rich_output_empty():
72 @pytest.mark.parametrize("method_mime", _mime_map.items())
73 @skip_iptest_but_not_pytest
74 def test_rich_output_empty(method_mime):
71 75 """RichOutput with no args"""
72 76 rich = capture.RichOutput()
73 for method, mime in _mime_map.items():
74 yield nt.assert_equal, getattr(rich, method)(), None
77 method, mime = method_mime
78 nt.assert_equal(getattr(rich, method)(), None)
75 79
76 80 def test_rich_output():
77 81 """test RichOutput basics"""
78 82 data = basic_data
79 83 metadata = basic_metadata
80 84 rich = capture.RichOutput(data=data, metadata=metadata)
81 yield nt.assert_equal, rich._repr_html_(), data['text/html']
82 yield nt.assert_equal, rich._repr_png_(), (data['image/png'], metadata['image/png'])
83 yield nt.assert_equal, rich._repr_latex_(), None
84 yield nt.assert_equal, rich._repr_javascript_(), None
85 yield nt.assert_equal, rich._repr_svg_(), None
85 nt.assert_equal(rich._repr_html_(), data["text/html"])
86 nt.assert_equal(rich._repr_png_(), (data["image/png"], metadata["image/png"]))
87 nt.assert_equal(rich._repr_latex_(), None)
88 nt.assert_equal(rich._repr_javascript_(), None)
89 nt.assert_equal(rich._repr_svg_(), None)
86 90
87 def test_rich_output_no_metadata():
91
92 @skip_iptest_but_not_pytest
93 @pytest.mark.parametrize("method_mime", _mime_map.items())
94 def test_rich_output_no_metadata(method_mime):
88 95 """test RichOutput with no metadata"""
89 96 data = full_data
90 97 rich = capture.RichOutput(data=data)
91 for method, mime in _mime_map.items():
92 yield nt.assert_equal, getattr(rich, method)(), data[mime]
98 method, mime = method_mime
99 nt.assert_equal(getattr(rich, method)(), data[mime])
100
93 101
94 def test_rich_output_metadata():
102 @skip_iptest_but_not_pytest
103 @pytest.mark.parametrize("method_mime", _mime_map.items())
104 def test_rich_output_metadata(method_mime):
95 105 """test RichOutput with metadata"""
96 106 data = full_data
97 107 metadata = full_metadata
98 108 rich = capture.RichOutput(data=data, metadata=metadata)
99 for method, mime in _mime_map.items():
100 yield nt.assert_equal, getattr(rich, method)(), (data[mime], metadata[mime])
109 method, mime = method_mime
110 nt.assert_equal(getattr(rich, method)(), (data[mime], metadata[mime]))
101 111
102 112 def test_rich_output_display():
103 113 """test RichOutput.display
104 114
105 115 This is a bit circular, because we are actually using the capture code we are testing
106 116 to test itself.
107 117 """
108 118 data = full_data
109 119 rich = capture.RichOutput(data=data)
110 120 with capture.capture_output() as cap:
111 121 rich.display()
112 yield nt.assert_equal, len(cap.outputs), 1
122 nt.assert_equal(len(cap.outputs), 1)
113 123 rich2 = cap.outputs[0]
114 yield nt.assert_equal, rich2.data, rich.data
115 yield nt.assert_equal, rich2.metadata, rich.metadata
124 nt.assert_equal(rich2.data, rich.data)
125 nt.assert_equal(rich2.metadata, rich.metadata)
116 126
117 127 def test_capture_output():
118 128 """capture_output works"""
119 129 rich = capture.RichOutput(data=full_data)
120 130 with capture.capture_output() as cap:
121 131 print(hello_stdout, end="")
122 132 print(hello_stderr, end="", file=sys.stderr)
123 133 rich.display()
124 yield nt.assert_equal, hello_stdout, cap.stdout
125 yield nt.assert_equal, hello_stderr, cap.stderr
134 nt.assert_equal(hello_stdout, cap.stdout)
135 nt.assert_equal(hello_stderr, cap.stderr)
136
126 137
127 138 def test_capture_output_no_stdout():
128 139 """test capture_output(stdout=False)"""
129 140 rich = capture.RichOutput(data=full_data)
130 141 with capture.capture_output(stdout=False) as cap:
131 142 print(hello_stdout, end="")
132 143 print(hello_stderr, end="", file=sys.stderr)
133 144 rich.display()
134 yield nt.assert_equal, "", cap.stdout
135 yield nt.assert_equal, hello_stderr, cap.stderr
136 yield nt.assert_equal, len(cap.outputs), 1
145 nt.assert_equal("", cap.stdout)
146 nt.assert_equal(hello_stderr, cap.stderr)
147 nt.assert_equal(len(cap.outputs), 1)
148
137 149
138 150 def test_capture_output_no_stderr():
139 151 """test capture_output(stderr=False)"""
140 152 rich = capture.RichOutput(data=full_data)
141 153 # add nested capture_output so stderr doesn't make it to nose output
142 154 with capture.capture_output(), capture.capture_output(stderr=False) as cap:
143 155 print(hello_stdout, end="")
144 156 print(hello_stderr, end="", file=sys.stderr)
145 157 rich.display()
146 yield nt.assert_equal, hello_stdout, cap.stdout
147 yield nt.assert_equal, "", cap.stderr
148 yield nt.assert_equal, len(cap.outputs), 1
158 nt.assert_equal(hello_stdout, cap.stdout)
159 nt.assert_equal("", cap.stderr)
160 nt.assert_equal(len(cap.outputs), 1)
161
149 162
150 163 def test_capture_output_no_display():
151 164 """test capture_output(display=False)"""
152 165 rich = capture.RichOutput(data=full_data)
153 166 with capture.capture_output(display=False) as cap:
154 167 print(hello_stdout, end="")
155 168 print(hello_stderr, end="", file=sys.stderr)
156 169 rich.display()
157 yield nt.assert_equal, hello_stdout, cap.stdout
158 yield nt.assert_equal, hello_stderr, cap.stderr
159 yield nt.assert_equal, cap.outputs, [] No newline at end of file
170 nt.assert_equal(hello_stdout, cap.stdout)
171 nt.assert_equal(hello_stderr, cap.stderr)
172 nt.assert_equal(cap.outputs, [])
@@ -1,78 +1,76 b''
1 1 # coding: utf-8
2 2 """Test suite for our color utilities.
3 3
4 4 Authors
5 5 -------
6 6
7 7 * Min RK
8 8 """
9 9 #-----------------------------------------------------------------------------
10 10 # Copyright (C) 2011 The IPython Development Team
11 11 #
12 12 # Distributed under the terms of the BSD License. The full license is in
13 13 # the file COPYING.txt, distributed as part of this software.
14 14 #-----------------------------------------------------------------------------
15 15
16 16 #-----------------------------------------------------------------------------
17 17 # Imports
18 18 #-----------------------------------------------------------------------------
19 19
20 20 # third party
21 21 import nose.tools as nt
22 22
23 from IPython.testing.decorators import skip_iptest_but_not_pytest
24
23 25 # our own
24 26 from IPython.utils.PyColorize import Parser
25 27 import io
28 import pytest
29
30
31 @pytest.fixture(scope="module", params=("Linux", "NoColor", "LightBG", "Neutral"))
32 def style(request):
33 yield request.param
26 34
27 35 #-----------------------------------------------------------------------------
28 36 # Test functions
29 37 #-----------------------------------------------------------------------------
30 38
31 sample = u"""
39 sample = """
32 40 def function(arg, *args, kwarg=True, **kwargs):
33 41 '''
34 42 this is docs
35 43 '''
36 44 pass is True
37 45 False == None
38 46
39 47 with io.open(ru'unicode'):
40 48 raise ValueError("\n escape \r sequence")
41 49
42 50 print("wěird ünicoðe")
43 51
44 52 class Bar(Super):
45 53
46 54 def __init__(self):
47 55 super(Bar, self).__init__(1**2, 3^4, 5 or 6)
48 56 """
49 57
50 def test_loop_colors():
51
52 for style in ('Linux', 'NoColor','LightBG', 'Neutral'):
53 58
54 def test_unicode_colorize():
55 p = Parser(style=style)
56 f1 = p.format('1/0', 'str')
57 f2 = p.format(u'1/0', 'str')
58 nt.assert_equal(f1, f2)
59
60 def test_parse_sample():
59 @skip_iptest_but_not_pytest
60 def test_parse_sample(style):
61 61 """and test writing to a buffer"""
62 62 buf = io.StringIO()
63 63 p = Parser(style=style)
64 64 p.format(sample, buf)
65 65 buf.seek(0)
66 66 f1 = buf.read()
67 67
68 nt.assert_not_in('ERROR', f1)
68 nt.assert_not_in("ERROR", f1)
69 69
70 def test_parse_error():
71 p = Parser(style=style)
72 f1 = p.format(')', 'str')
73 if style != 'NoColor':
74 nt.assert_in('ERROR', f1)
75 70
76 yield test_unicode_colorize
77 yield test_parse_sample
78 yield test_parse_error
71 @skip_iptest_but_not_pytest
72 def test_parse_error(style):
73 p = Parser(style=style)
74 f1 = p.format(")", "str")
75 if style != "NoColor":
76 nt.assert_in("ERROR", f1)
@@ -1,133 +1,141 b''
1 1 """Tests for tokenutil"""
2 2 # Copyright (c) IPython Development Team.
3 3 # Distributed under the terms of the Modified BSD License.
4 4
5 5 import nose.tools as nt
6 import pytest
7 from IPython.testing.decorators import skip_iptest_but_not_pytest
6 8
7 9 from IPython.utils.tokenutil import token_at_cursor, line_at_cursor
8 10
9 11 def expect_token(expected, cell, cursor_pos):
10 12 token = token_at_cursor(cell, cursor_pos)
11 13 offset = 0
12 14 for line in cell.splitlines():
13 15 if offset + len(line) >= cursor_pos:
14 16 break
15 17 else:
16 18 offset += len(line)+1
17 19 column = cursor_pos - offset
18 20 line_with_cursor = '%s|%s' % (line[:column], line[column:])
19 21 nt.assert_equal(token, expected,
20 22 "Expected %r, got %r in: %r (pos %i)" % (
21 23 expected, token, line_with_cursor, cursor_pos)
22 24 )
23 25
24 26 def test_simple():
25 27 cell = "foo"
26 28 for i in range(len(cell)):
27 29 expect_token("foo", cell, i)
28 30
29 31 def test_function():
30 32 cell = "foo(a=5, b='10')"
31 33 expected = 'foo'
32 34 # up to `foo(|a=`
33 35 for i in range(cell.find('a=') + 1):
34 36 expect_token("foo", cell, i)
35 37 # find foo after `=`
36 38 for i in [cell.find('=') + 1, cell.rfind('=') + 1]:
37 39 expect_token("foo", cell, i)
38 40 # in between `5,|` and `|b=`
39 41 for i in range(cell.find(','), cell.find('b=')):
40 42 expect_token("foo", cell, i)
41 43
42 44 def test_multiline():
43 45 cell = '\n'.join([
44 46 'a = 5',
45 47 'b = hello("string", there)'
46 48 ])
47 49 expected = 'hello'
48 50 start = cell.index(expected) + 1
49 51 for i in range(start, start + len(expected)):
50 52 expect_token(expected, cell, i)
51 53 expected = 'hello'
52 54 start = cell.index(expected) + 1
53 55 for i in range(start, start + len(expected)):
54 56 expect_token(expected, cell, i)
55 57
56 58 def test_multiline_token():
57 59 cell = '\n'.join([
58 60 '"""\n\nxxxxxxxxxx\n\n"""',
59 61 '5, """',
60 62 'docstring',
61 63 'multiline token',
62 64 '""", [',
63 65 '2, 3, "complicated"]',
64 66 'b = hello("string", there)'
65 67 ])
66 68 expected = 'hello'
67 69 start = cell.index(expected) + 1
68 70 for i in range(start, start + len(expected)):
69 71 expect_token(expected, cell, i)
70 72 expected = 'hello'
71 73 start = cell.index(expected) + 1
72 74 for i in range(start, start + len(expected)):
73 75 expect_token(expected, cell, i)
74 76
75 77 def test_nested_call():
76 78 cell = "foo(bar(a=5), b=10)"
77 79 expected = 'foo'
78 80 start = cell.index('bar') + 1
79 81 for i in range(start, start + 3):
80 82 expect_token(expected, cell, i)
81 83 expected = 'bar'
82 84 start = cell.index('a=')
83 85 for i in range(start, start + 3):
84 86 expect_token(expected, cell, i)
85 87 expected = 'foo'
86 88 start = cell.index(')') + 1
87 89 for i in range(start, len(cell)-1):
88 90 expect_token(expected, cell, i)
89 91
90 92 def test_attrs():
91 93 cell = "a = obj.attr.subattr"
92 94 expected = 'obj'
93 95 idx = cell.find('obj') + 1
94 96 for i in range(idx, idx + 3):
95 97 expect_token(expected, cell, i)
96 98 idx = cell.find('.attr') + 2
97 99 expected = 'obj.attr'
98 100 for i in range(idx, idx + 4):
99 101 expect_token(expected, cell, i)
100 102 idx = cell.find('.subattr') + 2
101 103 expected = 'obj.attr.subattr'
102 104 for i in range(idx, len(cell)):
103 105 expect_token(expected, cell, i)
104 106
105 107 def test_line_at_cursor():
106 108 cell = ""
107 109 (line, offset) = line_at_cursor(cell, cursor_pos=11)
108 110 nt.assert_equal(line, "")
109 111 nt.assert_equal(offset, 0)
110 112
111 113 # The position after a newline should be the start of the following line.
112 114 cell = "One\nTwo\n"
113 115 (line, offset) = line_at_cursor(cell, cursor_pos=4)
114 116 nt.assert_equal(line, "Two\n")
115 117 nt.assert_equal(offset, 4)
116 118
117 119 # The end of a cell should be on the last line
118 120 cell = "pri\npri"
119 121 (line, offset) = line_at_cursor(cell, cursor_pos=7)
120 122 nt.assert_equal(line, "pri")
121 123 nt.assert_equal(offset, 4)
122 124
123 def test_multiline_statement():
125
126 @pytest.mark.parametrize(
127 "c, token",
128 zip(
129 list(range(16, 22)) + list(range(22, 28)),
130 ["int"] * (22 - 16) + ["map"] * (28 - 22),
131 ),
132 )
133 @skip_iptest_but_not_pytest
134 def test_multiline_statement(c, token):
124 135 cell = """a = (1,
125 136 3)
126 137
127 138 int()
128 139 map()
129 140 """
130 for c in range(16, 22):
131 yield lambda cell, c: expect_token("int", cell, c), cell, c
132 for c in range(22, 28):
133 yield lambda cell, c: expect_token("map", cell, c), cell, c
141 expect_token(token, cell, c)
@@ -1,31 +1,31 b''
1 1 build: false
2 2 matrix:
3 3 fast_finish: true # immediately finish build once one of the jobs fails.
4 4
5 5 environment:
6 6 matrix:
7 7
8 8 - PYTHON: "C:\\Python37-x64"
9 9 PYTHON_VERSION: "3.7.x"
10 10 PYTHON_ARCH: "64"
11 11
12 12 - PYTHON: "C:\\Python38"
13 13 PYTHON_VERSION: "3.8.x"
14 14 PYTHON_ARCH: "32"
15 15
16 16 - PYTHON: "C:\\Python38-x64"
17 17 PYTHON_VERSION: "3.8.x"
18 18 PYTHON_ARCH: "64"
19 19
20 20 init:
21 21 - "ECHO %PYTHON% %PYTHON_VERSION% %PYTHON_ARCH%"
22 22
23 23 install:
24 24 - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
25 25 - "%CMD_IN_ENV% python -m pip install --upgrade setuptools pip"
26 - "%CMD_IN_ENV% pip install nose coverage"
26 - "%CMD_IN_ENV% pip install nose coverage pytest"
27 27 - "%CMD_IN_ENV% pip install .[test]"
28 28 - "%CMD_IN_ENV% mkdir results"
29 29 - "%CMD_IN_ENV% cd results"
30 30 test_script:
31 31 - "%CMD_IN_ENV% iptest --coverage xml"
General Comments 0
You need to be logged in to leave comments. Login now