##// END OF EJS Templates
Remove iptest and other Nose-dependent code
Nikita Kniazev -
Show More
@@ -1,52 +1,47 b''
1 1 name: Run tests
2 2
3 3 on:
4 4 push:
5 5 pull_request:
6 6 # Run weekly on Monday at 1:23 UTC
7 7 schedule:
8 8 - cron: '23 1 * * 1'
9 9 workflow_dispatch:
10 10
11 11
12 12 jobs:
13 13 test:
14 14 runs-on: ${{ matrix.os }}
15 15 strategy:
16 16 matrix:
17 17 os: [ubuntu-latest]
18 18 python-version: ["3.7", "3.8", "3.9"]
19 19 # Test all on ubuntu, test ends on macos
20 20 include:
21 21 - os: macos-latest
22 22 python-version: "3.7"
23 23 - os: macos-latest
24 24 python-version: "3.9"
25 25
26 26 steps:
27 27 - uses: actions/checkout@v2
28 28 - name: Set up Python ${{ matrix.python-version }}
29 29 uses: actions/setup-python@v2
30 30 with:
31 31 python-version: ${{ matrix.python-version }}
32 32 - name: Install and update Python dependencies
33 33 run: |
34 34 python -m pip install --upgrade pip setuptools wheel
35 35 python -m pip install --upgrade -e file://$PWD#egg=ipython[test]
36 36 python -m pip install --upgrade --upgrade-strategy eager trio curio
37 37 python -m pip install --upgrade pytest pytest-cov pytest-trio 'matplotlib!=3.2.0' pandas
38 38 python -m pip install --upgrade check-manifest pytest-cov anyio
39 39 - name: Check manifest
40 40 run: check-manifest
41 - name: iptest
42 run: |
43 cd /tmp && iptest --coverage xml && cd -
44 cp /tmp/ipy_coverage.xml ./
45 cp /tmp/.coverage ./
46 41 - name: pytest
47 42 env:
48 43 COLUMNS: 120
49 44 run: |
50 45 pytest --color=yes -v --cov --cov-report=xml
51 46 - name: Upload coverage to Codecov
52 47 uses: codecov/codecov-action@v2
@@ -1,95 +1,90 b''
1 1 ## Triaging Issues
2 2
3 3 On the IPython repository, we strive to trust users and give them responsibility.
4 4 By using one of our bots, any user can close issues or add/remove
5 5 labels by mentioning the bot and asking it to do things on your behalf.
6 6
7 7 To close an issue (or PR), even if you did not create it, use the following:
8 8
9 9 > @meeseeksdev close
10 10
11 11 This command can be in the middle of another comment, but must start on its
12 12 own line.
13 13
14 14 To add labels to an issue, ask the bot to `tag` with a comma-separated list of
15 15 tags to add:
16 16
17 17 > @meeseeksdev tag windows, documentation
18 18
19 19 Only already pre-created tags can be added. So far, the list is limited to:
20 20 `async/await`, `backported`, `help wanted`, `documentation`, `notebook`,
21 21 `tab-completion`, `windows`
22 22
23 23 To remove a label, use the `untag` command:
24 24
25 25 > @meeseeksdev untag windows, documentation
26 26
27 27 We'll be adding additional capabilities for the bot and will share them here
28 28 when they are ready to be used.
29 29
30 30 ## Opening an Issue
31 31
32 32 When opening a new Issue, please take the following steps:
33 33
34 34 1. Search GitHub and/or Google for your issue to avoid duplicate reports.
35 35 Keyword searches for your error messages are most helpful.
36 36 2. If possible, try updating to master and reproducing your issue,
37 37 because we may have already fixed it.
38 38 3. Try to include a minimal reproducible test case.
39 39 4. Include relevant system information. Start with the output of:
40 40
41 41 python -c "import IPython; print(IPython.sys_info())"
42 42
43 43 And include any relevant package versions, depending on the issue, such as
44 44 matplotlib, numpy, Qt, Qt bindings (PyQt/PySide), tornado, web browser, etc.
45 45
46 46 ## Pull Requests
47 47
48 48 Some guidelines on contributing to IPython:
49 49
50 50 * All work is submitted via Pull Requests.
51 51 * Pull Requests can be submitted as soon as there is code worth discussing.
52 52 Pull Requests track the branch, so you can continue to work after the PR is submitted.
53 53 Review and discussion can begin well before the work is complete,
54 54 and the more discussion the better.
55 55 The worst case is that the PR is closed.
56 56 * Pull Requests should generally be made against master
57 57 * Pull Requests should be tested, if feasible:
58 58 - bugfixes should include regression tests.
59 59 - new behavior should at least get minimal exercise.
60 60 * New features and backwards-incompatible changes should be documented by adding
61 61 a new file to the [pr](docs/source/whatsnew/pr) directory, see [the README.md
62 62 there](docs/source/whatsnew/pr/README.md) for details.
63 63 * Don't make 'cleanup' pull requests just to change code style.
64 64 We don't follow any style guide strictly, and we consider formatting changes
65 65 unnecessary noise.
66 66 If you're making functional changes, you can clean up the specific pieces of
67 67 code you're working on.
68 68
69 69 [Travis](http://travis-ci.org/#!/ipython/ipython) does a pretty good job testing
70 70 IPython and Pull Requests, but it may make sense to manually perform tests,
71 71 particularly for PRs that affect `IPython.parallel` or Windows.
72 72
73 73 For more detailed information, see our [GitHub Workflow](https://github.com/ipython/ipython/wiki/Dev:-GitHub-workflow).
74 74
75 75 ## Running Tests
76 76
77 77 All the tests can be run by using
78 78 ```shell
79 iptest
79 pytest
80 80 ```
81 81
82 82 All the tests for a single module (for example **test_alias**) can be run by using the fully qualified path to the module.
83 83 ```shell
84 iptest IPython.core.tests.test_alias
84 pytest IPython/core/tests/test_alias.py
85 85 ```
86 86
87 Only a single test (for example **test_alias_lifecycle**) within a single file can be run by adding the specific test after a `:` at the end:
87 Only a single test (for example **test_alias_lifecycle**) within a single file can be run by adding the specific test after a `::` at the end:
88 88 ```shell
89 iptest IPython.core.tests.test_alias:test_alias_lifecycle
90 ```
91
92 For convenience, the full path to a file can often be used instead of the module path on unix systems. For example we can run all the tests by using
93 ```shell
94 iptest IPython/core/tests/test_alias.py
89 pytest IPython/core/tests/test_alias.py::test_alias_lifecycle
95 90 ```
@@ -1,144 +1,143 b''
1 1 # encoding: utf-8
2 2 """
3 3 IPython: tools for interactive and parallel computing in Python.
4 4
5 5 https://ipython.org
6 6 """
7 7 #-----------------------------------------------------------------------------
8 8 # Copyright (c) 2008-2011, IPython Development Team.
9 9 # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu>
10 10 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
11 11 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
12 12 #
13 13 # Distributed under the terms of the Modified BSD License.
14 14 #
15 15 # The full license is in the file COPYING.txt, distributed with this software.
16 16 #-----------------------------------------------------------------------------
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Imports
20 20 #-----------------------------------------------------------------------------
21 21
22 22 import os
23 23 import sys
24 24
25 25 #-----------------------------------------------------------------------------
26 26 # Setup everything
27 27 #-----------------------------------------------------------------------------
28 28
29 29 # Don't forget to also update setup.py when this changes!
30 30 if sys.version_info < (3, 6):
31 31 raise ImportError(
32 32 """
33 33 IPython 7.10+ supports Python 3.6 and above.
34 34 When using Python 2.7, please install IPython 5.x LTS Long Term Support version.
35 35 Python 3.3 and 3.4 were supported up to IPython 6.x.
36 36 Python 3.5 was supported with IPython 7.0 to 7.9.
37 37
38 38 See IPython `README.rst` file for more information:
39 39
40 40 https://github.com/ipython/ipython/blob/master/README.rst
41 41
42 42 """)
43 43
44 44 #-----------------------------------------------------------------------------
45 45 # Setup the top level names
46 46 #-----------------------------------------------------------------------------
47 47
48 48 from .core.getipython import get_ipython
49 49 from .core import release
50 50 from .core.application import Application
51 51 from .terminal.embed import embed
52 52
53 53 from .core.interactiveshell import InteractiveShell
54 from .testing import test
55 54 from .utils.sysinfo import sys_info
56 55 from .utils.frame import extract_module_locals
57 56
58 57 # Release data
59 58 __author__ = '%s <%s>' % (release.author, release.author_email)
60 59 __license__ = release.license
61 60 __version__ = release.version
62 61 version_info = release.version_info
63 62
64 63 def embed_kernel(module=None, local_ns=None, **kwargs):
65 64 """Embed and start an IPython kernel in a given scope.
66 65
67 66 If you don't want the kernel to initialize the namespace
68 67 from the scope of the surrounding function,
69 68 and/or you want to load full IPython configuration,
70 69 you probably want `IPython.start_kernel()` instead.
71 70
72 71 Parameters
73 72 ----------
74 73 module : types.ModuleType, optional
75 74 The module to load into IPython globals (default: caller)
76 75 local_ns : dict, optional
77 76 The namespace to load into IPython user namespace (default: caller)
78 77 **kwargs : various, optional
79 78 Further keyword args are relayed to the IPKernelApp constructor,
80 79 allowing configuration of the Kernel. Will only have an effect
81 80 on the first embed_kernel call for a given process.
82 81 """
83 82
84 83 (caller_module, caller_locals) = extract_module_locals(1)
85 84 if module is None:
86 85 module = caller_module
87 86 if local_ns is None:
88 87 local_ns = caller_locals
89 88
90 89 # Only import .zmq when we really need it
91 90 from ipykernel.embed import embed_kernel as real_embed_kernel
92 91 real_embed_kernel(module=module, local_ns=local_ns, **kwargs)
93 92
94 93 def start_ipython(argv=None, **kwargs):
95 94 """Launch a normal IPython instance (as opposed to embedded)
96 95
97 96 `IPython.embed()` puts a shell in a particular calling scope,
98 97 such as a function or method for debugging purposes,
99 98 which is often not desirable.
100 99
101 100 `start_ipython()` does full, regular IPython initialization,
102 101 including loading startup files, configuration, etc.
103 102 much of which is skipped by `embed()`.
104 103
105 104 This is a public API method, and will survive implementation changes.
106 105
107 106 Parameters
108 107 ----------
109 108 argv : list or None, optional
110 109 If unspecified or None, IPython will parse command-line options from sys.argv.
111 110 To prevent any command-line parsing, pass an empty list: `argv=[]`.
112 111 user_ns : dict, optional
113 112 specify this dictionary to initialize the IPython user namespace with particular values.
114 113 **kwargs : various, optional
115 114 Any other kwargs will be passed to the Application constructor,
116 115 such as `config`.
117 116 """
118 117 from IPython.terminal.ipapp import launch_new_instance
119 118 return launch_new_instance(argv=argv, **kwargs)
120 119
121 120 def start_kernel(argv=None, **kwargs):
122 121 """Launch a normal IPython kernel instance (as opposed to embedded)
123 122
124 123 `IPython.embed_kernel()` puts a shell in a particular calling scope,
125 124 such as a function or method for debugging purposes,
126 125 which is often not desirable.
127 126
128 127 `start_kernel()` does full, regular IPython initialization,
129 128 including loading startup files, configuration, etc.
130 129 much of which is skipped by `embed()`.
131 130
132 131 Parameters
133 132 ----------
134 133 argv : list or None, optional
135 134 If unspecified or None, IPython will parse command-line options from sys.argv.
136 135 To prevent any command-line parsing, pass an empty list: `argv=[]`.
137 136 user_ns : dict, optional
138 137 specify this dictionary to initialize the IPython user namespace with particular values.
139 138 **kwargs : various, optional
140 139 Any other kwargs will be passed to the Application constructor,
141 140 such as `config`.
142 141 """
143 142 from IPython.kernel.zmq.kernelapp import launch_new_instance
144 143 return launch_new_instance(argv=argv, **kwargs)
@@ -1,1284 +1,1284 b''
1 1 # encoding: utf-8
2 2 """Tests for the IPython tab-completion machinery."""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7 import os
8 import pytest
8 9 import sys
9 10 import textwrap
10 11 import unittest
11 12
12 13 from contextlib import contextmanager
13 14
14 15 from traitlets.config.loader import Config
15 16 from IPython import get_ipython
16 17 from IPython.core import completer
17 from IPython.external import decorators
18 18 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
19 19 from IPython.utils.generics import complete_object
20 20 from IPython.testing import decorators as dec
21 21
22 22 from IPython.core.completer import (
23 23 Completion,
24 24 provisionalcompleter,
25 25 match_dict_keys,
26 26 _deduplicate_completions,
27 27 )
28 28
29 29 # -----------------------------------------------------------------------------
30 30 # Test functions
31 31 # -----------------------------------------------------------------------------
32 32
33 33 def recompute_unicode_ranges():
34 34 """
35 35 utility to recompute the largest unicode range without any characters
36 36
37 37 use to recompute the gap in the global _UNICODE_RANGES of completer.py
38 38 """
39 39 import itertools
40 40 import unicodedata
41 41 valid = []
42 42 for c in range(0,0x10FFFF + 1):
43 43 try:
44 44 unicodedata.name(chr(c))
45 45 except ValueError:
46 46 continue
47 47 valid.append(c)
48 48
49 49 def ranges(i):
50 50 for a, b in itertools.groupby(enumerate(i), lambda pair: pair[1] - pair[0]):
51 51 b = list(b)
52 52 yield b[0][1], b[-1][1]
53 53
54 54 rg = list(ranges(valid))
55 55 lens = []
56 56 gap_lens = []
57 57 pstart, pstop = 0,0
58 58 for start, stop in rg:
59 59 lens.append(stop-start)
60 60 gap_lens.append((start - pstop, hex(pstop), hex(start), f'{round((start - pstop)/0xe01f0*100)}%'))
61 61 pstart, pstop = start, stop
62 62
63 63 return sorted(gap_lens)[-1]
64 64
65 65
66 66
67 67 def test_unicode_range():
68 68 """
69 69 Test that the ranges we test for unicode names give the same number of
70 70 results than testing the full length.
71 71 """
72 72 from IPython.core.completer import _unicode_name_compute, _UNICODE_RANGES
73 73
74 74 expected_list = _unicode_name_compute([(0, 0x110000)])
75 75 test = _unicode_name_compute(_UNICODE_RANGES)
76 76 len_exp = len(expected_list)
77 77 len_test = len(test)
78 78
79 79 # do not inline the len() or on error pytest will try to print the 130 000 +
80 80 # elements.
81 81 message = None
82 82 if len_exp != len_test or len_exp > 131808:
83 83 size, start, stop, prct = recompute_unicode_ranges()
84 84 message = f"""_UNICODE_RANGES likely wrong and need updating. This is
85 85 likely due to a new release of Python. We've find that the biggest gap
86 86 in unicode characters has reduces in size to be {size} characters
87 87 ({prct}), from {start}, to {stop}. In completer.py likely update to
88 88
89 89 _UNICODE_RANGES = [(32, {start}), ({stop}, 0xe01f0)]
90 90
91 91 And update the assertion below to use
92 92
93 93 len_exp <= {len_exp}
94 94 """
95 95 assert len_exp == len_test, message
96 96
97 97 # fail if new unicode symbols have been added.
98 98 assert len_exp <= 137714, message
99 99
100 100
101 101 @contextmanager
102 102 def greedy_completion():
103 103 ip = get_ipython()
104 104 greedy_original = ip.Completer.greedy
105 105 try:
106 106 ip.Completer.greedy = True
107 107 yield
108 108 finally:
109 109 ip.Completer.greedy = greedy_original
110 110
111 111
112 112 def test_protect_filename():
113 113 if sys.platform == "win32":
114 114 pairs = [
115 115 ("abc", "abc"),
116 116 (" abc", '" abc"'),
117 117 ("a bc", '"a bc"'),
118 118 ("a bc", '"a bc"'),
119 119 (" bc", '" bc"'),
120 120 ]
121 121 else:
122 122 pairs = [
123 123 ("abc", "abc"),
124 124 (" abc", r"\ abc"),
125 125 ("a bc", r"a\ bc"),
126 126 ("a bc", r"a\ \ bc"),
127 127 (" bc", r"\ \ bc"),
128 128 # On posix, we also protect parens and other special characters.
129 129 ("a(bc", r"a\(bc"),
130 130 ("a)bc", r"a\)bc"),
131 131 ("a( )bc", r"a\(\ \)bc"),
132 132 ("a[1]bc", r"a\[1\]bc"),
133 133 ("a{1}bc", r"a\{1\}bc"),
134 134 ("a#bc", r"a\#bc"),
135 135 ("a?bc", r"a\?bc"),
136 136 ("a=bc", r"a\=bc"),
137 137 ("a\\bc", r"a\\bc"),
138 138 ("a|bc", r"a\|bc"),
139 139 ("a;bc", r"a\;bc"),
140 140 ("a:bc", r"a\:bc"),
141 141 ("a'bc", r"a\'bc"),
142 142 ("a*bc", r"a\*bc"),
143 143 ('a"bc', r"a\"bc"),
144 144 ("a^bc", r"a\^bc"),
145 145 ("a&bc", r"a\&bc"),
146 146 ]
147 147 # run the actual tests
148 148 for s1, s2 in pairs:
149 149 s1p = completer.protect_filename(s1)
150 150 assert s1p == s2
151 151
152 152
153 153 def check_line_split(splitter, test_specs):
154 154 for part1, part2, split in test_specs:
155 155 cursor_pos = len(part1)
156 156 line = part1 + part2
157 157 out = splitter.split_line(line, cursor_pos)
158 158 assert out == split
159 159
160 160
161 161 def test_line_split():
162 162 """Basic line splitter test with default specs."""
163 163 sp = completer.CompletionSplitter()
164 164 # The format of the test specs is: part1, part2, expected answer. Parts 1
165 165 # and 2 are joined into the 'line' sent to the splitter, as if the cursor
166 166 # was at the end of part1. So an empty part2 represents someone hitting
167 167 # tab at the end of the line, the most common case.
168 168 t = [
169 169 ("run some/scrip", "", "some/scrip"),
170 170 ("run scripts/er", "ror.py foo", "scripts/er"),
171 171 ("echo $HOM", "", "HOM"),
172 172 ("print sys.pa", "", "sys.pa"),
173 173 ("print(sys.pa", "", "sys.pa"),
174 174 ("execfile('scripts/er", "", "scripts/er"),
175 175 ("a[x.", "", "x."),
176 176 ("a[x.", "y", "x."),
177 177 ('cd "some_file/', "", "some_file/"),
178 178 ]
179 179 check_line_split(sp, t)
180 180 # Ensure splitting works OK with unicode by re-running the tests with
181 181 # all inputs turned into unicode
182 182 check_line_split(sp, [map(str, p) for p in t])
183 183
184 184
185 185 class NamedInstanceMetaclass(type):
186 186 def __getitem__(cls, item):
187 187 return cls.get_instance(item)
188 188
189 189
190 190 class NamedInstanceClass(metaclass=NamedInstanceMetaclass):
191 191 def __init__(self, name):
192 192 if not hasattr(self.__class__, "instances"):
193 193 self.__class__.instances = {}
194 194 self.__class__.instances[name] = self
195 195
196 196 @classmethod
197 197 def _ipython_key_completions_(cls):
198 198 return cls.instances.keys()
199 199
200 200 @classmethod
201 201 def get_instance(cls, name):
202 202 return cls.instances[name]
203 203
204 204
205 205 class KeyCompletable:
206 206 def __init__(self, things=()):
207 207 self.things = things
208 208
209 209 def _ipython_key_completions_(self):
210 210 return list(self.things)
211 211
212 212
213 213 class TestCompleter(unittest.TestCase):
214 214 def setUp(self):
215 215 """
216 216 We want to silence all PendingDeprecationWarning when testing the completer
217 217 """
218 218 self._assertwarns = self.assertWarns(PendingDeprecationWarning)
219 219 self._assertwarns.__enter__()
220 220
221 221 def tearDown(self):
222 222 try:
223 223 self._assertwarns.__exit__(None, None, None)
224 224 except AssertionError:
225 225 pass
226 226
227 227 def test_custom_completion_error(self):
228 228 """Test that errors from custom attribute completers are silenced."""
229 229 ip = get_ipython()
230 230
231 231 class A:
232 232 pass
233 233
234 234 ip.user_ns["x"] = A()
235 235
236 236 @complete_object.register(A)
237 237 def complete_A(a, existing_completions):
238 238 raise TypeError("this should be silenced")
239 239
240 240 ip.complete("x.")
241 241
242 242 def test_custom_completion_ordering(self):
243 243 """Test that errors from custom attribute completers are silenced."""
244 244 ip = get_ipython()
245 245
246 246 _, matches = ip.complete('in')
247 247 assert matches.index('input') < matches.index('int')
248 248
249 249 def complete_example(a):
250 250 return ['example2', 'example1']
251 251
252 252 ip.Completer.custom_completers.add_re('ex*', complete_example)
253 253 _, matches = ip.complete('ex')
254 254 assert matches.index('example2') < matches.index('example1')
255 255
256 256 def test_unicode_completions(self):
257 257 ip = get_ipython()
258 258 # Some strings that trigger different types of completion. Check them both
259 259 # in str and unicode forms
260 260 s = ["ru", "%ru", "cd /", "floa", "float(x)/"]
261 261 for t in s + list(map(str, s)):
262 262 # We don't need to check exact completion values (they may change
263 263 # depending on the state of the namespace, but at least no exceptions
264 264 # should be thrown and the return value should be a pair of text, list
265 265 # values.
266 266 text, matches = ip.complete(t)
267 267 self.assertIsInstance(text, str)
268 268 self.assertIsInstance(matches, list)
269 269
270 270 def test_latex_completions(self):
271 271 from IPython.core.latex_symbols import latex_symbols
272 272 import random
273 273
274 274 ip = get_ipython()
275 275 # Test some random unicode symbols
276 276 keys = random.sample(latex_symbols.keys(), 10)
277 277 for k in keys:
278 278 text, matches = ip.complete(k)
279 279 self.assertEqual(text, k)
280 280 self.assertEqual(matches, [latex_symbols[k]])
281 281 # Test a more complex line
282 282 text, matches = ip.complete("print(\\alpha")
283 283 self.assertEqual(text, "\\alpha")
284 284 self.assertEqual(matches[0], latex_symbols["\\alpha"])
285 285 # Test multiple matching latex symbols
286 286 text, matches = ip.complete("\\al")
287 287 self.assertIn("\\alpha", matches)
288 288 self.assertIn("\\aleph", matches)
289 289
290 290 def test_latex_no_results(self):
291 291 """
292 292 forward latex should really return nothing in either field if nothing is found.
293 293 """
294 294 ip = get_ipython()
295 295 text, matches = ip.Completer.latex_matches("\\really_i_should_match_nothing")
296 296 self.assertEqual(text, "")
297 297 self.assertEqual(matches, ())
298 298
299 299 def test_back_latex_completion(self):
300 300 ip = get_ipython()
301 301
302 302 # do not return more than 1 matches for \beta, only the latex one.
303 303 name, matches = ip.complete("\\β")
304 304 self.assertEqual(matches, ["\\beta"])
305 305
306 306 def test_back_unicode_completion(self):
307 307 ip = get_ipython()
308 308
309 309 name, matches = ip.complete("\\Ⅴ")
310 310 self.assertEqual(matches, ("\\ROMAN NUMERAL FIVE",))
311 311
312 312 def test_forward_unicode_completion(self):
313 313 ip = get_ipython()
314 314
315 315 name, matches = ip.complete("\\ROMAN NUMERAL FIVE")
316 316 self.assertEqual(matches, ["Ⅴ"]) # This is not a V
317 317 self.assertEqual(matches, ["\u2164"]) # same as above but explicit.
318 318
319 319 @unittest.skip("now we have a completion for \jmath")
320 @decorators.knownfailureif(
321 sys.platform == "win32", "Fails if there is a C:\\j... path"
320 @pytest.mark.xfail(
321 sys.platform == "win32", reason="Fails if there is a C:\\j... path"
322 322 )
323 323 def test_no_ascii_back_completion(self):
324 324 ip = get_ipython()
325 325 with TemporaryWorkingDirectory(): # Avoid any filename completions
326 326 # single ascii letter that don't have yet completions
327 327 for letter in "jJ":
328 328 name, matches = ip.complete("\\" + letter)
329 329 self.assertEqual(matches, [])
330 330
331 331 def test_delim_setting(self):
332 332 sp = completer.CompletionSplitter()
333 333 sp.delims = " "
334 334 self.assertEqual(sp.delims, " ")
335 335 self.assertEqual(sp._delim_expr, r"[\ ]")
336 336
337 337 def test_spaces(self):
338 338 """Test with only spaces as split chars."""
339 339 sp = completer.CompletionSplitter()
340 340 sp.delims = " "
341 341 t = [("foo", "", "foo"), ("run foo", "", "foo"), ("run foo", "bar", "foo")]
342 342 check_line_split(sp, t)
343 343
344 344 def test_has_open_quotes1(self):
345 345 for s in ["'", "'''", "'hi' '"]:
346 346 self.assertEqual(completer.has_open_quotes(s), "'")
347 347
348 348 def test_has_open_quotes2(self):
349 349 for s in ['"', '"""', '"hi" "']:
350 350 self.assertEqual(completer.has_open_quotes(s), '"')
351 351
352 352 def test_has_open_quotes3(self):
353 353 for s in ["''", "''' '''", "'hi' 'ipython'"]:
354 354 self.assertFalse(completer.has_open_quotes(s))
355 355
356 356 def test_has_open_quotes4(self):
357 357 for s in ['""', '""" """', '"hi" "ipython"']:
358 358 self.assertFalse(completer.has_open_quotes(s))
359 359
360 @decorators.knownfailureif(
361 sys.platform == "win32", "abspath completions fail on Windows"
360 @pytest.mark.xfail(
361 sys.platform == "win32", reason="abspath completions fail on Windows"
362 362 )
363 363 def test_abspath_file_completions(self):
364 364 ip = get_ipython()
365 365 with TemporaryDirectory() as tmpdir:
366 366 prefix = os.path.join(tmpdir, "foo")
367 367 suffixes = ["1", "2"]
368 368 names = [prefix + s for s in suffixes]
369 369 for n in names:
370 370 open(n, "w").close()
371 371
372 372 # Check simple completion
373 373 c = ip.complete(prefix)[1]
374 374 self.assertEqual(c, names)
375 375
376 376 # Now check with a function call
377 377 cmd = 'a = f("%s' % prefix
378 378 c = ip.complete(prefix, cmd)[1]
379 379 comp = [prefix + s for s in suffixes]
380 380 self.assertEqual(c, comp)
381 381
382 382 def test_local_file_completions(self):
383 383 ip = get_ipython()
384 384 with TemporaryWorkingDirectory():
385 385 prefix = "./foo"
386 386 suffixes = ["1", "2"]
387 387 names = [prefix + s for s in suffixes]
388 388 for n in names:
389 389 open(n, "w").close()
390 390
391 391 # Check simple completion
392 392 c = ip.complete(prefix)[1]
393 393 self.assertEqual(c, names)
394 394
395 395 # Now check with a function call
396 396 cmd = 'a = f("%s' % prefix
397 397 c = ip.complete(prefix, cmd)[1]
398 398 comp = {prefix + s for s in suffixes}
399 399 self.assertTrue(comp.issubset(set(c)))
400 400
401 401 def test_quoted_file_completions(self):
402 402 ip = get_ipython()
403 403 with TemporaryWorkingDirectory():
404 404 name = "foo'bar"
405 405 open(name, "w").close()
406 406
407 407 # Don't escape Windows
408 408 escaped = name if sys.platform == "win32" else "foo\\'bar"
409 409
410 410 # Single quote matches embedded single quote
411 411 text = "open('foo"
412 412 c = ip.Completer._complete(
413 413 cursor_line=0, cursor_pos=len(text), full_text=text
414 414 )[1]
415 415 self.assertEqual(c, [escaped])
416 416
417 417 # Double quote requires no escape
418 418 text = 'open("foo'
419 419 c = ip.Completer._complete(
420 420 cursor_line=0, cursor_pos=len(text), full_text=text
421 421 )[1]
422 422 self.assertEqual(c, [name])
423 423
424 424 # No quote requires an escape
425 425 text = "%ls foo"
426 426 c = ip.Completer._complete(
427 427 cursor_line=0, cursor_pos=len(text), full_text=text
428 428 )[1]
429 429 self.assertEqual(c, [escaped])
430 430
431 431 def test_all_completions_dups(self):
432 432 """
433 433 Make sure the output of `IPCompleter.all_completions` does not have
434 434 duplicated prefixes.
435 435 """
436 436 ip = get_ipython()
437 437 c = ip.Completer
438 438 ip.ex("class TestClass():\n\ta=1\n\ta1=2")
439 439 for jedi_status in [True, False]:
440 440 with provisionalcompleter():
441 441 ip.Completer.use_jedi = jedi_status
442 442 matches = c.all_completions("TestCl")
443 443 assert matches == ['TestClass'], jedi_status
444 444 matches = c.all_completions("TestClass.")
445 445 assert len(matches) > 2, jedi_status
446 446 matches = c.all_completions("TestClass.a")
447 447 assert matches == ['TestClass.a', 'TestClass.a1'], jedi_status
448 448
449 449 def test_jedi(self):
450 450 """
451 451 A couple of issue we had with Jedi
452 452 """
453 453 ip = get_ipython()
454 454
455 455 def _test_complete(reason, s, comp, start=None, end=None):
456 456 l = len(s)
457 457 start = start if start is not None else l
458 458 end = end if end is not None else l
459 459 with provisionalcompleter():
460 460 ip.Completer.use_jedi = True
461 461 completions = set(ip.Completer.completions(s, l))
462 462 ip.Completer.use_jedi = False
463 463 assert Completion(start, end, comp) in completions, reason
464 464
465 465 def _test_not_complete(reason, s, comp):
466 466 l = len(s)
467 467 with provisionalcompleter():
468 468 ip.Completer.use_jedi = True
469 469 completions = set(ip.Completer.completions(s, l))
470 470 ip.Completer.use_jedi = False
471 471 assert Completion(l, l, comp) not in completions, reason
472 472
473 473 import jedi
474 474
475 475 jedi_version = tuple(int(i) for i in jedi.__version__.split(".")[:3])
476 476 if jedi_version > (0, 10):
477 477 _test_complete("jedi >0.9 should complete and not crash", "a=1;a.", "real")
478 478 _test_complete("can infer first argument", 'a=(1,"foo");a[0].', "real")
479 479 _test_complete("can infer second argument", 'a=(1,"foo");a[1].', "capitalize")
480 480 _test_complete("cover duplicate completions", "im", "import", 0, 2)
481 481
482 482 _test_not_complete("does not mix types", 'a=(1,"foo");a[0].', "capitalize")
483 483
484 484 def test_completion_have_signature(self):
485 485 """
486 486 Lets make sure jedi is capable of pulling out the signature of the function we are completing.
487 487 """
488 488 ip = get_ipython()
489 489 with provisionalcompleter():
490 490 ip.Completer.use_jedi = True
491 491 completions = ip.Completer.completions("ope", 3)
492 492 c = next(completions) # should be `open`
493 493 ip.Completer.use_jedi = False
494 494 assert "file" in c.signature, "Signature of function was not found by completer"
495 495 assert (
496 496 "encoding" in c.signature
497 497 ), "Signature of function was not found by completer"
498 498
499 499 def test_deduplicate_completions(self):
500 500 """
501 501 Test that completions are correctly deduplicated (even if ranges are not the same)
502 502 """
503 503 ip = get_ipython()
504 504 ip.ex(
505 505 textwrap.dedent(
506 506 """
507 507 class Z:
508 508 zoo = 1
509 509 """
510 510 )
511 511 )
512 512 with provisionalcompleter():
513 513 ip.Completer.use_jedi = True
514 514 l = list(
515 515 _deduplicate_completions("Z.z", ip.Completer.completions("Z.z", 3))
516 516 )
517 517 ip.Completer.use_jedi = False
518 518
519 519 assert len(l) == 1, "Completions (Z.z<tab>) correctly deduplicate: %s " % l
520 520 assert l[0].text == "zoo" # and not `it.accumulate`
521 521
522 522 def test_greedy_completions(self):
523 523 """
524 524 Test the capability of the Greedy completer.
525 525
526 526 Most of the test here does not really show off the greedy completer, for proof
527 527 each of the text below now pass with Jedi. The greedy completer is capable of more.
528 528
529 529 See the :any:`test_dict_key_completion_contexts`
530 530
531 531 """
532 532 ip = get_ipython()
533 533 ip.ex("a=list(range(5))")
534 534 _, c = ip.complete(".", line="a[0].")
535 535 self.assertFalse(".real" in c, "Shouldn't have completed on a[0]: %s" % c)
536 536
537 537 def _(line, cursor_pos, expect, message, completion):
538 538 with greedy_completion(), provisionalcompleter():
539 539 ip.Completer.use_jedi = False
540 540 _, c = ip.complete(".", line=line, cursor_pos=cursor_pos)
541 541 self.assertIn(expect, c, message % c)
542 542
543 543 ip.Completer.use_jedi = True
544 544 with provisionalcompleter():
545 545 completions = ip.Completer.completions(line, cursor_pos)
546 546 self.assertIn(completion, completions)
547 547
548 548 with provisionalcompleter():
549 549 _(
550 550 "a[0].",
551 551 5,
552 552 "a[0].real",
553 553 "Should have completed on a[0].: %s",
554 554 Completion(5, 5, "real"),
555 555 )
556 556 _(
557 557 "a[0].r",
558 558 6,
559 559 "a[0].real",
560 560 "Should have completed on a[0].r: %s",
561 561 Completion(5, 6, "real"),
562 562 )
563 563
564 564 _(
565 565 "a[0].from_",
566 566 10,
567 567 "a[0].from_bytes",
568 568 "Should have completed on a[0].from_: %s",
569 569 Completion(5, 10, "from_bytes"),
570 570 )
571 571
572 572 def test_omit__names(self):
573 573 # also happens to test IPCompleter as a configurable
574 574 ip = get_ipython()
575 575 ip._hidden_attr = 1
576 576 ip._x = {}
577 577 c = ip.Completer
578 578 ip.ex("ip=get_ipython()")
579 579 cfg = Config()
580 580 cfg.IPCompleter.omit__names = 0
581 581 c.update_config(cfg)
582 582 with provisionalcompleter():
583 583 c.use_jedi = False
584 584 s, matches = c.complete("ip.")
585 585 self.assertIn("ip.__str__", matches)
586 586 self.assertIn("ip._hidden_attr", matches)
587 587
588 588 # c.use_jedi = True
589 589 # completions = set(c.completions('ip.', 3))
590 590 # self.assertIn(Completion(3, 3, '__str__'), completions)
591 591 # self.assertIn(Completion(3,3, "_hidden_attr"), completions)
592 592
593 593 cfg = Config()
594 594 cfg.IPCompleter.omit__names = 1
595 595 c.update_config(cfg)
596 596 with provisionalcompleter():
597 597 c.use_jedi = False
598 598 s, matches = c.complete("ip.")
599 599 self.assertNotIn("ip.__str__", matches)
600 600 # self.assertIn('ip._hidden_attr', matches)
601 601
602 602 # c.use_jedi = True
603 603 # completions = set(c.completions('ip.', 3))
604 604 # self.assertNotIn(Completion(3,3,'__str__'), completions)
605 605 # self.assertIn(Completion(3,3, "_hidden_attr"), completions)
606 606
607 607 cfg = Config()
608 608 cfg.IPCompleter.omit__names = 2
609 609 c.update_config(cfg)
610 610 with provisionalcompleter():
611 611 c.use_jedi = False
612 612 s, matches = c.complete("ip.")
613 613 self.assertNotIn("ip.__str__", matches)
614 614 self.assertNotIn("ip._hidden_attr", matches)
615 615
616 616 # c.use_jedi = True
617 617 # completions = set(c.completions('ip.', 3))
618 618 # self.assertNotIn(Completion(3,3,'__str__'), completions)
619 619 # self.assertNotIn(Completion(3,3, "_hidden_attr"), completions)
620 620
621 621 with provisionalcompleter():
622 622 c.use_jedi = False
623 623 s, matches = c.complete("ip._x.")
624 624 self.assertIn("ip._x.keys", matches)
625 625
626 626 # c.use_jedi = True
627 627 # completions = set(c.completions('ip._x.', 6))
628 628 # self.assertIn(Completion(6,6, "keys"), completions)
629 629
630 630 del ip._hidden_attr
631 631 del ip._x
632 632
633 633 def test_limit_to__all__False_ok(self):
634 634 """
635 635 Limit to all is deprecated, once we remove it this test can go away.
636 636 """
637 637 ip = get_ipython()
638 638 c = ip.Completer
639 639 c.use_jedi = False
640 640 ip.ex("class D: x=24")
641 641 ip.ex("d=D()")
642 642 cfg = Config()
643 643 cfg.IPCompleter.limit_to__all__ = False
644 644 c.update_config(cfg)
645 645 s, matches = c.complete("d.")
646 646 self.assertIn("d.x", matches)
647 647
648 648 def test_get__all__entries_ok(self):
649 649 class A:
650 650 __all__ = ["x", 1]
651 651
652 652 words = completer.get__all__entries(A())
653 653 self.assertEqual(words, ["x"])
654 654
655 655 def test_get__all__entries_no__all__ok(self):
656 656 class A:
657 657 pass
658 658
659 659 words = completer.get__all__entries(A())
660 660 self.assertEqual(words, [])
661 661
662 662 def test_func_kw_completions(self):
663 663 ip = get_ipython()
664 664 c = ip.Completer
665 665 c.use_jedi = False
666 666 ip.ex("def myfunc(a=1,b=2): return a+b")
667 667 s, matches = c.complete(None, "myfunc(1,b")
668 668 self.assertIn("b=", matches)
669 669 # Simulate completing with cursor right after b (pos==10):
670 670 s, matches = c.complete(None, "myfunc(1,b)", 10)
671 671 self.assertIn("b=", matches)
672 672 s, matches = c.complete(None, 'myfunc(a="escaped\\")string",b')
673 673 self.assertIn("b=", matches)
674 674 # builtin function
675 675 s, matches = c.complete(None, "min(k, k")
676 676 self.assertIn("key=", matches)
677 677
678 678 def test_default_arguments_from_docstring(self):
679 679 ip = get_ipython()
680 680 c = ip.Completer
681 681 kwd = c._default_arguments_from_docstring("min(iterable[, key=func]) -> value")
682 682 self.assertEqual(kwd, ["key"])
683 683 # with cython type etc
684 684 kwd = c._default_arguments_from_docstring(
685 685 "Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n"
686 686 )
687 687 self.assertEqual(kwd, ["ncall", "resume", "nsplit"])
688 688 # white spaces
689 689 kwd = c._default_arguments_from_docstring(
690 690 "\n Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n"
691 691 )
692 692 self.assertEqual(kwd, ["ncall", "resume", "nsplit"])
693 693
694 694 def test_line_magics(self):
695 695 ip = get_ipython()
696 696 c = ip.Completer
697 697 s, matches = c.complete(None, "lsmag")
698 698 self.assertIn("%lsmagic", matches)
699 699 s, matches = c.complete(None, "%lsmag")
700 700 self.assertIn("%lsmagic", matches)
701 701
702 702 def test_cell_magics(self):
703 703 from IPython.core.magic import register_cell_magic
704 704
705 705 @register_cell_magic
706 706 def _foo_cellm(line, cell):
707 707 pass
708 708
709 709 ip = get_ipython()
710 710 c = ip.Completer
711 711
712 712 s, matches = c.complete(None, "_foo_ce")
713 713 self.assertIn("%%_foo_cellm", matches)
714 714 s, matches = c.complete(None, "%%_foo_ce")
715 715 self.assertIn("%%_foo_cellm", matches)
716 716
717 717 def test_line_cell_magics(self):
718 718 from IPython.core.magic import register_line_cell_magic
719 719
720 720 @register_line_cell_magic
721 721 def _bar_cellm(line, cell):
722 722 pass
723 723
724 724 ip = get_ipython()
725 725 c = ip.Completer
726 726
727 727 # The policy here is trickier, see comments in completion code. The
728 728 # returned values depend on whether the user passes %% or not explicitly,
729 729 # and this will show a difference if the same name is both a line and cell
730 730 # magic.
731 731 s, matches = c.complete(None, "_bar_ce")
732 732 self.assertIn("%_bar_cellm", matches)
733 733 self.assertIn("%%_bar_cellm", matches)
734 734 s, matches = c.complete(None, "%_bar_ce")
735 735 self.assertIn("%_bar_cellm", matches)
736 736 self.assertIn("%%_bar_cellm", matches)
737 737 s, matches = c.complete(None, "%%_bar_ce")
738 738 self.assertNotIn("%_bar_cellm", matches)
739 739 self.assertIn("%%_bar_cellm", matches)
740 740
741 741 def test_magic_completion_order(self):
742 742 ip = get_ipython()
743 743 c = ip.Completer
744 744
745 745 # Test ordering of line and cell magics.
746 746 text, matches = c.complete("timeit")
747 747 self.assertEqual(matches, ["%timeit", "%%timeit"])
748 748
749 749 def test_magic_completion_shadowing(self):
750 750 ip = get_ipython()
751 751 c = ip.Completer
752 752 c.use_jedi = False
753 753
754 754 # Before importing matplotlib, %matplotlib magic should be the only option.
755 755 text, matches = c.complete("mat")
756 756 self.assertEqual(matches, ["%matplotlib"])
757 757
758 758 # The newly introduced name should shadow the magic.
759 759 ip.run_cell("matplotlib = 1")
760 760 text, matches = c.complete("mat")
761 761 self.assertEqual(matches, ["matplotlib"])
762 762
763 763 # After removing matplotlib from namespace, the magic should again be
764 764 # the only option.
765 765 del ip.user_ns["matplotlib"]
766 766 text, matches = c.complete("mat")
767 767 self.assertEqual(matches, ["%matplotlib"])
768 768
769 769 def test_magic_completion_shadowing_explicit(self):
770 770 """
771 771 If the user try to complete a shadowed magic, and explicit % start should
772 772 still return the completions.
773 773 """
774 774 ip = get_ipython()
775 775 c = ip.Completer
776 776
777 777 # Before importing matplotlib, %matplotlib magic should be the only option.
778 778 text, matches = c.complete("%mat")
779 779 self.assertEqual(matches, ["%matplotlib"])
780 780
781 781 ip.run_cell("matplotlib = 1")
782 782
783 783 # After removing matplotlib from namespace, the magic should still be
784 784 # the only option.
785 785 text, matches = c.complete("%mat")
786 786 self.assertEqual(matches, ["%matplotlib"])
787 787
788 788 def test_magic_config(self):
789 789 ip = get_ipython()
790 790 c = ip.Completer
791 791
792 792 s, matches = c.complete(None, "conf")
793 793 self.assertIn("%config", matches)
794 794 s, matches = c.complete(None, "conf")
795 795 self.assertNotIn("AliasManager", matches)
796 796 s, matches = c.complete(None, "config ")
797 797 self.assertIn("AliasManager", matches)
798 798 s, matches = c.complete(None, "%config ")
799 799 self.assertIn("AliasManager", matches)
800 800 s, matches = c.complete(None, "config Ali")
801 801 self.assertListEqual(["AliasManager"], matches)
802 802 s, matches = c.complete(None, "%config Ali")
803 803 self.assertListEqual(["AliasManager"], matches)
804 804 s, matches = c.complete(None, "config AliasManager")
805 805 self.assertListEqual(["AliasManager"], matches)
806 806 s, matches = c.complete(None, "%config AliasManager")
807 807 self.assertListEqual(["AliasManager"], matches)
808 808 s, matches = c.complete(None, "config AliasManager.")
809 809 self.assertIn("AliasManager.default_aliases", matches)
810 810 s, matches = c.complete(None, "%config AliasManager.")
811 811 self.assertIn("AliasManager.default_aliases", matches)
812 812 s, matches = c.complete(None, "config AliasManager.de")
813 813 self.assertListEqual(["AliasManager.default_aliases"], matches)
814 814 s, matches = c.complete(None, "config AliasManager.de")
815 815 self.assertListEqual(["AliasManager.default_aliases"], matches)
816 816
817 817 def test_magic_color(self):
818 818 ip = get_ipython()
819 819 c = ip.Completer
820 820
821 821 s, matches = c.complete(None, "colo")
822 822 self.assertIn("%colors", matches)
823 823 s, matches = c.complete(None, "colo")
824 824 self.assertNotIn("NoColor", matches)
825 825 s, matches = c.complete(None, "%colors") # No trailing space
826 826 self.assertNotIn("NoColor", matches)
827 827 s, matches = c.complete(None, "colors ")
828 828 self.assertIn("NoColor", matches)
829 829 s, matches = c.complete(None, "%colors ")
830 830 self.assertIn("NoColor", matches)
831 831 s, matches = c.complete(None, "colors NoCo")
832 832 self.assertListEqual(["NoColor"], matches)
833 833 s, matches = c.complete(None, "%colors NoCo")
834 834 self.assertListEqual(["NoColor"], matches)
835 835
836 836 def test_match_dict_keys(self):
837 837 """
838 838 Test that match_dict_keys works on a couple of use case does return what
839 839 expected, and does not crash
840 840 """
841 841 delims = " \t\n`!@#$^&*()=+[{]}\\|;:'\",<>?"
842 842
843 843 keys = ["foo", b"far"]
844 844 assert match_dict_keys(keys, "b'", delims=delims) == ("'", 2, ["far"])
845 845 assert match_dict_keys(keys, "b'f", delims=delims) == ("'", 2, ["far"])
846 846 assert match_dict_keys(keys, 'b"', delims=delims) == ('"', 2, ["far"])
847 847 assert match_dict_keys(keys, 'b"f', delims=delims) == ('"', 2, ["far"])
848 848
849 849 assert match_dict_keys(keys, "'", delims=delims) == ("'", 1, ["foo"])
850 850 assert match_dict_keys(keys, "'f", delims=delims) == ("'", 1, ["foo"])
851 851 assert match_dict_keys(keys, '"', delims=delims) == ('"', 1, ["foo"])
852 852 assert match_dict_keys(keys, '"f', delims=delims) == ('"', 1, ["foo"])
853 853
854 854 match_dict_keys
855 855
856 856 def test_match_dict_keys_tuple(self):
857 857 """
858 858 Test that match_dict_keys called with extra prefix works on a couple of use case,
859 859 does return what expected, and does not crash.
860 860 """
861 861 delims = " \t\n`!@#$^&*()=+[{]}\\|;:'\",<>?"
862 862
863 863 keys = [("foo", "bar"), ("foo", "oof"), ("foo", b"bar"), ('other', 'test')]
864 864
865 865 # Completion on first key == "foo"
866 866 assert match_dict_keys(keys, "'", delims=delims, extra_prefix=("foo",)) == ("'", 1, ["bar", "oof"])
867 867 assert match_dict_keys(keys, "\"", delims=delims, extra_prefix=("foo",)) == ("\"", 1, ["bar", "oof"])
868 868 assert match_dict_keys(keys, "'o", delims=delims, extra_prefix=("foo",)) == ("'", 1, ["oof"])
869 869 assert match_dict_keys(keys, "\"o", delims=delims, extra_prefix=("foo",)) == ("\"", 1, ["oof"])
870 870 assert match_dict_keys(keys, "b'", delims=delims, extra_prefix=("foo",)) == ("'", 2, ["bar"])
871 871 assert match_dict_keys(keys, "b\"", delims=delims, extra_prefix=("foo",)) == ("\"", 2, ["bar"])
872 872 assert match_dict_keys(keys, "b'b", delims=delims, extra_prefix=("foo",)) == ("'", 2, ["bar"])
873 873 assert match_dict_keys(keys, "b\"b", delims=delims, extra_prefix=("foo",)) == ("\"", 2, ["bar"])
874 874
875 875 # No Completion
876 876 assert match_dict_keys(keys, "'", delims=delims, extra_prefix=("no_foo",)) == ("'", 1, [])
877 877 assert match_dict_keys(keys, "'", delims=delims, extra_prefix=("fo",)) == ("'", 1, [])
878 878
879 879 keys = [('foo1', 'foo2', 'foo3', 'foo4'), ('foo1', 'foo2', 'bar', 'foo4')]
880 880 assert match_dict_keys(keys, "'foo", delims=delims, extra_prefix=('foo1',)) == ("'", 1, ["foo2", "foo2"])
881 881 assert match_dict_keys(keys, "'foo", delims=delims, extra_prefix=('foo1', 'foo2')) == ("'", 1, ["foo3"])
882 882 assert match_dict_keys(keys, "'foo", delims=delims, extra_prefix=('foo1', 'foo2', 'foo3')) == ("'", 1, ["foo4"])
883 883 assert match_dict_keys(keys, "'foo", delims=delims, extra_prefix=('foo1', 'foo2', 'foo3', 'foo4')) == ("'", 1, [])
884 884
885 885 def test_dict_key_completion_string(self):
886 886 """Test dictionary key completion for string keys"""
887 887 ip = get_ipython()
888 888 complete = ip.Completer.complete
889 889
890 890 ip.user_ns["d"] = {"abc": None}
891 891
892 892 # check completion at different stages
893 893 _, matches = complete(line_buffer="d[")
894 894 self.assertIn("'abc'", matches)
895 895 self.assertNotIn("'abc']", matches)
896 896
897 897 _, matches = complete(line_buffer="d['")
898 898 self.assertIn("abc", matches)
899 899 self.assertNotIn("abc']", matches)
900 900
901 901 _, matches = complete(line_buffer="d['a")
902 902 self.assertIn("abc", matches)
903 903 self.assertNotIn("abc']", matches)
904 904
905 905 # check use of different quoting
906 906 _, matches = complete(line_buffer='d["')
907 907 self.assertIn("abc", matches)
908 908 self.assertNotIn('abc"]', matches)
909 909
910 910 _, matches = complete(line_buffer='d["a')
911 911 self.assertIn("abc", matches)
912 912 self.assertNotIn('abc"]', matches)
913 913
914 914 # check sensitivity to following context
915 915 _, matches = complete(line_buffer="d[]", cursor_pos=2)
916 916 self.assertIn("'abc'", matches)
917 917
918 918 _, matches = complete(line_buffer="d['']", cursor_pos=3)
919 919 self.assertIn("abc", matches)
920 920 self.assertNotIn("abc'", matches)
921 921 self.assertNotIn("abc']", matches)
922 922
923 923 # check multiple solutions are correctly returned and that noise is not
924 924 ip.user_ns["d"] = {
925 925 "abc": None,
926 926 "abd": None,
927 927 "bad": None,
928 928 object(): None,
929 929 5: None,
930 930 ("abe", None): None,
931 931 (None, "abf"): None
932 932 }
933 933
934 934 _, matches = complete(line_buffer="d['a")
935 935 self.assertIn("abc", matches)
936 936 self.assertIn("abd", matches)
937 937 self.assertNotIn("bad", matches)
938 938 self.assertNotIn("abe", matches)
939 939 self.assertNotIn("abf", matches)
940 940 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
941 941
942 942 # check escaping and whitespace
943 943 ip.user_ns["d"] = {"a\nb": None, "a'b": None, 'a"b': None, "a word": None}
944 944 _, matches = complete(line_buffer="d['a")
945 945 self.assertIn("a\\nb", matches)
946 946 self.assertIn("a\\'b", matches)
947 947 self.assertIn('a"b', matches)
948 948 self.assertIn("a word", matches)
949 949 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
950 950
951 951 # - can complete on non-initial word of the string
952 952 _, matches = complete(line_buffer="d['a w")
953 953 self.assertIn("word", matches)
954 954
955 955 # - understands quote escaping
956 956 _, matches = complete(line_buffer="d['a\\'")
957 957 self.assertIn("b", matches)
958 958
959 959 # - default quoting should work like repr
960 960 _, matches = complete(line_buffer="d[")
961 961 self.assertIn('"a\'b"', matches)
962 962
963 963 # - when opening quote with ", possible to match with unescaped apostrophe
964 964 _, matches = complete(line_buffer="d[\"a'")
965 965 self.assertIn("b", matches)
966 966
967 967 # need to not split at delims that readline won't split at
968 968 if "-" not in ip.Completer.splitter.delims:
969 969 ip.user_ns["d"] = {"before-after": None}
970 970 _, matches = complete(line_buffer="d['before-af")
971 971 self.assertIn("before-after", matches)
972 972
973 973 # check completion on tuple-of-string keys at different stage - on first key
974 974 ip.user_ns["d"] = {('foo', 'bar'): None}
975 975 _, matches = complete(line_buffer="d[")
976 976 self.assertIn("'foo'", matches)
977 977 self.assertNotIn("'foo']", matches)
978 978 self.assertNotIn("'bar'", matches)
979 979 self.assertNotIn("foo", matches)
980 980 self.assertNotIn("bar", matches)
981 981
982 982 # - match the prefix
983 983 _, matches = complete(line_buffer="d['f")
984 984 self.assertIn("foo", matches)
985 985 self.assertNotIn("foo']", matches)
986 986 self.assertNotIn('foo"]', matches)
987 987 _, matches = complete(line_buffer="d['foo")
988 988 self.assertIn("foo", matches)
989 989
990 990 # - can complete on second key
991 991 _, matches = complete(line_buffer="d['foo', ")
992 992 self.assertIn("'bar'", matches)
993 993 _, matches = complete(line_buffer="d['foo', 'b")
994 994 self.assertIn("bar", matches)
995 995 self.assertNotIn("foo", matches)
996 996
997 997 # - does not propose missing keys
998 998 _, matches = complete(line_buffer="d['foo', 'f")
999 999 self.assertNotIn("bar", matches)
1000 1000 self.assertNotIn("foo", matches)
1001 1001
1002 1002 # check sensitivity to following context
1003 1003 _, matches = complete(line_buffer="d['foo',]", cursor_pos=8)
1004 1004 self.assertIn("'bar'", matches)
1005 1005 self.assertNotIn("bar", matches)
1006 1006 self.assertNotIn("'foo'", matches)
1007 1007 self.assertNotIn("foo", matches)
1008 1008
1009 1009 _, matches = complete(line_buffer="d['']", cursor_pos=3)
1010 1010 self.assertIn("foo", matches)
1011 1011 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1012 1012
1013 1013 _, matches = complete(line_buffer='d[""]', cursor_pos=3)
1014 1014 self.assertIn("foo", matches)
1015 1015 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1016 1016
1017 1017 _, matches = complete(line_buffer='d["foo","]', cursor_pos=9)
1018 1018 self.assertIn("bar", matches)
1019 1019 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1020 1020
1021 1021 _, matches = complete(line_buffer='d["foo",]', cursor_pos=8)
1022 1022 self.assertIn("'bar'", matches)
1023 1023 self.assertNotIn("bar", matches)
1024 1024
1025 1025 # Can complete with longer tuple keys
1026 1026 ip.user_ns["d"] = {('foo', 'bar', 'foobar'): None}
1027 1027
1028 1028 # - can complete second key
1029 1029 _, matches = complete(line_buffer="d['foo', 'b")
1030 1030 self.assertIn("bar", matches)
1031 1031 self.assertNotIn("foo", matches)
1032 1032 self.assertNotIn("foobar", matches)
1033 1033
1034 1034 # - can complete third key
1035 1035 _, matches = complete(line_buffer="d['foo', 'bar', 'fo")
1036 1036 self.assertIn("foobar", matches)
1037 1037 self.assertNotIn("foo", matches)
1038 1038 self.assertNotIn("bar", matches)
1039 1039
1040 1040 def test_dict_key_completion_contexts(self):
1041 1041 """Test expression contexts in which dict key completion occurs"""
1042 1042 ip = get_ipython()
1043 1043 complete = ip.Completer.complete
1044 1044 d = {"abc": None}
1045 1045 ip.user_ns["d"] = d
1046 1046
1047 1047 class C:
1048 1048 data = d
1049 1049
1050 1050 ip.user_ns["C"] = C
1051 1051 ip.user_ns["get"] = lambda: d
1052 1052
1053 1053 def assert_no_completion(**kwargs):
1054 1054 _, matches = complete(**kwargs)
1055 1055 self.assertNotIn("abc", matches)
1056 1056 self.assertNotIn("abc'", matches)
1057 1057 self.assertNotIn("abc']", matches)
1058 1058 self.assertNotIn("'abc'", matches)
1059 1059 self.assertNotIn("'abc']", matches)
1060 1060
1061 1061 def assert_completion(**kwargs):
1062 1062 _, matches = complete(**kwargs)
1063 1063 self.assertIn("'abc'", matches)
1064 1064 self.assertNotIn("'abc']", matches)
1065 1065
1066 1066 # no completion after string closed, even if reopened
1067 1067 assert_no_completion(line_buffer="d['a'")
1068 1068 assert_no_completion(line_buffer='d["a"')
1069 1069 assert_no_completion(line_buffer="d['a' + ")
1070 1070 assert_no_completion(line_buffer="d['a' + '")
1071 1071
1072 1072 # completion in non-trivial expressions
1073 1073 assert_completion(line_buffer="+ d[")
1074 1074 assert_completion(line_buffer="(d[")
1075 1075 assert_completion(line_buffer="C.data[")
1076 1076
1077 1077 # greedy flag
1078 1078 def assert_completion(**kwargs):
1079 1079 _, matches = complete(**kwargs)
1080 1080 self.assertIn("get()['abc']", matches)
1081 1081
1082 1082 assert_no_completion(line_buffer="get()[")
1083 1083 with greedy_completion():
1084 1084 assert_completion(line_buffer="get()[")
1085 1085 assert_completion(line_buffer="get()['")
1086 1086 assert_completion(line_buffer="get()['a")
1087 1087 assert_completion(line_buffer="get()['ab")
1088 1088 assert_completion(line_buffer="get()['abc")
1089 1089
1090 1090 def test_dict_key_completion_bytes(self):
1091 1091 """Test handling of bytes in dict key completion"""
1092 1092 ip = get_ipython()
1093 1093 complete = ip.Completer.complete
1094 1094
1095 1095 ip.user_ns["d"] = {"abc": None, b"abd": None}
1096 1096
1097 1097 _, matches = complete(line_buffer="d[")
1098 1098 self.assertIn("'abc'", matches)
1099 1099 self.assertIn("b'abd'", matches)
1100 1100
1101 1101 if False: # not currently implemented
1102 1102 _, matches = complete(line_buffer="d[b")
1103 1103 self.assertIn("b'abd'", matches)
1104 1104 self.assertNotIn("b'abc'", matches)
1105 1105
1106 1106 _, matches = complete(line_buffer="d[b'")
1107 1107 self.assertIn("abd", matches)
1108 1108 self.assertNotIn("abc", matches)
1109 1109
1110 1110 _, matches = complete(line_buffer="d[B'")
1111 1111 self.assertIn("abd", matches)
1112 1112 self.assertNotIn("abc", matches)
1113 1113
1114 1114 _, matches = complete(line_buffer="d['")
1115 1115 self.assertIn("abc", matches)
1116 1116 self.assertNotIn("abd", matches)
1117 1117
1118 1118 def test_dict_key_completion_unicode_py3(self):
1119 1119 """Test handling of unicode in dict key completion"""
1120 1120 ip = get_ipython()
1121 1121 complete = ip.Completer.complete
1122 1122
1123 1123 ip.user_ns["d"] = {"a\u05d0": None}
1124 1124
1125 1125 # query using escape
1126 1126 if sys.platform != "win32":
1127 1127 # Known failure on Windows
1128 1128 _, matches = complete(line_buffer="d['a\\u05d0")
1129 1129 self.assertIn("u05d0", matches) # tokenized after \\
1130 1130
1131 1131 # query using character
1132 1132 _, matches = complete(line_buffer="d['a\u05d0")
1133 1133 self.assertIn("a\u05d0", matches)
1134 1134
1135 1135 with greedy_completion():
1136 1136 # query using escape
1137 1137 _, matches = complete(line_buffer="d['a\\u05d0")
1138 1138 self.assertIn("d['a\\u05d0']", matches) # tokenized after \\
1139 1139
1140 1140 # query using character
1141 1141 _, matches = complete(line_buffer="d['a\u05d0")
1142 1142 self.assertIn("d['a\u05d0']", matches)
1143 1143
1144 1144 @dec.skip_without("numpy")
1145 1145 def test_struct_array_key_completion(self):
1146 1146 """Test dict key completion applies to numpy struct arrays"""
1147 1147 import numpy
1148 1148
1149 1149 ip = get_ipython()
1150 1150 complete = ip.Completer.complete
1151 1151 ip.user_ns["d"] = numpy.array([], dtype=[("hello", "f"), ("world", "f")])
1152 1152 _, matches = complete(line_buffer="d['")
1153 1153 self.assertIn("hello", matches)
1154 1154 self.assertIn("world", matches)
1155 1155 # complete on the numpy struct itself
1156 1156 dt = numpy.dtype(
1157 1157 [("my_head", [("my_dt", ">u4"), ("my_df", ">u4")]), ("my_data", ">f4", 5)]
1158 1158 )
1159 1159 x = numpy.zeros(2, dtype=dt)
1160 1160 ip.user_ns["d"] = x[1]
1161 1161 _, matches = complete(line_buffer="d['")
1162 1162 self.assertIn("my_head", matches)
1163 1163 self.assertIn("my_data", matches)
1164 1164 # complete on a nested level
1165 1165 with greedy_completion():
1166 1166 ip.user_ns["d"] = numpy.zeros(2, dtype=dt)
1167 1167 _, matches = complete(line_buffer="d[1]['my_head']['")
1168 1168 self.assertTrue(any(["my_dt" in m for m in matches]))
1169 1169 self.assertTrue(any(["my_df" in m for m in matches]))
1170 1170
1171 1171 @dec.skip_without("pandas")
1172 1172 def test_dataframe_key_completion(self):
1173 1173 """Test dict key completion applies to pandas DataFrames"""
1174 1174 import pandas
1175 1175
1176 1176 ip = get_ipython()
1177 1177 complete = ip.Completer.complete
1178 1178 ip.user_ns["d"] = pandas.DataFrame({"hello": [1], "world": [2]})
1179 1179 _, matches = complete(line_buffer="d['")
1180 1180 self.assertIn("hello", matches)
1181 1181 self.assertIn("world", matches)
1182 1182
1183 1183 def test_dict_key_completion_invalids(self):
1184 1184 """Smoke test cases dict key completion can't handle"""
1185 1185 ip = get_ipython()
1186 1186 complete = ip.Completer.complete
1187 1187
1188 1188 ip.user_ns["no_getitem"] = None
1189 1189 ip.user_ns["no_keys"] = []
1190 1190 ip.user_ns["cant_call_keys"] = dict
1191 1191 ip.user_ns["empty"] = {}
1192 1192 ip.user_ns["d"] = {"abc": 5}
1193 1193
1194 1194 _, matches = complete(line_buffer="no_getitem['")
1195 1195 _, matches = complete(line_buffer="no_keys['")
1196 1196 _, matches = complete(line_buffer="cant_call_keys['")
1197 1197 _, matches = complete(line_buffer="empty['")
1198 1198 _, matches = complete(line_buffer="name_error['")
1199 1199 _, matches = complete(line_buffer="d['\\") # incomplete escape
1200 1200
1201 1201 def test_object_key_completion(self):
1202 1202 ip = get_ipython()
1203 1203 ip.user_ns["key_completable"] = KeyCompletable(["qwerty", "qwick"])
1204 1204
1205 1205 _, matches = ip.Completer.complete(line_buffer="key_completable['qw")
1206 1206 self.assertIn("qwerty", matches)
1207 1207 self.assertIn("qwick", matches)
1208 1208
1209 1209 def test_class_key_completion(self):
1210 1210 ip = get_ipython()
1211 1211 NamedInstanceClass("qwerty")
1212 1212 NamedInstanceClass("qwick")
1213 1213 ip.user_ns["named_instance_class"] = NamedInstanceClass
1214 1214
1215 1215 _, matches = ip.Completer.complete(line_buffer="named_instance_class['qw")
1216 1216 self.assertIn("qwerty", matches)
1217 1217 self.assertIn("qwick", matches)
1218 1218
1219 1219 def test_tryimport(self):
1220 1220 """
1221 1221 Test that try-import don't crash on trailing dot, and import modules before
1222 1222 """
1223 1223 from IPython.core.completerlib import try_import
1224 1224
1225 1225 assert try_import("IPython.")
1226 1226
1227 1227 def test_aimport_module_completer(self):
1228 1228 ip = get_ipython()
1229 1229 _, matches = ip.complete("i", "%aimport i")
1230 1230 self.assertIn("io", matches)
1231 1231 self.assertNotIn("int", matches)
1232 1232
1233 1233 def test_nested_import_module_completer(self):
1234 1234 ip = get_ipython()
1235 1235 _, matches = ip.complete(None, "import IPython.co", 17)
1236 1236 self.assertIn("IPython.core", matches)
1237 1237 self.assertNotIn("import IPython.core", matches)
1238 1238 self.assertNotIn("IPython.display", matches)
1239 1239
1240 1240 def test_import_module_completer(self):
1241 1241 ip = get_ipython()
1242 1242 _, matches = ip.complete("i", "import i")
1243 1243 self.assertIn("io", matches)
1244 1244 self.assertNotIn("int", matches)
1245 1245
1246 1246 def test_from_module_completer(self):
1247 1247 ip = get_ipython()
1248 1248 _, matches = ip.complete("B", "from io import B", 16)
1249 1249 self.assertIn("BytesIO", matches)
1250 1250 self.assertNotIn("BaseException", matches)
1251 1251
1252 1252 def test_snake_case_completion(self):
1253 1253 ip = get_ipython()
1254 1254 ip.Completer.use_jedi = False
1255 1255 ip.user_ns["some_three"] = 3
1256 1256 ip.user_ns["some_four"] = 4
1257 1257 _, matches = ip.complete("s_", "print(s_f")
1258 1258 self.assertIn("some_three", matches)
1259 1259 self.assertIn("some_four", matches)
1260 1260
1261 1261 def test_mix_terms(self):
1262 1262 ip = get_ipython()
1263 1263 from textwrap import dedent
1264 1264
1265 1265 ip.Completer.use_jedi = False
1266 1266 ip.ex(
1267 1267 dedent(
1268 1268 """
1269 1269 class Test:
1270 1270 def meth(self, meth_arg1):
1271 1271 print("meth")
1272 1272
1273 1273 def meth_1(self, meth1_arg1, meth1_arg2):
1274 1274 print("meth1")
1275 1275
1276 1276 def meth_2(self, meth2_arg1, meth2_arg2):
1277 1277 print("meth2")
1278 1278 test = Test()
1279 1279 """
1280 1280 )
1281 1281 )
1282 1282 _, matches = ip.complete(None, "test.meth(")
1283 1283 self.assertIn("meth_arg1=", matches)
1284 1284 self.assertNotIn("meth2_arg1=", matches)
@@ -1,1366 +1,1363 b''
1 1 # -*- coding: utf-8 -*-
2 """Tests for various magic functions.
3
4 Needs to be run by nose (to make ipython session available).
5 """
2 """Tests for various magic functions."""
6 3
7 4 import asyncio
8 5 import io
9 6 import os
10 7 import re
11 8 import shlex
12 9 import sys
13 10 import warnings
14 11 from importlib import invalidate_caches
15 12 from io import StringIO
16 13 from pathlib import Path
17 14 from textwrap import dedent
18 15 from unittest import TestCase, mock
19 16
20 17 import pytest
21 18
22 19 from IPython import get_ipython
23 20 from IPython.core import magic
24 21 from IPython.core.error import UsageError
25 22 from IPython.core.magic import (
26 23 Magics,
27 24 cell_magic,
28 25 line_magic,
29 26 magics_class,
30 27 register_cell_magic,
31 28 register_line_magic,
32 29 )
33 30 from IPython.core.magics import code, execution, logging, osm, script
34 31 from IPython.testing import decorators as dec
35 32 from IPython.testing import tools as tt
36 33 from IPython.utils.io import capture_output
37 34 from IPython.utils.process import find_cmd
38 35 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
39 36
40 37 from .test_debugger import PdbTestInput
41 38
42 39
43 40 @magic.magics_class
44 41 class DummyMagics(magic.Magics): pass
45 42
46 43 def test_extract_code_ranges():
47 44 instr = "1 3 5-6 7-9 10:15 17: :10 10- -13 :"
48 45 expected = [
49 46 (0, 1),
50 47 (2, 3),
51 48 (4, 6),
52 49 (6, 9),
53 50 (9, 14),
54 51 (16, None),
55 52 (None, 9),
56 53 (9, None),
57 54 (None, 13),
58 55 (None, None),
59 56 ]
60 57 actual = list(code.extract_code_ranges(instr))
61 58 assert actual == expected
62 59
63 60 def test_extract_symbols():
64 61 source = """import foo\na = 10\ndef b():\n return 42\n\n\nclass A: pass\n\n\n"""
65 62 symbols_args = ["a", "b", "A", "A,b", "A,a", "z"]
66 63 expected = [([], ['a']),
67 64 (["def b():\n return 42\n"], []),
68 65 (["class A: pass\n"], []),
69 66 (["class A: pass\n", "def b():\n return 42\n"], []),
70 67 (["class A: pass\n"], ['a']),
71 68 ([], ['z'])]
72 69 for symbols, exp in zip(symbols_args, expected):
73 70 assert code.extract_symbols(source, symbols) == exp
74 71
75 72
76 73 def test_extract_symbols_raises_exception_with_non_python_code():
77 74 source = ("=begin A Ruby program :)=end\n"
78 75 "def hello\n"
79 76 "puts 'Hello world'\n"
80 77 "end")
81 78 with pytest.raises(SyntaxError):
82 79 code.extract_symbols(source, "hello")
83 80
84 81
85 82 def test_magic_not_found():
86 83 # magic not found raises UsageError
87 84 with pytest.raises(UsageError):
88 85 _ip.magic('doesntexist')
89 86
90 87 # ensure result isn't success when a magic isn't found
91 88 result = _ip.run_cell('%doesntexist')
92 89 assert isinstance(result.error_in_exec, UsageError)
93 90
94 91
95 92 def test_cell_magic_not_found():
96 93 # magic not found raises UsageError
97 94 with pytest.raises(UsageError):
98 95 _ip.run_cell_magic('doesntexist', 'line', 'cell')
99 96
100 97 # ensure result isn't success when a magic isn't found
101 98 result = _ip.run_cell('%%doesntexist')
102 99 assert isinstance(result.error_in_exec, UsageError)
103 100
104 101
105 102 def test_magic_error_status():
106 103 def fail(shell):
107 104 1/0
108 105 _ip.register_magic_function(fail)
109 106 result = _ip.run_cell('%fail')
110 107 assert isinstance(result.error_in_exec, ZeroDivisionError)
111 108
112 109
113 110 def test_config():
114 111 """ test that config magic does not raise
115 112 can happen if Configurable init is moved too early into
116 113 Magics.__init__ as then a Config object will be registered as a
117 114 magic.
118 115 """
119 116 ## should not raise.
120 117 _ip.magic('config')
121 118
122 119 def test_config_available_configs():
123 120 """ test that config magic prints available configs in unique and
124 121 sorted order. """
125 122 with capture_output() as captured:
126 123 _ip.magic('config')
127 124
128 125 stdout = captured.stdout
129 126 config_classes = stdout.strip().split('\n')[1:]
130 127 assert config_classes == sorted(set(config_classes))
131 128
132 129 def test_config_print_class():
133 130 """ test that config with a classname prints the class's options. """
134 131 with capture_output() as captured:
135 132 _ip.magic('config TerminalInteractiveShell')
136 133
137 134 stdout = captured.stdout
138 135 if not re.match("TerminalInteractiveShell.* options", stdout.splitlines()[0]):
139 136 print(stdout)
140 137 raise AssertionError("1st line of stdout not like "
141 138 "'TerminalInteractiveShell.* options'")
142 139
143 140 def test_rehashx():
144 141 # clear up everything
145 142 _ip.alias_manager.clear_aliases()
146 143 del _ip.db['syscmdlist']
147 144
148 145 _ip.magic('rehashx')
149 146 # Practically ALL ipython development systems will have more than 10 aliases
150 147
151 148 assert len(_ip.alias_manager.aliases) > 10
152 149 for name, cmd in _ip.alias_manager.aliases:
153 150 # we must strip dots from alias names
154 151 assert "." not in name
155 152
156 153 # rehashx must fill up syscmdlist
157 154 scoms = _ip.db['syscmdlist']
158 155 assert len(scoms) > 10
159 156
160 157
161 158 def test_magic_parse_options():
162 159 """Test that we don't mangle paths when parsing magic options."""
163 160 ip = get_ipython()
164 161 path = 'c:\\x'
165 162 m = DummyMagics(ip)
166 163 opts = m.parse_options('-f %s' % path,'f:')[0]
167 164 # argv splitting is os-dependent
168 165 if os.name == 'posix':
169 166 expected = 'c:x'
170 167 else:
171 168 expected = path
172 169 assert opts["f"] == expected
173 170
174 171
175 172 def test_magic_parse_long_options():
176 173 """Magic.parse_options can handle --foo=bar long options"""
177 174 ip = get_ipython()
178 175 m = DummyMagics(ip)
179 176 opts, _ = m.parse_options("--foo --bar=bubble", "a", "foo", "bar=")
180 177 assert "foo" in opts
181 178 assert "bar" in opts
182 179 assert opts["bar"] == "bubble"
183 180
184 181
185 182 def doctest_hist_f():
186 183 """Test %hist -f with temporary filename.
187 184
188 185 In [9]: import tempfile
189 186
190 187 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
191 188
192 189 In [11]: %hist -nl -f $tfile 3
193 190
194 191 In [13]: import os; os.unlink(tfile)
195 192 """
196 193
197 194
198 195 def doctest_hist_op():
199 196 """Test %hist -op
200 197
201 198 In [1]: class b(float):
202 199 ...: pass
203 200 ...:
204 201
205 202 In [2]: class s(object):
206 203 ...: def __str__(self):
207 204 ...: return 's'
208 205 ...:
209 206
210 207 In [3]:
211 208
212 209 In [4]: class r(b):
213 210 ...: def __repr__(self):
214 211 ...: return 'r'
215 212 ...:
216 213
217 214 In [5]: class sr(s,r): pass
218 215 ...:
219 216
220 217 In [6]:
221 218
222 219 In [7]: bb=b()
223 220
224 221 In [8]: ss=s()
225 222
226 223 In [9]: rr=r()
227 224
228 225 In [10]: ssrr=sr()
229 226
230 227 In [11]: 4.5
231 228 Out[11]: 4.5
232 229
233 230 In [12]: str(ss)
234 231 Out[12]: 's'
235 232
236 233 In [13]:
237 234
238 235 In [14]: %hist -op
239 236 >>> class b:
240 237 ... pass
241 238 ...
242 239 >>> class s(b):
243 240 ... def __str__(self):
244 241 ... return 's'
245 242 ...
246 243 >>>
247 244 >>> class r(b):
248 245 ... def __repr__(self):
249 246 ... return 'r'
250 247 ...
251 248 >>> class sr(s,r): pass
252 249 >>>
253 250 >>> bb=b()
254 251 >>> ss=s()
255 252 >>> rr=r()
256 253 >>> ssrr=sr()
257 254 >>> 4.5
258 255 4.5
259 256 >>> str(ss)
260 257 's'
261 258 >>>
262 259 """
263 260
264 261 def test_hist_pof():
265 262 ip = get_ipython()
266 263 ip.run_cell("1+2", store_history=True)
267 264 #raise Exception(ip.history_manager.session_number)
268 265 #raise Exception(list(ip.history_manager._get_range_session()))
269 266 with TemporaryDirectory() as td:
270 267 tf = os.path.join(td, 'hist.py')
271 268 ip.run_line_magic('history', '-pof %s' % tf)
272 269 assert os.path.isfile(tf)
273 270
274 271
275 272 def test_macro():
276 273 ip = get_ipython()
277 274 ip.history_manager.reset() # Clear any existing history.
278 275 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
279 276 for i, cmd in enumerate(cmds, start=1):
280 277 ip.history_manager.store_inputs(i, cmd)
281 278 ip.magic("macro test 1-3")
282 279 assert ip.user_ns["test"].value == "\n".join(cmds) + "\n"
283 280
284 281 # List macros
285 282 assert "test" in ip.magic("macro")
286 283
287 284
288 285 def test_macro_run():
289 286 """Test that we can run a multi-line macro successfully."""
290 287 ip = get_ipython()
291 288 ip.history_manager.reset()
292 289 cmds = ["a=10", "a+=1", "print(a)", "%macro test 2-3"]
293 290 for cmd in cmds:
294 291 ip.run_cell(cmd, store_history=True)
295 292 assert ip.user_ns["test"].value == "a+=1\nprint(a)\n"
296 293 with tt.AssertPrints("12"):
297 294 ip.run_cell("test")
298 295 with tt.AssertPrints("13"):
299 296 ip.run_cell("test")
300 297
301 298
302 299 def test_magic_magic():
303 300 """Test %magic"""
304 301 ip = get_ipython()
305 302 with capture_output() as captured:
306 303 ip.magic("magic")
307 304
308 305 stdout = captured.stdout
309 306 assert "%magic" in stdout
310 307 assert "IPython" in stdout
311 308 assert "Available" in stdout
312 309
313 310
314 311 @dec.skipif_not_numpy
315 312 def test_numpy_reset_array_undec():
316 313 "Test '%reset array' functionality"
317 314 _ip.ex("import numpy as np")
318 315 _ip.ex("a = np.empty(2)")
319 316 assert "a" in _ip.user_ns
320 317 _ip.magic("reset -f array")
321 318 assert "a" not in _ip.user_ns
322 319
323 320
324 321 def test_reset_out():
325 322 "Test '%reset out' magic"
326 323 _ip.run_cell("parrot = 'dead'", store_history=True)
327 324 # test '%reset -f out', make an Out prompt
328 325 _ip.run_cell("parrot", store_history=True)
329 326 assert "dead" in [_ip.user_ns[x] for x in ("_", "__", "___")]
330 327 _ip.magic("reset -f out")
331 328 assert "dead" not in [_ip.user_ns[x] for x in ("_", "__", "___")]
332 329 assert len(_ip.user_ns["Out"]) == 0
333 330
334 331
335 332 def test_reset_in():
336 333 "Test '%reset in' magic"
337 334 # test '%reset -f in'
338 335 _ip.run_cell("parrot", store_history=True)
339 336 assert "parrot" in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
340 337 _ip.magic("%reset -f in")
341 338 assert "parrot" not in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
342 339 assert len(set(_ip.user_ns["In"])) == 1
343 340
344 341
345 342 def test_reset_dhist():
346 343 "Test '%reset dhist' magic"
347 344 _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing
348 345 _ip.magic("cd " + os.path.dirname(pytest.__file__))
349 346 _ip.magic("cd -")
350 347 assert len(_ip.user_ns["_dh"]) > 0
351 348 _ip.magic("reset -f dhist")
352 349 assert len(_ip.user_ns["_dh"]) == 0
353 350 _ip.run_cell("_dh = [d for d in tmp]") # restore
354 351
355 352
356 353 def test_reset_in_length():
357 354 "Test that '%reset in' preserves In[] length"
358 355 _ip.run_cell("print 'foo'")
359 356 _ip.run_cell("reset -f in")
360 357 assert len(_ip.user_ns["In"]) == _ip.displayhook.prompt_count + 1
361 358
362 359
363 360 class TestResetErrors(TestCase):
364 361
365 362 def test_reset_redefine(self):
366 363
367 364 @magics_class
368 365 class KernelMagics(Magics):
369 366 @line_magic
370 367 def less(self, shell): pass
371 368
372 369 _ip.register_magics(KernelMagics)
373 370
374 371 with self.assertLogs() as cm:
375 372 # hack, we want to just capture logs, but assertLogs fails if not
376 373 # logs get produce.
377 374 # so log one things we ignore.
378 375 import logging as log_mod
379 376 log = log_mod.getLogger()
380 377 log.info('Nothing')
381 378 # end hack.
382 379 _ip.run_cell("reset -f")
383 380
384 381 assert len(cm.output) == 1
385 382 for out in cm.output:
386 383 assert "Invalid alias" not in out
387 384
388 385 def test_tb_syntaxerror():
389 386 """test %tb after a SyntaxError"""
390 387 ip = get_ipython()
391 388 ip.run_cell("for")
392 389
393 390 # trap and validate stdout
394 391 save_stdout = sys.stdout
395 392 try:
396 393 sys.stdout = StringIO()
397 394 ip.run_cell("%tb")
398 395 out = sys.stdout.getvalue()
399 396 finally:
400 397 sys.stdout = save_stdout
401 398 # trim output, and only check the last line
402 399 last_line = out.rstrip().splitlines()[-1].strip()
403 400 assert last_line == "SyntaxError: invalid syntax"
404 401
405 402
406 403 def test_time():
407 404 ip = get_ipython()
408 405
409 406 with tt.AssertPrints("Wall time: "):
410 407 ip.run_cell("%time None")
411 408
412 409 ip.run_cell("def f(kmjy):\n"
413 410 " %time print (2*kmjy)")
414 411
415 412 with tt.AssertPrints("Wall time: "):
416 413 with tt.AssertPrints("hihi", suppress=False):
417 414 ip.run_cell("f('hi')")
418 415
419 416 def test_time_last_not_expression():
420 417 ip.run_cell("%%time\n"
421 418 "var_1 = 1\n"
422 419 "var_2 = 2\n")
423 420 assert ip.user_ns['var_1'] == 1
424 421 del ip.user_ns['var_1']
425 422 assert ip.user_ns['var_2'] == 2
426 423 del ip.user_ns['var_2']
427 424
428 425
429 426 @dec.skip_win32
430 427 def test_time2():
431 428 ip = get_ipython()
432 429
433 430 with tt.AssertPrints("CPU times: user "):
434 431 ip.run_cell("%time None")
435 432
436 433 def test_time3():
437 434 """Erroneous magic function calls, issue gh-3334"""
438 435 ip = get_ipython()
439 436 ip.user_ns.pop('run', None)
440 437
441 438 with tt.AssertNotPrints("not found", channel='stderr'):
442 439 ip.run_cell("%%time\n"
443 440 "run = 0\n"
444 441 "run += 1")
445 442
446 443 def test_multiline_time():
447 444 """Make sure last statement from time return a value."""
448 445 ip = get_ipython()
449 446 ip.user_ns.pop('run', None)
450 447
451 448 ip.run_cell(dedent("""\
452 449 %%time
453 450 a = "ho"
454 451 b = "hey"
455 452 a+b
456 453 """
457 454 )
458 455 )
459 456 assert ip.user_ns_hidden["_"] == "hohey"
460 457
461 458
462 459 def test_time_local_ns():
463 460 """
464 461 Test that local_ns is actually global_ns when running a cell magic
465 462 """
466 463 ip = get_ipython()
467 464 ip.run_cell("%%time\n" "myvar = 1")
468 465 assert ip.user_ns["myvar"] == 1
469 466 del ip.user_ns["myvar"]
470 467
471 468
472 469 def test_doctest_mode():
473 470 "Toggle doctest_mode twice, it should be a no-op and run without error"
474 471 _ip.magic('doctest_mode')
475 472 _ip.magic('doctest_mode')
476 473
477 474
478 475 def test_parse_options():
479 476 """Tests for basic options parsing in magics."""
480 477 # These are only the most minimal of tests, more should be added later. At
481 478 # the very least we check that basic text/unicode calls work OK.
482 479 m = DummyMagics(_ip)
483 480 assert m.parse_options("foo", "")[1] == "foo"
484 481 assert m.parse_options("foo", "")[1] == "foo"
485 482
486 483
487 484 def test_parse_options_preserve_non_option_string():
488 485 """Test to assert preservation of non-option part of magic-block, while parsing magic options."""
489 486 m = DummyMagics(_ip)
490 487 opts, stmt = m.parse_options(
491 488 " -n1 -r 13 _ = 314 + foo", "n:r:", preserve_non_opts=True
492 489 )
493 490 assert opts == {"n": "1", "r": "13"}
494 491 assert stmt == "_ = 314 + foo"
495 492
496 493
497 494 def test_run_magic_preserve_code_block():
498 495 """Test to assert preservation of non-option part of magic-block, while running magic."""
499 496 _ip.user_ns["spaces"] = []
500 497 _ip.magic("timeit -n1 -r1 spaces.append([s.count(' ') for s in ['document']])")
501 498 assert _ip.user_ns["spaces"] == [[0]]
502 499
503 500
504 501 def test_dirops():
505 502 """Test various directory handling operations."""
506 503 # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/')
507 504 curpath = os.getcwd
508 505 startdir = os.getcwd()
509 506 ipdir = os.path.realpath(_ip.ipython_dir)
510 507 try:
511 508 _ip.magic('cd "%s"' % ipdir)
512 509 assert curpath() == ipdir
513 510 _ip.magic('cd -')
514 511 assert curpath() == startdir
515 512 _ip.magic('pushd "%s"' % ipdir)
516 513 assert curpath() == ipdir
517 514 _ip.magic('popd')
518 515 assert curpath() == startdir
519 516 finally:
520 517 os.chdir(startdir)
521 518
522 519
523 520 def test_cd_force_quiet():
524 521 """Test OSMagics.cd_force_quiet option"""
525 522 _ip.config.OSMagics.cd_force_quiet = True
526 523 osmagics = osm.OSMagics(shell=_ip)
527 524
528 525 startdir = os.getcwd()
529 526 ipdir = os.path.realpath(_ip.ipython_dir)
530 527
531 528 try:
532 529 with tt.AssertNotPrints(ipdir):
533 530 osmagics.cd('"%s"' % ipdir)
534 531 with tt.AssertNotPrints(startdir):
535 532 osmagics.cd('-')
536 533 finally:
537 534 os.chdir(startdir)
538 535
539 536
540 537 def test_xmode():
541 538 # Calling xmode three times should be a no-op
542 539 xmode = _ip.InteractiveTB.mode
543 540 for i in range(4):
544 541 _ip.magic("xmode")
545 542 assert _ip.InteractiveTB.mode == xmode
546 543
547 544 def test_reset_hard():
548 545 monitor = []
549 546 class A(object):
550 547 def __del__(self):
551 548 monitor.append(1)
552 549 def __repr__(self):
553 550 return "<A instance>"
554 551
555 552 _ip.user_ns["a"] = A()
556 553 _ip.run_cell("a")
557 554
558 555 assert monitor == []
559 556 _ip.magic("reset -f")
560 557 assert monitor == [1]
561 558
562 559 class TestXdel(tt.TempFileMixin):
563 560 def test_xdel(self):
564 561 """Test that references from %run are cleared by xdel."""
565 562 src = ("class A(object):\n"
566 563 " monitor = []\n"
567 564 " def __del__(self):\n"
568 565 " self.monitor.append(1)\n"
569 566 "a = A()\n")
570 567 self.mktmp(src)
571 568 # %run creates some hidden references...
572 569 _ip.magic("run %s" % self.fname)
573 570 # ... as does the displayhook.
574 571 _ip.run_cell("a")
575 572
576 573 monitor = _ip.user_ns["A"].monitor
577 574 assert monitor == []
578 575
579 576 _ip.magic("xdel a")
580 577
581 578 # Check that a's __del__ method has been called.
582 579 assert monitor == [1]
583 580
584 581 def doctest_who():
585 582 """doctest for %who
586 583
587 584 In [1]: %reset -sf
588 585
589 586 In [2]: alpha = 123
590 587
591 588 In [3]: beta = 'beta'
592 589
593 590 In [4]: %who int
594 591 alpha
595 592
596 593 In [5]: %who str
597 594 beta
598 595
599 596 In [6]: %whos
600 597 Variable Type Data/Info
601 598 ----------------------------
602 599 alpha int 123
603 600 beta str beta
604 601
605 602 In [7]: %who_ls
606 603 Out[7]: ['alpha', 'beta']
607 604 """
608 605
609 606 def test_whos():
610 607 """Check that whos is protected against objects where repr() fails."""
611 608 class A(object):
612 609 def __repr__(self):
613 610 raise Exception()
614 611 _ip.user_ns['a'] = A()
615 612 _ip.magic("whos")
616 613
617 614 def doctest_precision():
618 615 """doctest for %precision
619 616
620 617 In [1]: f = get_ipython().display_formatter.formatters['text/plain']
621 618
622 619 In [2]: %precision 5
623 620 Out[2]: '%.5f'
624 621
625 622 In [3]: f.float_format
626 623 Out[3]: '%.5f'
627 624
628 625 In [4]: %precision %e
629 626 Out[4]: '%e'
630 627
631 628 In [5]: f(3.1415927)
632 629 Out[5]: '3.141593e+00'
633 630 """
634 631
635 632 def test_debug_magic():
636 633 """Test debugging a small code with %debug
637 634
638 635 In [1]: with PdbTestInput(['c']):
639 636 ...: %debug print("a b") #doctest: +ELLIPSIS
640 637 ...:
641 638 ...
642 639 ipdb> c
643 640 a b
644 641 In [2]:
645 642 """
646 643
647 644 def test_psearch():
648 645 with tt.AssertPrints("dict.fromkeys"):
649 646 _ip.run_cell("dict.fr*?")
650 647 with tt.AssertPrints("π.is_integer"):
651 648 _ip.run_cell("π = 3.14;\nπ.is_integ*?")
652 649
653 650 def test_timeit_shlex():
654 651 """test shlex issues with timeit (#1109)"""
655 652 _ip.ex("def f(*a,**kw): pass")
656 653 _ip.magic('timeit -n1 "this is a bug".count(" ")')
657 654 _ip.magic('timeit -r1 -n1 f(" ", 1)')
658 655 _ip.magic('timeit -r1 -n1 f(" ", 1, " ", 2, " ")')
659 656 _ip.magic('timeit -r1 -n1 ("a " + "b")')
660 657 _ip.magic('timeit -r1 -n1 f("a " + "b")')
661 658 _ip.magic('timeit -r1 -n1 f("a " + "b ")')
662 659
663 660
664 661 def test_timeit_special_syntax():
665 662 "Test %%timeit with IPython special syntax"
666 663 @register_line_magic
667 664 def lmagic(line):
668 665 ip = get_ipython()
669 666 ip.user_ns['lmagic_out'] = line
670 667
671 668 # line mode test
672 669 _ip.run_line_magic("timeit", "-n1 -r1 %lmagic my line")
673 670 assert _ip.user_ns["lmagic_out"] == "my line"
674 671 # cell mode test
675 672 _ip.run_cell_magic("timeit", "-n1 -r1", "%lmagic my line2")
676 673 assert _ip.user_ns["lmagic_out"] == "my line2"
677 674
678 675
679 676 def test_timeit_return():
680 677 """
681 678 test whether timeit -o return object
682 679 """
683 680
684 681 res = _ip.run_line_magic('timeit','-n10 -r10 -o 1')
685 682 assert(res is not None)
686 683
687 684 def test_timeit_quiet():
688 685 """
689 686 test quiet option of timeit magic
690 687 """
691 688 with tt.AssertNotPrints("loops"):
692 689 _ip.run_cell("%timeit -n1 -r1 -q 1")
693 690
694 691 def test_timeit_return_quiet():
695 692 with tt.AssertNotPrints("loops"):
696 693 res = _ip.run_line_magic('timeit', '-n1 -r1 -q -o 1')
697 694 assert (res is not None)
698 695
699 696 def test_timeit_invalid_return():
700 697 with pytest.raises(SyntaxError):
701 698 _ip.run_line_magic('timeit', 'return')
702 699
703 700 @dec.skipif(execution.profile is None)
704 701 def test_prun_special_syntax():
705 702 "Test %%prun with IPython special syntax"
706 703 @register_line_magic
707 704 def lmagic(line):
708 705 ip = get_ipython()
709 706 ip.user_ns['lmagic_out'] = line
710 707
711 708 # line mode test
712 709 _ip.run_line_magic("prun", "-q %lmagic my line")
713 710 assert _ip.user_ns["lmagic_out"] == "my line"
714 711 # cell mode test
715 712 _ip.run_cell_magic("prun", "-q", "%lmagic my line2")
716 713 assert _ip.user_ns["lmagic_out"] == "my line2"
717 714
718 715
719 716 @dec.skipif(execution.profile is None)
720 717 def test_prun_quotes():
721 718 "Test that prun does not clobber string escapes (GH #1302)"
722 719 _ip.magic(r"prun -q x = '\t'")
723 720 assert _ip.user_ns["x"] == "\t"
724 721
725 722
726 723 def test_extension():
727 724 # Debugging information for failures of this test
728 725 print('sys.path:')
729 726 for p in sys.path:
730 727 print(' ', p)
731 728 print('CWD', os.getcwd())
732 729
733 730 pytest.raises(ImportError, _ip.magic, "load_ext daft_extension")
734 731 daft_path = os.path.join(os.path.dirname(__file__), "daft_extension")
735 732 sys.path.insert(0, daft_path)
736 733 try:
737 734 _ip.user_ns.pop('arq', None)
738 735 invalidate_caches() # Clear import caches
739 736 _ip.magic("load_ext daft_extension")
740 737 assert _ip.user_ns["arq"] == 185
741 738 _ip.magic("unload_ext daft_extension")
742 739 assert 'arq' not in _ip.user_ns
743 740 finally:
744 741 sys.path.remove(daft_path)
745 742
746 743
747 744 def test_notebook_export_json():
748 745 _ip = get_ipython()
749 746 _ip.history_manager.reset() # Clear any existing history.
750 747 cmds = ["a=1", "def b():\n return a**2", "print('noël, été', b())"]
751 748 for i, cmd in enumerate(cmds, start=1):
752 749 _ip.history_manager.store_inputs(i, cmd)
753 750 with TemporaryDirectory() as td:
754 751 outfile = os.path.join(td, "nb.ipynb")
755 752 _ip.magic("notebook -e %s" % outfile)
756 753
757 754
758 755 class TestEnv(TestCase):
759 756
760 757 def test_env(self):
761 758 env = _ip.magic("env")
762 759 self.assertTrue(isinstance(env, dict))
763 760
764 761 def test_env_secret(self):
765 762 env = _ip.magic("env")
766 763 hidden = "<hidden>"
767 764 with mock.patch.dict(
768 765 os.environ,
769 766 {
770 767 "API_KEY": "abc123",
771 768 "SECRET_THING": "ssshhh",
772 769 "JUPYTER_TOKEN": "",
773 770 "VAR": "abc"
774 771 }
775 772 ):
776 773 env = _ip.magic("env")
777 774 assert env["API_KEY"] == hidden
778 775 assert env["SECRET_THING"] == hidden
779 776 assert env["JUPYTER_TOKEN"] == hidden
780 777 assert env["VAR"] == "abc"
781 778
782 779 def test_env_get_set_simple(self):
783 780 env = _ip.magic("env var val1")
784 781 self.assertEqual(env, None)
785 782 self.assertEqual(os.environ['var'], 'val1')
786 783 self.assertEqual(_ip.magic("env var"), 'val1')
787 784 env = _ip.magic("env var=val2")
788 785 self.assertEqual(env, None)
789 786 self.assertEqual(os.environ['var'], 'val2')
790 787
791 788 def test_env_get_set_complex(self):
792 789 env = _ip.magic("env var 'val1 '' 'val2")
793 790 self.assertEqual(env, None)
794 791 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
795 792 self.assertEqual(_ip.magic("env var"), "'val1 '' 'val2")
796 793 env = _ip.magic('env var=val2 val3="val4')
797 794 self.assertEqual(env, None)
798 795 self.assertEqual(os.environ['var'], 'val2 val3="val4')
799 796
800 797 def test_env_set_bad_input(self):
801 798 self.assertRaises(UsageError, lambda: _ip.magic("set_env var"))
802 799
803 800 def test_env_set_whitespace(self):
804 801 self.assertRaises(UsageError, lambda: _ip.magic("env var A=B"))
805 802
806 803
807 804 class CellMagicTestCase(TestCase):
808 805
809 806 def check_ident(self, magic):
810 807 # Manually called, we get the result
811 808 out = _ip.run_cell_magic(magic, "a", "b")
812 809 assert out == ("a", "b")
813 810 # Via run_cell, it goes into the user's namespace via displayhook
814 811 _ip.run_cell("%%" + magic + " c\nd\n")
815 812 assert _ip.user_ns["_"] == ("c", "d\n")
816 813
817 814 def test_cell_magic_func_deco(self):
818 815 "Cell magic using simple decorator"
819 816 @register_cell_magic
820 817 def cellm(line, cell):
821 818 return line, cell
822 819
823 820 self.check_ident('cellm')
824 821
825 822 def test_cell_magic_reg(self):
826 823 "Cell magic manually registered"
827 824 def cellm(line, cell):
828 825 return line, cell
829 826
830 827 _ip.register_magic_function(cellm, 'cell', 'cellm2')
831 828 self.check_ident('cellm2')
832 829
833 830 def test_cell_magic_class(self):
834 831 "Cell magics declared via a class"
835 832 @magics_class
836 833 class MyMagics(Magics):
837 834
838 835 @cell_magic
839 836 def cellm3(self, line, cell):
840 837 return line, cell
841 838
842 839 _ip.register_magics(MyMagics)
843 840 self.check_ident('cellm3')
844 841
845 842 def test_cell_magic_class2(self):
846 843 "Cell magics declared via a class, #2"
847 844 @magics_class
848 845 class MyMagics2(Magics):
849 846
850 847 @cell_magic('cellm4')
851 848 def cellm33(self, line, cell):
852 849 return line, cell
853 850
854 851 _ip.register_magics(MyMagics2)
855 852 self.check_ident('cellm4')
856 853 # Check that nothing is registered as 'cellm33'
857 854 c33 = _ip.find_cell_magic('cellm33')
858 855 assert c33 == None
859 856
860 857 def test_file():
861 858 """Basic %%writefile"""
862 859 ip = get_ipython()
863 860 with TemporaryDirectory() as td:
864 861 fname = os.path.join(td, 'file1')
865 862 ip.run_cell_magic("writefile", fname, u'\n'.join([
866 863 'line1',
867 864 'line2',
868 865 ]))
869 866 s = Path(fname).read_text()
870 867 assert "line1\n" in s
871 868 assert "line2" in s
872 869
873 870
874 871 @dec.skip_win32
875 872 def test_file_single_quote():
876 873 """Basic %%writefile with embedded single quotes"""
877 874 ip = get_ipython()
878 875 with TemporaryDirectory() as td:
879 876 fname = os.path.join(td, '\'file1\'')
880 877 ip.run_cell_magic("writefile", fname, u'\n'.join([
881 878 'line1',
882 879 'line2',
883 880 ]))
884 881 s = Path(fname).read_text()
885 882 assert "line1\n" in s
886 883 assert "line2" in s
887 884
888 885
889 886 @dec.skip_win32
890 887 def test_file_double_quote():
891 888 """Basic %%writefile with embedded double quotes"""
892 889 ip = get_ipython()
893 890 with TemporaryDirectory() as td:
894 891 fname = os.path.join(td, '"file1"')
895 892 ip.run_cell_magic("writefile", fname, u'\n'.join([
896 893 'line1',
897 894 'line2',
898 895 ]))
899 896 s = Path(fname).read_text()
900 897 assert "line1\n" in s
901 898 assert "line2" in s
902 899
903 900
904 901 def test_file_var_expand():
905 902 """%%writefile $filename"""
906 903 ip = get_ipython()
907 904 with TemporaryDirectory() as td:
908 905 fname = os.path.join(td, 'file1')
909 906 ip.user_ns['filename'] = fname
910 907 ip.run_cell_magic("writefile", '$filename', u'\n'.join([
911 908 'line1',
912 909 'line2',
913 910 ]))
914 911 s = Path(fname).read_text()
915 912 assert "line1\n" in s
916 913 assert "line2" in s
917 914
918 915
919 916 def test_file_unicode():
920 917 """%%writefile with unicode cell"""
921 918 ip = get_ipython()
922 919 with TemporaryDirectory() as td:
923 920 fname = os.path.join(td, 'file1')
924 921 ip.run_cell_magic("writefile", fname, u'\n'.join([
925 922 u'liné1',
926 923 u'liné2',
927 924 ]))
928 925 with io.open(fname, encoding='utf-8') as f:
929 926 s = f.read()
930 927 assert "liné1\n" in s
931 928 assert "liné2" in s
932 929
933 930
934 931 def test_file_amend():
935 932 """%%writefile -a amends files"""
936 933 ip = get_ipython()
937 934 with TemporaryDirectory() as td:
938 935 fname = os.path.join(td, 'file2')
939 936 ip.run_cell_magic("writefile", fname, u'\n'.join([
940 937 'line1',
941 938 'line2',
942 939 ]))
943 940 ip.run_cell_magic("writefile", "-a %s" % fname, u'\n'.join([
944 941 'line3',
945 942 'line4',
946 943 ]))
947 944 s = Path(fname).read_text()
948 945 assert "line1\n" in s
949 946 assert "line3\n" in s
950 947
951 948
952 949 def test_file_spaces():
953 950 """%%file with spaces in filename"""
954 951 ip = get_ipython()
955 952 with TemporaryWorkingDirectory() as td:
956 953 fname = "file name"
957 954 ip.run_cell_magic("file", '"%s"'%fname, u'\n'.join([
958 955 'line1',
959 956 'line2',
960 957 ]))
961 958 s = Path(fname).read_text()
962 959 assert "line1\n" in s
963 960 assert "line2" in s
964 961
965 962
966 963 def test_script_config():
967 964 ip = get_ipython()
968 965 ip.config.ScriptMagics.script_magics = ['whoda']
969 966 sm = script.ScriptMagics(shell=ip)
970 967 assert "whoda" in sm.magics["cell"]
971 968
972 969
973 970 @dec.skip_iptest_but_not_pytest
974 971 @dec.skip_win32
975 972 @pytest.mark.skipif(
976 973 sys.platform == "win32", reason="This test does not run under Windows"
977 974 )
978 975 def test_script_out():
979 976 assert asyncio.get_event_loop().is_running() is False
980 977
981 978 ip = get_ipython()
982 979 ip.run_cell_magic("script", "--out output sh", "echo 'hi'")
983 980 assert asyncio.get_event_loop().is_running() is False
984 981 assert ip.user_ns["output"] == "hi\n"
985 982
986 983
987 984 @dec.skip_iptest_but_not_pytest
988 985 @dec.skip_win32
989 986 @pytest.mark.skipif(
990 987 sys.platform == "win32", reason="This test does not run under Windows"
991 988 )
992 989 def test_script_err():
993 990 ip = get_ipython()
994 991 assert asyncio.get_event_loop().is_running() is False
995 992 ip.run_cell_magic("script", "--err error sh", "echo 'hello' >&2")
996 993 assert asyncio.get_event_loop().is_running() is False
997 994 assert ip.user_ns["error"] == "hello\n"
998 995
999 996
1000 997 @dec.skip_iptest_but_not_pytest
1001 998 @dec.skip_win32
1002 999 @pytest.mark.skipif(
1003 1000 sys.platform == "win32", reason="This test does not run under Windows"
1004 1001 )
1005 1002 def test_script_out_err():
1006 1003
1007 1004 ip = get_ipython()
1008 1005 ip.run_cell_magic(
1009 1006 "script", "--out output --err error sh", "echo 'hi'\necho 'hello' >&2"
1010 1007 )
1011 1008 assert ip.user_ns["output"] == "hi\n"
1012 1009 assert ip.user_ns["error"] == "hello\n"
1013 1010
1014 1011
1015 1012 @dec.skip_iptest_but_not_pytest
1016 1013 @dec.skip_win32
1017 1014 @pytest.mark.skipif(
1018 1015 sys.platform == "win32", reason="This test does not run under Windows"
1019 1016 )
1020 1017 async def test_script_bg_out():
1021 1018 ip = get_ipython()
1022 1019 ip.run_cell_magic("script", "--bg --out output sh", "echo 'hi'")
1023 1020 assert (await ip.user_ns["output"].read()) == b"hi\n"
1024 1021 ip.user_ns["output"].close()
1025 1022 asyncio.get_event_loop().stop()
1026 1023
1027 1024
1028 1025 @dec.skip_iptest_but_not_pytest
1029 1026 @dec.skip_win32
1030 1027 @pytest.mark.skipif(
1031 1028 sys.platform == "win32", reason="This test does not run under Windows"
1032 1029 )
1033 1030 async def test_script_bg_err():
1034 1031 ip = get_ipython()
1035 1032 ip.run_cell_magic("script", "--bg --err error sh", "echo 'hello' >&2")
1036 1033 assert (await ip.user_ns["error"].read()) == b"hello\n"
1037 1034 ip.user_ns["error"].close()
1038 1035
1039 1036
1040 1037 @dec.skip_iptest_but_not_pytest
1041 1038 @dec.skip_win32
1042 1039 @pytest.mark.skipif(
1043 1040 sys.platform == "win32", reason="This test does not run under Windows"
1044 1041 )
1045 1042 async def test_script_bg_out_err():
1046 1043 ip = get_ipython()
1047 1044 ip.run_cell_magic(
1048 1045 "script", "--bg --out output --err error sh", "echo 'hi'\necho 'hello' >&2"
1049 1046 )
1050 1047 assert (await ip.user_ns["output"].read()) == b"hi\n"
1051 1048 assert (await ip.user_ns["error"].read()) == b"hello\n"
1052 1049 ip.user_ns["output"].close()
1053 1050 ip.user_ns["error"].close()
1054 1051
1055 1052
1056 1053 def test_script_defaults():
1057 1054 ip = get_ipython()
1058 1055 for cmd in ['sh', 'bash', 'perl', 'ruby']:
1059 1056 try:
1060 1057 find_cmd(cmd)
1061 1058 except Exception:
1062 1059 pass
1063 1060 else:
1064 1061 assert cmd in ip.magics_manager.magics["cell"]
1065 1062
1066 1063
1067 1064 @magics_class
1068 1065 class FooFoo(Magics):
1069 1066 """class with both %foo and %%foo magics"""
1070 1067 @line_magic('foo')
1071 1068 def line_foo(self, line):
1072 1069 "I am line foo"
1073 1070 pass
1074 1071
1075 1072 @cell_magic("foo")
1076 1073 def cell_foo(self, line, cell):
1077 1074 "I am cell foo, not line foo"
1078 1075 pass
1079 1076
1080 1077 def test_line_cell_info():
1081 1078 """%%foo and %foo magics are distinguishable to inspect"""
1082 1079 ip = get_ipython()
1083 1080 ip.magics_manager.register(FooFoo)
1084 1081 oinfo = ip.object_inspect("foo")
1085 1082 assert oinfo["found"] is True
1086 1083 assert oinfo["ismagic"] is True
1087 1084
1088 1085 oinfo = ip.object_inspect("%%foo")
1089 1086 assert oinfo["found"] is True
1090 1087 assert oinfo["ismagic"] is True
1091 1088 assert oinfo["docstring"] == FooFoo.cell_foo.__doc__
1092 1089
1093 1090 oinfo = ip.object_inspect("%foo")
1094 1091 assert oinfo["found"] is True
1095 1092 assert oinfo["ismagic"] is True
1096 1093 assert oinfo["docstring"] == FooFoo.line_foo.__doc__
1097 1094
1098 1095
1099 1096 def test_multiple_magics():
1100 1097 ip = get_ipython()
1101 1098 foo1 = FooFoo(ip)
1102 1099 foo2 = FooFoo(ip)
1103 1100 mm = ip.magics_manager
1104 1101 mm.register(foo1)
1105 1102 assert mm.magics["line"]["foo"].__self__ is foo1
1106 1103 mm.register(foo2)
1107 1104 assert mm.magics["line"]["foo"].__self__ is foo2
1108 1105
1109 1106
1110 1107 def test_alias_magic():
1111 1108 """Test %alias_magic."""
1112 1109 ip = get_ipython()
1113 1110 mm = ip.magics_manager
1114 1111
1115 1112 # Basic operation: both cell and line magics are created, if possible.
1116 1113 ip.run_line_magic("alias_magic", "timeit_alias timeit")
1117 1114 assert "timeit_alias" in mm.magics["line"]
1118 1115 assert "timeit_alias" in mm.magics["cell"]
1119 1116
1120 1117 # --cell is specified, line magic not created.
1121 1118 ip.run_line_magic("alias_magic", "--cell timeit_cell_alias timeit")
1122 1119 assert "timeit_cell_alias" not in mm.magics["line"]
1123 1120 assert "timeit_cell_alias" in mm.magics["cell"]
1124 1121
1125 1122 # Test that line alias is created successfully.
1126 1123 ip.run_line_magic("alias_magic", "--line env_alias env")
1127 1124 assert ip.run_line_magic("env", "") == ip.run_line_magic("env_alias", "")
1128 1125
1129 1126 # Test that line alias with parameters passed in is created successfully.
1130 1127 ip.run_line_magic(
1131 1128 "alias_magic", "--line history_alias history --params " + shlex.quote("3")
1132 1129 )
1133 1130 assert "history_alias" in mm.magics["line"]
1134 1131
1135 1132
1136 1133 def test_save():
1137 1134 """Test %save."""
1138 1135 ip = get_ipython()
1139 1136 ip.history_manager.reset() # Clear any existing history.
1140 1137 cmds = ["a=1", "def b():\n return a**2", "print(a, b())"]
1141 1138 for i, cmd in enumerate(cmds, start=1):
1142 1139 ip.history_manager.store_inputs(i, cmd)
1143 1140 with TemporaryDirectory() as tmpdir:
1144 1141 file = os.path.join(tmpdir, "testsave.py")
1145 1142 ip.run_line_magic("save", "%s 1-10" % file)
1146 1143 content = Path(file).read_text()
1147 1144 assert content.count(cmds[0]) == 1
1148 1145 assert "coding: utf-8" in content
1149 1146 ip.run_line_magic("save", "-a %s 1-10" % file)
1150 1147 content = Path(file).read_text()
1151 1148 assert content.count(cmds[0]) == 2
1152 1149 assert "coding: utf-8" in content
1153 1150
1154 1151
1155 1152 def test_save_with_no_args():
1156 1153 ip = get_ipython()
1157 1154 ip.history_manager.reset() # Clear any existing history.
1158 1155 cmds = ["a=1", "def b():\n return a**2", "print(a, b())", "%save"]
1159 1156 for i, cmd in enumerate(cmds, start=1):
1160 1157 ip.history_manager.store_inputs(i, cmd)
1161 1158
1162 1159 with TemporaryDirectory() as tmpdir:
1163 1160 path = os.path.join(tmpdir, "testsave.py")
1164 1161 ip.run_line_magic("save", path)
1165 1162 content = Path(path).read_text()
1166 1163 expected_content = dedent(
1167 1164 """\
1168 1165 # coding: utf-8
1169 1166 a=1
1170 1167 def b():
1171 1168 return a**2
1172 1169 print(a, b())
1173 1170 """
1174 1171 )
1175 1172 assert content == expected_content
1176 1173
1177 1174
1178 1175 def test_store():
1179 1176 """Test %store."""
1180 1177 ip = get_ipython()
1181 1178 ip.run_line_magic('load_ext', 'storemagic')
1182 1179
1183 1180 # make sure the storage is empty
1184 1181 ip.run_line_magic("store", "-z")
1185 1182 ip.user_ns["var"] = 42
1186 1183 ip.run_line_magic("store", "var")
1187 1184 ip.user_ns["var"] = 39
1188 1185 ip.run_line_magic("store", "-r")
1189 1186 assert ip.user_ns["var"] == 42
1190 1187
1191 1188 ip.run_line_magic("store", "-d var")
1192 1189 ip.user_ns["var"] = 39
1193 1190 ip.run_line_magic("store", "-r")
1194 1191 assert ip.user_ns["var"] == 39
1195 1192
1196 1193
1197 1194 def _run_edit_test(arg_s, exp_filename=None,
1198 1195 exp_lineno=-1,
1199 1196 exp_contents=None,
1200 1197 exp_is_temp=None):
1201 1198 ip = get_ipython()
1202 1199 M = code.CodeMagics(ip)
1203 1200 last_call = ['','']
1204 1201 opts,args = M.parse_options(arg_s,'prxn:')
1205 1202 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
1206 1203
1207 1204 if exp_filename is not None:
1208 1205 assert exp_filename == filename
1209 1206 if exp_contents is not None:
1210 1207 with io.open(filename, 'r', encoding='utf-8') as f:
1211 1208 contents = f.read()
1212 1209 assert exp_contents == contents
1213 1210 if exp_lineno != -1:
1214 1211 assert exp_lineno == lineno
1215 1212 if exp_is_temp is not None:
1216 1213 assert exp_is_temp == is_temp
1217 1214
1218 1215
1219 1216 def test_edit_interactive():
1220 1217 """%edit on interactively defined objects"""
1221 1218 ip = get_ipython()
1222 1219 n = ip.execution_count
1223 1220 ip.run_cell("def foo(): return 1", store_history=True)
1224 1221
1225 1222 try:
1226 1223 _run_edit_test("foo")
1227 1224 except code.InteractivelyDefined as e:
1228 1225 assert e.index == n
1229 1226 else:
1230 1227 raise AssertionError("Should have raised InteractivelyDefined")
1231 1228
1232 1229
1233 1230 def test_edit_cell():
1234 1231 """%edit [cell id]"""
1235 1232 ip = get_ipython()
1236 1233
1237 1234 ip.run_cell("def foo(): return 1", store_history=True)
1238 1235
1239 1236 # test
1240 1237 _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)
1241 1238
1242 1239 def test_edit_fname():
1243 1240 """%edit file"""
1244 1241 # test
1245 1242 _run_edit_test("test file.py", exp_filename="test file.py")
1246 1243
1247 1244 def test_bookmark():
1248 1245 ip = get_ipython()
1249 1246 ip.run_line_magic('bookmark', 'bmname')
1250 1247 with tt.AssertPrints('bmname'):
1251 1248 ip.run_line_magic('bookmark', '-l')
1252 1249 ip.run_line_magic('bookmark', '-d bmname')
1253 1250
1254 1251 def test_ls_magic():
1255 1252 ip = get_ipython()
1256 1253 json_formatter = ip.display_formatter.formatters['application/json']
1257 1254 json_formatter.enabled = True
1258 1255 lsmagic = ip.magic('lsmagic')
1259 1256 with warnings.catch_warnings(record=True) as w:
1260 1257 j = json_formatter(lsmagic)
1261 1258 assert sorted(j) == ["cell", "line"]
1262 1259 assert w == [] # no warnings
1263 1260
1264 1261
1265 1262 def test_strip_initial_indent():
1266 1263 def sii(s):
1267 1264 lines = s.splitlines()
1268 1265 return '\n'.join(code.strip_initial_indent(lines))
1269 1266
1270 1267 assert sii(" a = 1\nb = 2") == "a = 1\nb = 2"
1271 1268 assert sii(" a\n b\nc") == "a\n b\nc"
1272 1269 assert sii("a\n b") == "a\n b"
1273 1270
1274 1271 def test_logging_magic_quiet_from_arg():
1275 1272 _ip.config.LoggingMagics.quiet = False
1276 1273 lm = logging.LoggingMagics(shell=_ip)
1277 1274 with TemporaryDirectory() as td:
1278 1275 try:
1279 1276 with tt.AssertNotPrints(re.compile("Activating.*")):
1280 1277 lm.logstart('-q {}'.format(
1281 1278 os.path.join(td, "quiet_from_arg.log")))
1282 1279 finally:
1283 1280 _ip.logger.logstop()
1284 1281
1285 1282 def test_logging_magic_quiet_from_config():
1286 1283 _ip.config.LoggingMagics.quiet = True
1287 1284 lm = logging.LoggingMagics(shell=_ip)
1288 1285 with TemporaryDirectory() as td:
1289 1286 try:
1290 1287 with tt.AssertNotPrints(re.compile("Activating.*")):
1291 1288 lm.logstart(os.path.join(td, "quiet_from_config.log"))
1292 1289 finally:
1293 1290 _ip.logger.logstop()
1294 1291
1295 1292
1296 1293 def test_logging_magic_not_quiet():
1297 1294 _ip.config.LoggingMagics.quiet = False
1298 1295 lm = logging.LoggingMagics(shell=_ip)
1299 1296 with TemporaryDirectory() as td:
1300 1297 try:
1301 1298 with tt.AssertPrints(re.compile("Activating.*")):
1302 1299 lm.logstart(os.path.join(td, "not_quiet.log"))
1303 1300 finally:
1304 1301 _ip.logger.logstop()
1305 1302
1306 1303
1307 1304 def test_time_no_var_expand():
1308 1305 _ip.user_ns['a'] = 5
1309 1306 _ip.user_ns['b'] = []
1310 1307 _ip.magic('time b.append("{a}")')
1311 1308 assert _ip.user_ns['b'] == ['{a}']
1312 1309
1313 1310
1314 1311 # this is slow, put at the end for local testing.
1315 1312 def test_timeit_arguments():
1316 1313 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
1317 1314 if sys.version_info < (3,7):
1318 1315 _ip.magic("timeit -n1 -r1 ('#')")
1319 1316 else:
1320 1317 # 3.7 optimize no-op statement like above out, and complain there is
1321 1318 # nothing in the for loop.
1322 1319 _ip.magic("timeit -n1 -r1 a=('#')")
1323 1320
1324 1321
1325 1322 TEST_MODULE = """
1326 1323 print('Loaded my_tmp')
1327 1324 if __name__ == "__main__":
1328 1325 print('I just ran a script')
1329 1326 """
1330 1327
1331 1328
1332 1329 def test_run_module_from_import_hook():
1333 1330 "Test that a module can be loaded via an import hook"
1334 1331 with TemporaryDirectory() as tmpdir:
1335 1332 fullpath = os.path.join(tmpdir, 'my_tmp.py')
1336 1333 Path(fullpath).write_text(TEST_MODULE)
1337 1334
1338 1335 class MyTempImporter(object):
1339 1336 def __init__(self):
1340 1337 pass
1341 1338
1342 1339 def find_module(self, fullname, path=None):
1343 1340 if 'my_tmp' in fullname:
1344 1341 return self
1345 1342 return None
1346 1343
1347 1344 def load_module(self, name):
1348 1345 import imp
1349 1346 return imp.load_source('my_tmp', fullpath)
1350 1347
1351 1348 def get_code(self, fullname):
1352 1349 return compile(Path(fullpath).read_text(), "foo", "exec")
1353 1350
1354 1351 def is_package(self, __):
1355 1352 return False
1356 1353
1357 1354 sys.meta_path.insert(0, MyTempImporter())
1358 1355
1359 1356 with capture_output() as captured:
1360 1357 _ip.magic("run -m my_tmp")
1361 1358 _ip.run_cell("import my_tmp")
1362 1359
1363 1360 output = "Loaded my_tmp\nI just ran a script\nLoaded my_tmp\n"
1364 1361 assert output == captured.stdout
1365 1362
1366 1363 sys.meta_path.pop(0)
@@ -1,193 +1,190 b''
1 """Tests for various magic functions specific to the terminal frontend.
2
3 Needs to be run by nose (to make ipython session available).
4 """
1 """Tests for various magic functions specific to the terminal frontend."""
5 2
6 3 #-----------------------------------------------------------------------------
7 4 # Imports
8 5 #-----------------------------------------------------------------------------
9 6
10 7 import sys
11 8 from io import StringIO
12 9 from unittest import TestCase
13 10
14 11 from IPython.testing import tools as tt
15 12
16 13 #-----------------------------------------------------------------------------
17 14 # Test functions begin
18 15 #-----------------------------------------------------------------------------
19 16
20 17 def check_cpaste(code, should_fail=False):
21 18 """Execute code via 'cpaste' and ensure it was executed, unless
22 19 should_fail is set.
23 20 """
24 21 ip.user_ns['code_ran'] = False
25 22
26 23 src = StringIO()
27 24 if not hasattr(src, 'encoding'):
28 25 # IPython expects stdin to have an encoding attribute
29 26 src.encoding = None
30 27 src.write(code)
31 28 src.write('\n--\n')
32 29 src.seek(0)
33 30
34 31 stdin_save = sys.stdin
35 32 sys.stdin = src
36 33
37 34 try:
38 35 context = tt.AssertPrints if should_fail else tt.AssertNotPrints
39 36 with context("Traceback (most recent call last)"):
40 37 ip.magic('cpaste')
41 38
42 39 if not should_fail:
43 40 assert ip.user_ns['code_ran'], "%r failed" % code
44 41 finally:
45 42 sys.stdin = stdin_save
46 43
47 44 def test_cpaste():
48 45 """Test cpaste magic"""
49 46
50 47 def runf():
51 48 """Marker function: sets a flag when executed.
52 49 """
53 50 ip.user_ns['code_ran'] = True
54 51 return 'runf' # return string so '+ runf()' doesn't result in success
55 52
56 53 tests = {'pass': ["runf()",
57 54 "In [1]: runf()",
58 55 "In [1]: if 1:\n ...: runf()",
59 56 "> > > runf()",
60 57 ">>> runf()",
61 58 " >>> runf()",
62 59 ],
63 60
64 61 'fail': ["1 + runf()",
65 62 "++ runf()",
66 63 ]}
67 64
68 65 ip.user_ns['runf'] = runf
69 66
70 67 for code in tests['pass']:
71 68 check_cpaste(code)
72 69
73 70 for code in tests['fail']:
74 71 check_cpaste(code, should_fail=True)
75 72
76 73
77 74 class PasteTestCase(TestCase):
78 75 """Multiple tests for clipboard pasting"""
79 76
80 77 def paste(self, txt, flags='-q'):
81 78 """Paste input text, by default in quiet mode"""
82 79 ip.hooks.clipboard_get = lambda : txt
83 80 ip.magic('paste '+flags)
84 81
85 82 def setUp(self):
86 83 # Inject fake clipboard hook but save original so we can restore it later
87 84 self.original_clip = ip.hooks.clipboard_get
88 85
89 86 def tearDown(self):
90 87 # Restore original hook
91 88 ip.hooks.clipboard_get = self.original_clip
92 89
93 90 def test_paste(self):
94 91 ip.user_ns.pop("x", None)
95 92 self.paste("x = 1")
96 93 self.assertEqual(ip.user_ns["x"], 1)
97 94 ip.user_ns.pop("x")
98 95
99 96 def test_paste_pyprompt(self):
100 97 ip.user_ns.pop("x", None)
101 98 self.paste(">>> x=2")
102 99 self.assertEqual(ip.user_ns["x"], 2)
103 100 ip.user_ns.pop("x")
104 101
105 102 def test_paste_py_multi(self):
106 103 self.paste("""
107 104 >>> x = [1,2,3]
108 105 >>> y = []
109 106 >>> for i in x:
110 107 ... y.append(i**2)
111 108 ...
112 109 """
113 110 )
114 111 self.assertEqual(ip.user_ns["x"], [1, 2, 3])
115 112 self.assertEqual(ip.user_ns["y"], [1, 4, 9])
116 113
117 114 def test_paste_py_multi_r(self):
118 115 "Now, test that self.paste -r works"
119 116 self.test_paste_py_multi()
120 117 self.assertEqual(ip.user_ns.pop("x"), [1, 2, 3])
121 118 self.assertEqual(ip.user_ns.pop("y"), [1, 4, 9])
122 119 self.assertFalse("x" in ip.user_ns)
123 120 ip.magic("paste -r")
124 121 self.assertEqual(ip.user_ns["x"], [1, 2, 3])
125 122 self.assertEqual(ip.user_ns["y"], [1, 4, 9])
126 123
127 124 def test_paste_email(self):
128 125 "Test pasting of email-quoted contents"
129 126 self.paste("""\
130 127 >> def foo(x):
131 128 >> return x + 1
132 129 >> xx = foo(1.1)"""
133 130 )
134 131 self.assertEqual(ip.user_ns["xx"], 2.1)
135 132
136 133 def test_paste_email2(self):
137 134 "Email again; some programs add a space also at each quoting level"
138 135 self.paste("""\
139 136 > > def foo(x):
140 137 > > return x + 1
141 138 > > yy = foo(2.1) """
142 139 )
143 140 self.assertEqual(ip.user_ns["yy"], 3.1)
144 141
145 142 def test_paste_email_py(self):
146 143 "Email quoting of interactive input"
147 144 self.paste("""\
148 145 >> >>> def f(x):
149 146 >> ... return x+1
150 147 >> ...
151 148 >> >>> zz = f(2.5) """
152 149 )
153 150 self.assertEqual(ip.user_ns["zz"], 3.5)
154 151
155 152 def test_paste_echo(self):
156 153 "Also test self.paste echoing, by temporarily faking the writer"
157 154 w = StringIO()
158 155 writer = ip.write
159 156 ip.write = w.write
160 157 code = """
161 158 a = 100
162 159 b = 200"""
163 160 try:
164 161 self.paste(code,'')
165 162 out = w.getvalue()
166 163 finally:
167 164 ip.write = writer
168 165 self.assertEqual(ip.user_ns["a"], 100)
169 166 self.assertEqual(ip.user_ns["b"], 200)
170 167 assert out == code + "\n## -- End pasted text --\n"
171 168
172 169 def test_paste_leading_commas(self):
173 170 "Test multiline strings with leading commas"
174 171 tm = ip.magics_manager.registry['TerminalMagics']
175 172 s = '''\
176 173 a = """
177 174 ,1,2,3
178 175 """'''
179 176 ip.user_ns.pop("foo", None)
180 177 tm.store_or_execute(s, "foo")
181 178 self.assertIn("foo", ip.user_ns)
182 179
183 180 def test_paste_trailing_question(self):
184 181 "Test pasting sources with trailing question marks"
185 182 tm = ip.magics_manager.registry['TerminalMagics']
186 183 s = '''\
187 184 def funcfoo():
188 185 if True: #am i true?
189 186 return 'fooresult'
190 187 '''
191 188 ip.user_ns.pop('funcfoo', None)
192 189 self.paste(s)
193 190 self.assertEqual(ip.user_ns["funcfoo"](), "fooresult")
@@ -1,594 +1,599 b''
1 1 # encoding: utf-8
2 2 """Tests for code execution (%run and related), which is particularly tricky.
3 3
4 4 Because of how %run manages namespaces, and the fact that we are trying here to
5 5 verify subtle object deletion and reference counting issues, the %run tests
6 6 will be kept in this separate file. This makes it easier to aggregate in one
7 7 place the tricks needed to handle it; most other magics are much easier to test
8 8 and we do so in a common test_magic file.
9 9
10 10 Note that any test using `run -i` should make sure to do a `reset` afterwards,
11 11 as otherwise it may influence later tests.
12 12 """
13 13
14 14 # Copyright (c) IPython Development Team.
15 15 # Distributed under the terms of the Modified BSD License.
16 16
17 17
18 18
19 19 import functools
20 20 import os
21 21 from os.path import join as pjoin
22 22 import random
23 23 import string
24 24 import sys
25 25 import textwrap
26 26 import unittest
27 27 from unittest.mock import patch
28 28
29 29 import pytest
30 30
31 31 from IPython.testing import decorators as dec
32 32 from IPython.testing import tools as tt
33 33 from IPython.utils.io import capture_output
34 34 from IPython.utils.tempdir import TemporaryDirectory
35 35 from IPython.core import debugger
36 36
37 37 def doctest_refbug():
38 38 """Very nasty problem with references held by multiple runs of a script.
39 39 See: https://github.com/ipython/ipython/issues/141
40 40
41 41 In [1]: _ip.clear_main_mod_cache()
42 42 # random
43 43
44 44 In [2]: %run refbug
45 45
46 46 In [3]: call_f()
47 47 lowercased: hello
48 48
49 49 In [4]: %run refbug
50 50
51 51 In [5]: call_f()
52 52 lowercased: hello
53 53 lowercased: hello
54 54 """
55 55
56 56
57 57 def doctest_run_builtins():
58 58 r"""Check that %run doesn't damage __builtins__.
59 59
60 60 In [1]: import tempfile
61 61
62 62 In [2]: bid1 = id(__builtins__)
63 63
64 64 In [3]: fname = tempfile.mkstemp('.py')[1]
65 65
66 66 In [3]: f = open(fname,'w')
67 67
68 68 In [4]: dummy= f.write('pass\n')
69 69
70 70 In [5]: f.flush()
71 71
72 72 In [6]: t1 = type(__builtins__)
73 73
74 74 In [7]: %run $fname
75 75
76 76 In [7]: f.close()
77 77
78 78 In [8]: bid2 = id(__builtins__)
79 79
80 80 In [9]: t2 = type(__builtins__)
81 81
82 82 In [10]: t1 == t2
83 83 Out[10]: True
84 84
85 85 In [10]: bid1 == bid2
86 86 Out[10]: True
87 87
88 88 In [12]: try:
89 89 ....: os.unlink(fname)
90 90 ....: except:
91 91 ....: pass
92 92 ....:
93 93 """
94 94
95 95
96 96 def doctest_run_option_parser():
97 97 r"""Test option parser in %run.
98 98
99 99 In [1]: %run print_argv.py
100 100 []
101 101
102 102 In [2]: %run print_argv.py print*.py
103 103 ['print_argv.py']
104 104
105 105 In [3]: %run -G print_argv.py print*.py
106 106 ['print*.py']
107 107
108 108 """
109 109
110 110
111 111 @dec.skip_win32
112 112 def doctest_run_option_parser_for_posix():
113 113 r"""Test option parser in %run (Linux/OSX specific).
114 114
115 115 You need double quote to escape glob in POSIX systems:
116 116
117 117 In [1]: %run print_argv.py print\\*.py
118 118 ['print*.py']
119 119
120 120 You can't use quote to escape glob in POSIX systems:
121 121
122 122 In [2]: %run print_argv.py 'print*.py'
123 123 ['print_argv.py']
124 124
125 125 """
126 126
127 127
128 doctest_run_option_parser_for_posix.__skip_doctest__ = sys.platform == "win32"
129
130
128 131 @dec.skip_if_not_win32
129 132 def doctest_run_option_parser_for_windows():
130 133 r"""Test option parser in %run (Windows specific).
131 134
132 135 In Windows, you can't escape ``*` `by backslash:
133 136
134 137 In [1]: %run print_argv.py print\\*.py
135 ['print\\*.py']
138 ['print\\\\*.py']
136 139
137 140 You can use quote to escape glob:
138 141
139 142 In [2]: %run print_argv.py 'print*.py'
140 ['print*.py']
143 ["'print*.py'"]
141 144
142 145 """
143 146
144 147
148 doctest_run_option_parser_for_windows.__skip_doctest__ = sys.platform != "win32"
149
150
145 151 def doctest_reset_del():
146 152 """Test that resetting doesn't cause errors in __del__ methods.
147 153
148 154 In [2]: class A(object):
149 155 ...: def __del__(self):
150 156 ...: print(str("Hi"))
151 157 ...:
152 158
153 159 In [3]: a = A()
154 160
155 161 In [4]: get_ipython().reset()
156 162 Hi
157 163
158 164 In [5]: 1+1
159 165 Out[5]: 2
160 166 """
161 167
162 168 # For some tests, it will be handy to organize them in a class with a common
163 169 # setup that makes a temp file
164 170
165 171 class TestMagicRunPass(tt.TempFileMixin):
166 172
167 173 def setUp(self):
168 174 content = "a = [1,2,3]\nb = 1"
169 175 self.mktmp(content)
170 176
171 177 def run_tmpfile(self):
172 178 _ip = get_ipython()
173 179 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
174 180 # See below and ticket https://bugs.launchpad.net/bugs/366353
175 181 _ip.magic('run %s' % self.fname)
176 182
177 183 def run_tmpfile_p(self):
178 184 _ip = get_ipython()
179 185 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
180 186 # See below and ticket https://bugs.launchpad.net/bugs/366353
181 187 _ip.magic('run -p %s' % self.fname)
182 188
183 189 def test_builtins_id(self):
184 190 """Check that %run doesn't damage __builtins__ """
185 191 _ip = get_ipython()
186 192 # Test that the id of __builtins__ is not modified by %run
187 193 bid1 = id(_ip.user_ns['__builtins__'])
188 194 self.run_tmpfile()
189 195 bid2 = id(_ip.user_ns['__builtins__'])
190 196 assert bid1 == bid2
191 197
192 198 def test_builtins_type(self):
193 199 """Check that the type of __builtins__ doesn't change with %run.
194 200
195 201 However, the above could pass if __builtins__ was already modified to
196 202 be a dict (it should be a module) by a previous use of %run. So we
197 203 also check explicitly that it really is a module:
198 204 """
199 205 _ip = get_ipython()
200 206 self.run_tmpfile()
201 207 assert type(_ip.user_ns["__builtins__"]) == type(sys)
202 208
203 209 def test_run_profile(self):
204 210 """Test that the option -p, which invokes the profiler, do not
205 211 crash by invoking execfile"""
206 212 self.run_tmpfile_p()
207 213
208 214 def test_run_debug_twice(self):
209 215 # https://github.com/ipython/ipython/issues/10028
210 216 _ip = get_ipython()
211 217 with tt.fake_input(['c']):
212 218 _ip.magic('run -d %s' % self.fname)
213 219 with tt.fake_input(['c']):
214 220 _ip.magic('run -d %s' % self.fname)
215 221
216 222 def test_run_debug_twice_with_breakpoint(self):
217 223 """Make a valid python temp file."""
218 224 _ip = get_ipython()
219 225 with tt.fake_input(['b 2', 'c', 'c']):
220 226 _ip.magic('run -d %s' % self.fname)
221 227
222 228 with tt.fake_input(['c']):
223 229 with tt.AssertNotPrints('KeyError'):
224 230 _ip.magic('run -d %s' % self.fname)
225 231
226 232
227 233 class TestMagicRunSimple(tt.TempFileMixin):
228 234
229 235 def test_simpledef(self):
230 236 """Test that simple class definitions work."""
231 237 src = ("class foo: pass\n"
232 238 "def f(): return foo()")
233 239 self.mktmp(src)
234 240 _ip.magic("run %s" % self.fname)
235 241 _ip.run_cell("t = isinstance(f(), foo)")
236 242 assert _ip.user_ns["t"] is True
237 243
238 244 def test_obj_del(self):
239 245 """Test that object's __del__ methods are called on exit."""
240 246 src = ("class A(object):\n"
241 247 " def __del__(self):\n"
242 248 " print('object A deleted')\n"
243 249 "a = A()\n")
244 250 self.mktmp(src)
245 251 err = None
246 252 tt.ipexec_validate(self.fname, 'object A deleted', err)
247 253
248 254 def test_aggressive_namespace_cleanup(self):
249 255 """Test that namespace cleanup is not too aggressive GH-238
250 256
251 257 Returning from another run magic deletes the namespace"""
252 258 # see ticket https://github.com/ipython/ipython/issues/238
253 259
254 260 with tt.TempFileMixin() as empty:
255 261 empty.mktmp("")
256 262 # On Windows, the filename will have \users in it, so we need to use the
257 263 # repr so that the \u becomes \\u.
258 264 src = (
259 265 "ip = get_ipython()\n"
260 266 "for i in range(5):\n"
261 267 " try:\n"
262 268 " ip.magic(%r)\n"
263 269 " except NameError as e:\n"
264 270 " print(i)\n"
265 271 " break\n" % ("run " + empty.fname)
266 272 )
267 273 self.mktmp(src)
268 274 _ip.magic("run %s" % self.fname)
269 275 _ip.run_cell("ip == get_ipython()")
270 276 assert _ip.user_ns["i"] == 4
271 277
272 278 def test_run_second(self):
273 279 """Test that running a second file doesn't clobber the first, gh-3547"""
274 280 self.mktmp("avar = 1\n" "def afunc():\n" " return avar\n")
275 281
276 282 with tt.TempFileMixin() as empty:
277 283 empty.mktmp("")
278 284
279 285 _ip.magic("run %s" % self.fname)
280 286 _ip.magic("run %s" % empty.fname)
281 287 assert _ip.user_ns["afunc"]() == 1
282 288
283 289 @dec.skip_win32
284 290 def test_tclass(self):
285 291 mydir = os.path.dirname(__file__)
286 292 tc = os.path.join(mydir, 'tclass')
287 293 src = ("%%run '%s' C-first\n"
288 294 "%%run '%s' C-second\n"
289 295 "%%run '%s' C-third\n") % (tc, tc, tc)
290 296 self.mktmp(src, '.ipy')
291 297 out = """\
292 298 ARGV 1-: ['C-first']
293 299 ARGV 1-: ['C-second']
294 300 tclass.py: deleting object: C-first
295 301 ARGV 1-: ['C-third']
296 302 tclass.py: deleting object: C-second
297 303 tclass.py: deleting object: C-third
298 304 """
299 305 err = None
300 306 tt.ipexec_validate(self.fname, out, err)
301 307
302 308 def test_run_i_after_reset(self):
303 309 """Check that %run -i still works after %reset (gh-693)"""
304 310 src = "yy = zz\n"
305 311 self.mktmp(src)
306 312 _ip.run_cell("zz = 23")
307 313 try:
308 314 _ip.magic("run -i %s" % self.fname)
309 315 assert _ip.user_ns["yy"] == 23
310 316 finally:
311 317 _ip.magic('reset -f')
312 318
313 319 _ip.run_cell("zz = 23")
314 320 try:
315 321 _ip.magic("run -i %s" % self.fname)
316 322 assert _ip.user_ns["yy"] == 23
317 323 finally:
318 324 _ip.magic('reset -f')
319 325
320 326 def test_unicode(self):
321 327 """Check that files in odd encodings are accepted."""
322 328 mydir = os.path.dirname(__file__)
323 329 na = os.path.join(mydir, 'nonascii.py')
324 330 _ip.magic('run "%s"' % na)
325 331 assert _ip.user_ns["u"] == "Ўт№Ф"
326 332
327 333 def test_run_py_file_attribute(self):
328 334 """Test handling of `__file__` attribute in `%run <file>.py`."""
329 335 src = "t = __file__\n"
330 336 self.mktmp(src)
331 337 _missing = object()
332 338 file1 = _ip.user_ns.get('__file__', _missing)
333 339 _ip.magic('run %s' % self.fname)
334 340 file2 = _ip.user_ns.get('__file__', _missing)
335 341
336 342 # Check that __file__ was equal to the filename in the script's
337 343 # namespace.
338 344 assert _ip.user_ns["t"] == self.fname
339 345
340 346 # Check that __file__ was not leaked back into user_ns.
341 347 assert file1 == file2
342 348
343 349 def test_run_ipy_file_attribute(self):
344 350 """Test handling of `__file__` attribute in `%run <file.ipy>`."""
345 351 src = "t = __file__\n"
346 352 self.mktmp(src, ext='.ipy')
347 353 _missing = object()
348 354 file1 = _ip.user_ns.get('__file__', _missing)
349 355 _ip.magic('run %s' % self.fname)
350 356 file2 = _ip.user_ns.get('__file__', _missing)
351 357
352 358 # Check that __file__ was equal to the filename in the script's
353 359 # namespace.
354 360 assert _ip.user_ns["t"] == self.fname
355 361
356 362 # Check that __file__ was not leaked back into user_ns.
357 363 assert file1 == file2
358 364
359 365 def test_run_formatting(self):
360 366 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
361 367 src = "pass"
362 368 self.mktmp(src)
363 369 _ip.magic('run -t -N 1 %s' % self.fname)
364 370 _ip.magic('run -t -N 10 %s' % self.fname)
365 371
366 372 def test_ignore_sys_exit(self):
367 373 """Test the -e option to ignore sys.exit()"""
368 374 src = "import sys; sys.exit(1)"
369 375 self.mktmp(src)
370 376 with tt.AssertPrints('SystemExit'):
371 377 _ip.magic('run %s' % self.fname)
372 378
373 379 with tt.AssertNotPrints('SystemExit'):
374 380 _ip.magic('run -e %s' % self.fname)
375 381
376 382 def test_run_nb(self):
377 383 """Test %run notebook.ipynb"""
378 384 from nbformat import v4, writes
379 385 nb = v4.new_notebook(
380 386 cells=[
381 387 v4.new_markdown_cell("The Ultimate Question of Everything"),
382 388 v4.new_code_cell("answer=42")
383 389 ]
384 390 )
385 391 src = writes(nb, version=4)
386 392 self.mktmp(src, ext='.ipynb')
387 393
388 394 _ip.magic("run %s" % self.fname)
389 395
390 396 assert _ip.user_ns["answer"] == 42
391 397
392 398 def test_run_nb_error(self):
393 399 """Test %run notebook.ipynb error"""
394 400 from nbformat import v4, writes
395 401 # %run when a file name isn't provided
396 402 pytest.raises(Exception, _ip.magic, "run")
397 403
398 404 # %run when a file doesn't exist
399 405 pytest.raises(Exception, _ip.magic, "run foobar.ipynb")
400 406
401 407 # %run on a notebook with an error
402 408 nb = v4.new_notebook(
403 409 cells=[
404 410 v4.new_code_cell("0/0")
405 411 ]
406 412 )
407 413 src = writes(nb, version=4)
408 414 self.mktmp(src, ext='.ipynb')
409 415 pytest.raises(Exception, _ip.magic, "run %s" % self.fname)
410 416
411 417 def test_file_options(self):
412 418 src = ('import sys\n'
413 419 'a = " ".join(sys.argv[1:])\n')
414 420 self.mktmp(src)
415 421 test_opts = "-x 3 --verbose"
416 422 _ip.run_line_magic("run", "{0} {1}".format(self.fname, test_opts))
417 423 assert _ip.user_ns["a"] == test_opts
418 424
419 425
420 426 class TestMagicRunWithPackage(unittest.TestCase):
421 427
422 428 def writefile(self, name, content):
423 429 path = os.path.join(self.tempdir.name, name)
424 430 d = os.path.dirname(path)
425 431 if not os.path.isdir(d):
426 432 os.makedirs(d)
427 433 with open(path, 'w') as f:
428 434 f.write(textwrap.dedent(content))
429 435
430 436 def setUp(self):
431 437 self.package = package = 'tmp{0}'.format(''.join([random.choice(string.ascii_letters) for i in range(10)]))
432 438 """Temporary (probably) valid python package name."""
433 439
434 440 self.value = int(random.random() * 10000)
435 441
436 442 self.tempdir = TemporaryDirectory()
437 443 self.__orig_cwd = os.getcwd()
438 444 sys.path.insert(0, self.tempdir.name)
439 445
440 446 self.writefile(os.path.join(package, '__init__.py'), '')
441 447 self.writefile(os.path.join(package, 'sub.py'), """
442 448 x = {0!r}
443 449 """.format(self.value))
444 450 self.writefile(os.path.join(package, 'relative.py'), """
445 451 from .sub import x
446 452 """)
447 453 self.writefile(os.path.join(package, 'absolute.py'), """
448 454 from {0}.sub import x
449 455 """.format(package))
450 456 self.writefile(os.path.join(package, 'args.py'), """
451 457 import sys
452 458 a = " ".join(sys.argv[1:])
453 459 """.format(package))
454 460
455 461 def tearDown(self):
456 462 os.chdir(self.__orig_cwd)
457 463 sys.path[:] = [p for p in sys.path if p != self.tempdir.name]
458 464 self.tempdir.cleanup()
459 465
460 466 def check_run_submodule(self, submodule, opts=''):
461 467 _ip.user_ns.pop('x', None)
462 468 _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts))
463 469 self.assertEqual(_ip.user_ns['x'], self.value,
464 470 'Variable `x` is not loaded from module `{0}`.'
465 471 .format(submodule))
466 472
467 473 def test_run_submodule_with_absolute_import(self):
468 474 self.check_run_submodule('absolute')
469 475
470 476 def test_run_submodule_with_relative_import(self):
471 477 """Run submodule that has a relative import statement (#2727)."""
472 478 self.check_run_submodule('relative')
473 479
474 480 def test_prun_submodule_with_absolute_import(self):
475 481 self.check_run_submodule('absolute', '-p')
476 482
477 483 def test_prun_submodule_with_relative_import(self):
478 484 self.check_run_submodule('relative', '-p')
479 485
480 486 def with_fake_debugger(func):
481 487 @functools.wraps(func)
482 488 def wrapper(*args, **kwds):
483 489 with patch.object(debugger.Pdb, 'run', staticmethod(eval)):
484 490 return func(*args, **kwds)
485 491 return wrapper
486 492
487 493 @with_fake_debugger
488 494 def test_debug_run_submodule_with_absolute_import(self):
489 495 self.check_run_submodule('absolute', '-d')
490 496
491 497 @with_fake_debugger
492 498 def test_debug_run_submodule_with_relative_import(self):
493 499 self.check_run_submodule('relative', '-d')
494 500
495 501 def test_module_options(self):
496 502 _ip.user_ns.pop("a", None)
497 503 test_opts = "-x abc -m test"
498 504 _ip.run_line_magic("run", "-m {0}.args {1}".format(self.package, test_opts))
499 505 assert _ip.user_ns["a"] == test_opts
500 506
501 507 def test_module_options_with_separator(self):
502 508 _ip.user_ns.pop("a", None)
503 509 test_opts = "-x abc -m test"
504 510 _ip.run_line_magic("run", "-m {0}.args -- {1}".format(self.package, test_opts))
505 511 assert _ip.user_ns["a"] == test_opts
506 512
507 513
508 514 def test_run__name__():
509 515 with TemporaryDirectory() as td:
510 516 path = pjoin(td, 'foo.py')
511 517 with open(path, 'w') as f:
512 518 f.write("q = __name__")
513 519
514 520 _ip.user_ns.pop("q", None)
515 521 _ip.magic("run {}".format(path))
516 522 assert _ip.user_ns.pop("q") == "__main__"
517 523
518 524 _ip.magic("run -n {}".format(path))
519 525 assert _ip.user_ns.pop("q") == "foo"
520 526
521 527 try:
522 528 _ip.magic("run -i -n {}".format(path))
523 529 assert _ip.user_ns.pop("q") == "foo"
524 530 finally:
525 531 _ip.magic('reset -f')
526 532
527 533
528 534 def test_run_tb():
529 535 """Test traceback offset in %run"""
530 536 with TemporaryDirectory() as td:
531 537 path = pjoin(td, 'foo.py')
532 538 with open(path, 'w') as f:
533 539 f.write('\n'.join([
534 540 "def foo():",
535 541 " return bar()",
536 542 "def bar():",
537 543 " raise RuntimeError('hello!')",
538 544 "foo()",
539 545 ]))
540 546 with capture_output() as io:
541 547 _ip.magic('run {}'.format(path))
542 548 out = io.stdout
543 549 assert "execfile" not in out
544 550 assert "RuntimeError" in out
545 551 assert out.count("---->") == 3
546 552 del ip.user_ns['bar']
547 553 del ip.user_ns['foo']
548 554
549 555
550 556 def test_multiprocessing_run():
551 557 """Set we can run mutiprocesgin without messing up up main namespace
552 558
553 559 Note that import `nose.tools as nt` mdify the value s
554 560 sys.module['__mp_main__'] so we need to temporarily set it to None to test
555 561 the issue.
556 562 """
557 563 with TemporaryDirectory() as td:
558 564 mpm = sys.modules.get('__mp_main__')
559 assert mpm is not None
560 565 sys.modules['__mp_main__'] = None
561 566 try:
562 567 path = pjoin(td, 'test.py')
563 568 with open(path, 'w') as f:
564 569 f.write("import multiprocessing\nprint('hoy')")
565 570 with capture_output() as io:
566 571 _ip.run_line_magic('run', path)
567 572 _ip.run_cell("i_m_undefined")
568 573 out = io.stdout
569 574 assert "hoy" in out
570 575 assert "AttributeError" not in out
571 576 assert "NameError" in out
572 577 assert out.count("---->") == 1
573 578 except:
574 579 raise
575 580 finally:
576 581 sys.modules['__mp_main__'] = mpm
577 582
578 @dec.knownfailureif(sys.platform == 'win32', "writes to io.stdout aren't captured on Windows")
583
579 584 def test_script_tb():
580 585 """Test traceback offset in `ipython script.py`"""
581 586 with TemporaryDirectory() as td:
582 587 path = pjoin(td, 'foo.py')
583 588 with open(path, 'w') as f:
584 589 f.write('\n'.join([
585 590 "def foo():",
586 591 " return bar()",
587 592 "def bar():",
588 593 " raise RuntimeError('hello!')",
589 594 "foo()",
590 595 ]))
591 596 out, err = tt.ipexec(path)
592 597 assert "execfile" not in out
593 598 assert "RuntimeError" in out
594 599 assert out.count("---->") == 3
@@ -1,49 +1,20 b''
1 1 """Testing support (tools to test IPython itself).
2 2 """
3 3
4 4 #-----------------------------------------------------------------------------
5 5 # Copyright (C) 2009-2011 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 import os
13 13
14 14 #-----------------------------------------------------------------------------
15 # Functions
16 #-----------------------------------------------------------------------------
17
18 # User-level entry point for testing
19 def test(**kwargs):
20 """Run the entire IPython test suite.
21
22 Any of the options for run_iptestall() may be passed as keyword arguments.
23
24 For example::
25
26 IPython.test(testgroups=['lib', 'config', 'utils'], fast=2)
27
28 will run those three sections of the test suite, using two processes.
29 """
30
31 # Do the import internally, so that this function doesn't increase total
32 # import time
33 from .iptestcontroller import run_iptestall, default_options
34 options = default_options()
35 for name, val in kwargs.items():
36 setattr(options, name, val)
37 run_iptestall(options)
38
39 #-----------------------------------------------------------------------------
40 15 # Constants
41 16 #-----------------------------------------------------------------------------
42 17
43 18 # We scale all timeouts via this factor, slow machines can increase it
44 19 IPYTHON_TESTING_TIMEOUT_SCALE = float(os.getenv(
45 20 'IPYTHON_TESTING_TIMEOUT_SCALE', 1))
46
47 # So nose doesn't try to run this as a test itself and we end up with an
48 # infinite test loop
49 test.__test__ = False
@@ -1,399 +1,342 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 # Grab the numpy-specific decorators which we keep in a file that we
48 # occasionally update from upstream: decorators.py is a copy of
49 # numpy.testing.decorators, we expose all of it here.
50 from IPython.external.decorators import knownfailureif
51
52 47 #-----------------------------------------------------------------------------
53 48 # Classes and functions
54 49 #-----------------------------------------------------------------------------
55 50
56 51 # Simple example of the basic idea
57 52 def as_unittest(func):
58 53 """Decorator to make a simple function into a normal test via unittest."""
59 54 class Tester(unittest.TestCase):
60 55 def test(self):
61 56 func()
62 57
63 58 Tester.__name__ = func.__name__
64 59
65 60 return Tester
66 61
67 62 # Utility functions
68 63
69 64 def apply_wrapper(wrapper, func):
70 65 """Apply a wrapper to a function for decoration.
71 66
72 67 This mixes Michele Simionato's decorator tool with nose's make_decorator,
73 68 to apply a wrapper in a decorator so that all nose attributes, as well as
74 69 function signature and other properties, survive the decoration cleanly.
75 70 This will ensure that wrapped functions can still be well introspected via
76 71 IPython, for example.
77 72 """
78 73 warnings.warn("The function `apply_wrapper` is deprecated since IPython 4.0",
79 74 DeprecationWarning, stacklevel=2)
80 75 import nose.tools
81 76
82 77 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
83 78
84 79
85 80 def make_label_dec(label, ds=None):
86 81 """Factory function to create a decorator that applies one or more labels.
87 82
88 83 Parameters
89 84 ----------
90 85 label : string or sequence
91 86 One or more labels that will be applied by the decorator to the functions
92 87 it decorates. Labels are attributes of the decorated function with their
93 88 value set to True.
94 89
95 90 ds : string
96 91 An optional docstring for the resulting decorator. If not given, a
97 92 default docstring is auto-generated.
98 93
99 94 Returns
100 95 -------
101 96 A decorator.
102 97
103 98 Examples
104 99 --------
105 100
106 101 A simple labeling decorator:
107 102
108 103 >>> slow = make_label_dec('slow')
109 104 >>> slow.__doc__
110 105 "Labels a test as 'slow'."
111 106
112 107 And one that uses multiple labels and a custom docstring:
113 108
114 109 >>> rare = make_label_dec(['slow','hard'],
115 110 ... "Mix labels 'slow' and 'hard' for rare tests.")
116 111 >>> rare.__doc__
117 112 "Mix labels 'slow' and 'hard' for rare tests."
118 113
119 114 Now, let's test using this one:
120 115 >>> @rare
121 116 ... def f(): pass
122 117 ...
123 118 >>>
124 119 >>> f.slow
125 120 True
126 121 >>> f.hard
127 122 True
128 123 """
129 124
130 125 warnings.warn("The function `make_label_dec` is deprecated since IPython 4.0",
131 126 DeprecationWarning, stacklevel=2)
132 127 if isinstance(label, str):
133 128 labels = [label]
134 129 else:
135 130 labels = label
136 131
137 132 # Validate that the given label(s) are OK for use in setattr() by doing a
138 133 # dry run on a dummy function.
139 134 tmp = lambda : None
140 135 for label in labels:
141 136 setattr(tmp,label,True)
142 137
143 138 # This is the actual decorator we'll return
144 139 def decor(f):
145 140 for label in labels:
146 141 setattr(f,label,True)
147 142 return f
148 143
149 144 # Apply the user's docstring, or autogenerate a basic one
150 145 if ds is None:
151 146 ds = "Labels a test as %r." % label
152 147 decor.__doc__ = ds
153 148
154 149 return decor
155 150
156 151
157 152 def skip_iptest_but_not_pytest(f):
158 153 """
159 154 Warning this will make the test invisible to iptest.
160 155 """
161 156 import os
162 157
163 158 if os.environ.get("IPTEST_WORKING_DIR", None) is not None:
164 159 f.__test__ = False
165 160 return f
166 161
167 162
168 # Inspired by numpy's skipif, but uses the full apply_wrapper utility to
169 # preserve function metadata better and allows the skip condition to be a
170 # callable.
171 163 def skipif(skip_condition, msg=None):
172 ''' Make function raise SkipTest exception if skip_condition is true
164 """Make function raise SkipTest exception if skip_condition is true
173 165
174 166 Parameters
175 167 ----------
176 168
177 169 skip_condition : bool or callable
178 170 Flag to determine whether to skip test. If the condition is a
179 171 callable, it is used at runtime to dynamically make the decision. This
180 172 is useful for tests that may require costly imports, to delay the cost
181 173 until the test suite is actually executed.
182 174 msg : string
183 175 Message to give on raising a SkipTest exception.
184 176
185 177 Returns
186 178 -------
187 179 decorator : function
188 180 Decorator, which, when applied to a function, causes SkipTest
189 181 to be raised when the skip_condition was True, and the function
190 182 to be called normally otherwise.
183 """
184 if msg is None:
185 msg = "Test skipped due to test condition."
191 186
192 Notes
193 -----
194 You will see from the code that we had to further decorate the
195 decorator with the nose.tools.make_decorator function in order to
196 transmit function name, and various other metadata.
197 '''
198
199 def skip_decorator(f):
200 # Local import to avoid a hard nose dependency and only incur the
201 # import time overhead at actual test-time.
202 import nose
203
204 # Allow for both boolean or callable skip conditions.
205 if callable(skip_condition):
206 skip_val = skip_condition
207 else:
208 skip_val = lambda : skip_condition
209
210 def get_msg(func,msg=None):
211 """Skip message with information about function being skipped."""
212 if msg is None: out = 'Test skipped due to test condition.'
213 else: out = msg
214 return "Skipping test: %s. %s" % (func.__name__,out)
215
216 # We need to define *two* skippers because Python doesn't allow both
217 # return with value and yield inside the same function.
218 def skipper_func(*args, **kwargs):
219 """Skipper for normal test functions."""
220 if skip_val():
221 raise nose.SkipTest(get_msg(f,msg))
222 else:
223 return f(*args, **kwargs)
224
225 def skipper_gen(*args, **kwargs):
226 """Skipper for test generators."""
227 if skip_val():
228 raise nose.SkipTest(get_msg(f,msg))
229 else:
230 for x in f(*args, **kwargs):
231 yield x
232
233 # Choose the right skipper to use when building the actual generator.
234 if nose.util.isgenerator(f):
235 skipper = skipper_gen
236 else:
237 skipper = skipper_func
187 import pytest
238 188
239 return nose.tools.make_decorator(f)(skipper)
189 assert isinstance(skip_condition, bool)
190 return pytest.mark.skipif(skip_condition, reason=msg)
240 191
241 return skip_decorator
242 192
243 193 # A version with the condition set to true, common case just to attach a message
244 194 # to a skip decorator
245 195 def skip(msg=None):
246 196 """Decorator factory - mark a test function for skipping from test suite.
247 197
248 198 Parameters
249 199 ----------
250 200 msg : string
251 201 Optional message to be added.
252 202
253 203 Returns
254 204 -------
255 205 decorator : function
256 206 Decorator, which, when applied to a function, causes SkipTest
257 207 to be raised, with the optional message added.
258 208 """
259 209 if msg and not isinstance(msg, str):
260 210 raise ValueError('invalid object passed to `@skip` decorator, did you '
261 211 'meant `@skip()` with brackets ?')
262 212 return skipif(True, msg)
263 213
264 214
265 215 def onlyif(condition, msg):
266 216 """The reverse from skipif, see skipif for details."""
267 217
268 if callable(condition):
269 skip_condition = lambda : not condition()
270 else:
271 skip_condition = lambda : not condition
272
273 return skipif(skip_condition, msg)
218 return skipif(not condition, msg)
274 219
275 220 #-----------------------------------------------------------------------------
276 221 # Utility functions for decorators
277 222 def module_not_available(module):
278 223 """Can module be imported? Returns true if module does NOT import.
279 224
280 225 This is used to make a decorator to skip tests that require module to be
281 226 available, but delay the 'import numpy' to test execution time.
282 227 """
283 228 try:
284 229 mod = import_module(module)
285 230 mod_not_avail = False
286 231 except ImportError:
287 232 mod_not_avail = True
288 233
289 234 return mod_not_avail
290 235
291 236
292 237 def decorated_dummy(dec, name):
293 238 """Return a dummy function decorated with dec, with the given name.
294 239
295 240 Examples
296 241 --------
297 242 import IPython.testing.decorators as dec
298 243 setup = dec.decorated_dummy(dec.skip_if_no_x11, __name__)
299 244 """
300 245 warnings.warn("The function `decorated_dummy` is deprecated since IPython 4.0",
301 246 DeprecationWarning, stacklevel=2)
302 247 dummy = lambda: None
303 248 dummy.__name__ = name
304 249 return dec(dummy)
305 250
306 251 #-----------------------------------------------------------------------------
307 252 # Decorators for public use
308 253
309 254 # Decorators to skip certain tests on specific platforms.
310 255 skip_win32 = skipif(sys.platform == 'win32',
311 256 "This test does not run under Windows")
312 257 skip_linux = skipif(sys.platform.startswith('linux'),
313 258 "This test does not run under Linux")
314 259 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
315 260
316 261
317 262 # Decorators to skip tests if not on specific platforms.
318 263 skip_if_not_win32 = skipif(sys.platform != 'win32',
319 264 "This test only runs under Windows")
320 265 skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
321 266 "This test only runs under Linux")
322 267 skip_if_not_osx = skipif(sys.platform != 'darwin',
323 268 "This test only runs under OSX")
324 269
325 270
326 271 _x11_skip_cond = (sys.platform not in ('darwin', 'win32') and
327 272 os.environ.get('DISPLAY', '') == '')
328 273 _x11_skip_msg = "Skipped under *nix when X11/XOrg not available"
329 274
330 275 skip_if_no_x11 = skipif(_x11_skip_cond, _x11_skip_msg)
331 276
332 277
333 278 # Decorators to skip certain tests on specific platform/python combinations
334 279 skip_win32_py38 = skipif(sys.version_info > (3,8) and os.name == 'nt')
335 280
336 281
337 282 # not a decorator itself, returns a dummy function to be used as setup
338 283 def skip_file_no_x11(name):
339 284 warnings.warn("The function `skip_file_no_x11` is deprecated since IPython 4.0",
340 285 DeprecationWarning, stacklevel=2)
341 286 return decorated_dummy(skip_if_no_x11, name) if _x11_skip_cond else None
342 287
343 288 # Other skip decorators
344 289
345 290 # generic skip without module
346 291 skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod)
347 292
348 293 skipif_not_numpy = skip_without('numpy')
349 294
350 295 skipif_not_matplotlib = skip_without('matplotlib')
351 296
352 297 skipif_not_sympy = skip_without('sympy')
353 298
354 skip_known_failure = knownfailureif(True,'This test is known to fail')
355
356 299 # A null 'decorator', useful to make more readable code that needs to pick
357 300 # between different decorators based on OS or other conditions
358 301 null_deco = lambda f: f
359 302
360 303 # Some tests only run where we can use unicode paths. Note that we can't just
361 304 # check os.path.supports_unicode_filenames, which is always False on Linux.
362 305 try:
363 306 f = tempfile.NamedTemporaryFile(prefix=u"tmp€")
364 307 except UnicodeEncodeError:
365 308 unicode_paths = False
366 309 else:
367 310 unicode_paths = True
368 311 f.close()
369 312
370 313 onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable "
371 314 "where we can use unicode in filenames."))
372 315
373 316
374 317 def onlyif_cmds_exist(*commands):
375 318 """
376 319 Decorator to skip test when at least one of `commands` is not found.
377 320 """
378 321 for cmd in commands:
379 322 reason = f"This test runs only if command '{cmd}' is installed"
380 323 if not shutil.which(cmd):
381 324 if os.environ.get("IPTEST_WORKING_DIR", None) is not None:
382 325 return skip(reason)
383 326 else:
384 327 import pytest
385 328
386 329 return pytest.mark.skip(reason=reason)
387 330 return null_deco
388 331
389 332 def onlyif_any_cmd_exists(*commands):
390 333 """
391 334 Decorator to skip test unless at least one of `commands` is found.
392 335 """
393 336 warnings.warn("The function `onlyif_any_cmd_exists` is deprecated since IPython 4.0",
394 337 DeprecationWarning, stacklevel=2)
395 338 for cmd in commands:
396 339 if shutil.which(cmd):
397 340 return null_deco
398 341 return skip("This test runs only if one of the commands {0} "
399 342 "is installed".format(commands))
@@ -1,762 +1,452 b''
1 1 """Nose Plugin that supports IPython doctests.
2 2
3 3 Limitations:
4 4
5 5 - When generating examples for use as doctests, make sure that you have
6 6 pretty-printing OFF. This can be done either by setting the
7 7 ``PlainTextFormatter.pprint`` option in your configuration file to False, or
8 8 by interactively disabling it with %Pprint. This is required so that IPython
9 9 output matches that of normal Python, which is used by doctest for internal
10 10 execution.
11 11
12 12 - Do not rely on specific prompt numbers for results (such as using
13 13 '_34==True', for example). For IPython tests run via an external process the
14 14 prompt numbers may be different, and IPython tests run as normal python code
15 15 won't even have these special _NN variables set at all.
16 16 """
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Module imports
20 20
21 21 # From the standard library
22 import builtins as builtin_mod
23 22 import doctest
24 23 import inspect
25 24 import logging
26 25 import os
27 26 import re
28 import sys
29 from importlib import import_module
30 from io import StringIO
31 27
32 28 from testpath import modified_env
33 29
34 from inspect import getmodule
35
36 from pathlib import Path, PurePath
37
38 # We are overriding the default doctest runner, so we need to import a few
39 # things from doctest directly
40 from doctest import (REPORTING_FLAGS, REPORT_ONLY_FIRST_FAILURE,
41 _unittest_reportflags, DocTestRunner,
42 _extract_future_flags, pdb, _OutputRedirectingPdb,
43 _exception_traceback,
44 linecache)
45
46 # Third-party modules
47
48 from nose.plugins import doctests, Plugin
49 from nose.util import anyp, tolist
50
51 30 #-----------------------------------------------------------------------------
52 31 # Module globals and other constants
53 32 #-----------------------------------------------------------------------------
54 33
55 34 log = logging.getLogger(__name__)
56 35
57 36
58 37 #-----------------------------------------------------------------------------
59 38 # Classes and functions
60 39 #-----------------------------------------------------------------------------
61 40
62 41 def is_extension_module(filename):
63 42 """Return whether the given filename is an extension module.
64 43
65 44 This simply checks that the extension is either .so or .pyd.
66 45 """
67 46 return os.path.splitext(filename)[1].lower() in ('.so','.pyd')
68 47
69 48
70 49 class DocTestSkip(object):
71 50 """Object wrapper for doctests to be skipped."""
72 51
73 52 ds_skip = """Doctest to skip.
74 53 >>> 1 #doctest: +SKIP
75 54 """
76 55
77 56 def __init__(self,obj):
78 57 self.obj = obj
79 58
80 59 def __getattribute__(self,key):
81 60 if key == '__doc__':
82 61 return DocTestSkip.ds_skip
83 62 else:
84 63 return getattr(object.__getattribute__(self,'obj'),key)
85 64
86 65 # Modified version of the one in the stdlib, that fixes a python bug (doctests
87 66 # not found in extension modules, http://bugs.python.org/issue3158)
88 67 class DocTestFinder(doctest.DocTestFinder):
89 68
90 69 def _from_module(self, module, object):
91 70 """
92 71 Return true if the given object is defined in the given
93 72 module.
94 73 """
95 74 if module is None:
96 75 return True
97 76 elif inspect.isfunction(object):
98 77 return module.__dict__ is object.__globals__
99 78 elif inspect.isbuiltin(object):
100 79 return module.__name__ == object.__module__
101 80 elif inspect.isclass(object):
102 81 return module.__name__ == object.__module__
103 82 elif inspect.ismethod(object):
104 83 # This one may be a bug in cython that fails to correctly set the
105 84 # __module__ attribute of methods, but since the same error is easy
106 85 # to make by extension code writers, having this safety in place
107 86 # isn't such a bad idea
108 87 return module.__name__ == object.__self__.__class__.__module__
109 88 elif inspect.getmodule(object) is not None:
110 89 return module is inspect.getmodule(object)
111 90 elif hasattr(object, '__module__'):
112 91 return module.__name__ == object.__module__
113 92 elif isinstance(object, property):
114 93 return True # [XX] no way not be sure.
115 94 elif inspect.ismethoddescriptor(object):
116 95 # Unbound PyQt signals reach this point in Python 3.4b3, and we want
117 96 # to avoid throwing an error. See also http://bugs.python.org/issue3158
118 97 return False
119 98 else:
120 99 raise ValueError("object must be a class or function, got %r" % object)
121 100
122 101 def _find(self, tests, obj, name, module, source_lines, globs, seen):
123 102 """
124 103 Find tests for the given object and any contained objects, and
125 104 add them to `tests`.
126 105 """
127 106 print('_find for:', obj, name, module) # dbg
128 107 if bool(getattr(obj, "__skip_doctest__", False)):
129 108 #print 'SKIPPING DOCTEST FOR:',obj # dbg
130 109 obj = DocTestSkip(obj)
131 110
132 111 doctest.DocTestFinder._find(self,tests, obj, name, module,
133 112 source_lines, globs, seen)
134 113
135 114 # Below we re-run pieces of the above method with manual modifications,
136 115 # because the original code is buggy and fails to correctly identify
137 116 # doctests in extension modules.
138 117
139 118 # Local shorthands
140 119 from inspect import isroutine, isclass
141 120
142 121 # Look for tests in a module's contained objects.
143 122 if inspect.ismodule(obj) and self._recurse:
144 123 for valname, val in obj.__dict__.items():
145 124 valname1 = '%s.%s' % (name, valname)
146 125 if ( (isroutine(val) or isclass(val))
147 126 and self._from_module(module, val) ):
148 127
149 128 self._find(tests, val, valname1, module, source_lines,
150 129 globs, seen)
151 130
152 131 # Look for tests in a class's contained objects.
153 132 if inspect.isclass(obj) and self._recurse:
154 133 #print 'RECURSE into class:',obj # dbg
155 134 for valname, val in obj.__dict__.items():
156 135 # Special handling for staticmethod/classmethod.
157 136 if isinstance(val, staticmethod):
158 137 val = getattr(obj, valname)
159 138 if isinstance(val, classmethod):
160 139 val = getattr(obj, valname).__func__
161 140
162 141 # Recurse to methods, properties, and nested classes.
163 142 if ((inspect.isfunction(val) or inspect.isclass(val) or
164 143 inspect.ismethod(val) or
165 144 isinstance(val, property)) and
166 145 self._from_module(module, val)):
167 146 valname = '%s.%s' % (name, valname)
168 147 self._find(tests, val, valname, module, source_lines,
169 148 globs, seen)
170 149
171 150
172 151 class IPDoctestOutputChecker(doctest.OutputChecker):
173 152 """Second-chance checker with support for random tests.
174 153
175 154 If the default comparison doesn't pass, this checker looks in the expected
176 155 output string for flags that tell us to ignore the output.
177 156 """
178 157
179 158 random_re = re.compile(r'#\s*random\s+')
180 159
181 160 def check_output(self, want, got, optionflags):
182 161 """Check output, accepting special markers embedded in the output.
183 162
184 163 If the output didn't pass the default validation but the special string
185 164 '#random' is included, we accept it."""
186 165
187 166 # Let the original tester verify first, in case people have valid tests
188 167 # that happen to have a comment saying '#random' embedded in.
189 168 ret = doctest.OutputChecker.check_output(self, want, got,
190 169 optionflags)
191 170 if not ret and self.random_re.search(want):
192 171 #print >> sys.stderr, 'RANDOM OK:',want # dbg
193 172 return True
194 173
195 174 return ret
196 175
197 176
198 class DocTestCase(doctests.DocTestCase):
199 """Proxy for DocTestCase: provides an address() method that
200 returns the correct address for the doctest case. Otherwise
201 acts as a proxy to the test case. To provide hints for address(),
202 an obj may also be passed -- this will be used as the test object
203 for purposes of determining the test address, if it is provided.
204 """
205
206 # Note: this method was taken from numpy's nosetester module.
207
208 # Subclass nose.plugins.doctests.DocTestCase to work around a bug in
209 # its constructor that blocks non-default arguments from being passed
210 # down into doctest.DocTestCase
211
212 def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
213 checker=None, obj=None, result_var='_'):
214 self._result_var = result_var
215 doctests.DocTestCase.__init__(self, test,
216 optionflags=optionflags,
217 setUp=setUp, tearDown=tearDown,
218 checker=checker)
219 # Now we must actually copy the original constructor from the stdlib
220 # doctest class, because we can't call it directly and a bug in nose
221 # means it never gets passed the right arguments.
222
223 self._dt_optionflags = optionflags
224 self._dt_checker = checker
225 self._dt_test = test
226 self._dt_test_globs_ori = test.globs
227 self._dt_setUp = setUp
228 self._dt_tearDown = tearDown
229
230 # XXX - store this runner once in the object!
231 runner = IPDocTestRunner(optionflags=optionflags,
232 checker=checker, verbose=False)
233 self._dt_runner = runner
234
235
236 # Each doctest should remember the directory it was loaded from, so
237 # things like %run work without too many contortions
238 self._ori_dir = os.path.dirname(test.filename)
239
240 # Modified runTest from the default stdlib
241 def runTest(self):
242 test = self._dt_test
243 runner = self._dt_runner
244
245 old = sys.stdout
246 new = StringIO()
247 optionflags = self._dt_optionflags
248
249 if not (optionflags & REPORTING_FLAGS):
250 # The option flags don't include any reporting flags,
251 # so add the default reporting flags
252 optionflags |= _unittest_reportflags
253
254 try:
255 # Save our current directory and switch out to the one where the
256 # test was originally created, in case another doctest did a
257 # directory change. We'll restore this in the finally clause.
258 curdir = os.getcwd()
259 #print 'runTest in dir:', self._ori_dir # dbg
260 os.chdir(self._ori_dir)
261
262 runner.DIVIDER = "-"*70
263 failures, tries = runner.run(test,out=new.write,
264 clear_globs=False)
265 finally:
266 sys.stdout = old
267 os.chdir(curdir)
268
269 if failures:
270 raise self.failureException(self.format_failure(new.getvalue()))
271
272 def setUp(self):
273 """Modified test setup that syncs with ipython namespace"""
274 #print "setUp test", self._dt_test.examples # dbg
275 if isinstance(self._dt_test.examples[0], IPExample):
276 # for IPython examples *only*, we swap the globals with the ipython
277 # namespace, after updating it with the globals (which doctest
278 # fills with the necessary info from the module being tested).
279 self.user_ns_orig = {}
280 self.user_ns_orig.update(_ip.user_ns)
281 _ip.user_ns.update(self._dt_test.globs)
282 # We must remove the _ key in the namespace, so that Python's
283 # doctest code sets it naturally
284 _ip.user_ns.pop('_', None)
285 _ip.user_ns['__builtins__'] = builtin_mod
286 self._dt_test.globs = _ip.user_ns
287
288 super(DocTestCase, self).setUp()
289
290 def tearDown(self):
291
292 # Undo the test.globs reassignment we made, so that the parent class
293 # teardown doesn't destroy the ipython namespace
294 if isinstance(self._dt_test.examples[0], IPExample):
295 self._dt_test.globs = self._dt_test_globs_ori
296 _ip.user_ns.clear()
297 _ip.user_ns.update(self.user_ns_orig)
298
299 # XXX - fperez: I am not sure if this is truly a bug in nose 0.11, but
300 # it does look like one to me: its tearDown method tries to run
301 #
302 # delattr(builtin_mod, self._result_var)
303 #
304 # without checking that the attribute really is there; it implicitly
305 # assumes it should have been set via displayhook. But if the
306 # displayhook was never called, this doesn't necessarily happen. I
307 # haven't been able to find a little self-contained example outside of
308 # ipython that would show the problem so I can report it to the nose
309 # team, but it does happen a lot in our code.
310 #
311 # So here, we just protect as narrowly as possible by trapping an
312 # attribute error whose message would be the name of self._result_var,
313 # and letting any other error propagate.
314 try:
315 super(DocTestCase, self).tearDown()
316 except AttributeError as exc:
317 if exc.args[0] != self._result_var:
318 raise
319
320
321 177 # A simple subclassing of the original with a different class name, so we can
322 178 # distinguish and treat differently IPython examples from pure python ones.
323 179 class IPExample(doctest.Example): pass
324 180
325 181
326 182 class IPExternalExample(doctest.Example):
327 183 """Doctest examples to be run in an external process."""
328 184
329 185 def __init__(self, source, want, exc_msg=None, lineno=0, indent=0,
330 186 options=None):
331 187 # Parent constructor
332 188 doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
333 189
334 190 # An EXTRA newline is needed to prevent pexpect hangs
335 191 self.source += '\n'
336 192
337 193
338 194 class IPDocTestParser(doctest.DocTestParser):
339 195 """
340 196 A class used to parse strings containing doctest examples.
341 197
342 198 Note: This is a version modified to properly recognize IPython input and
343 199 convert any IPython examples into valid Python ones.
344 200 """
345 201 # This regular expression is used to find doctest examples in a
346 202 # string. It defines three groups: `source` is the source code
347 203 # (including leading indentation and prompts); `indent` is the
348 204 # indentation of the first (PS1) line of the source code; and
349 205 # `want` is the expected output (including leading indentation).
350 206
351 207 # Classic Python prompts or default IPython ones
352 208 _PS1_PY = r'>>>'
353 209 _PS2_PY = r'\.\.\.'
354 210
355 211 _PS1_IP = r'In\ \[\d+\]:'
356 212 _PS2_IP = r'\ \ \ \.\.\.+:'
357 213
358 214 _RE_TPL = r'''
359 215 # Source consists of a PS1 line followed by zero or more PS2 lines.
360 216 (?P<source>
361 217 (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
362 218 (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
363 219 \n? # a newline
364 220 # Want consists of any non-blank lines that do not start with PS1.
365 221 (?P<want> (?:(?![ ]*$) # Not a blank line
366 222 (?![ ]*%s) # Not a line starting with PS1
367 223 (?![ ]*%s) # Not a line starting with PS2
368 224 .*$\n? # But any other line
369 225 )*)
370 226 '''
371 227
372 228 _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY),
373 229 re.MULTILINE | re.VERBOSE)
374 230
375 231 _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
376 232 re.MULTILINE | re.VERBOSE)
377 233
378 234 # Mark a test as being fully random. In this case, we simply append the
379 235 # random marker ('#random') to each individual example's output. This way
380 236 # we don't need to modify any other code.
381 237 _RANDOM_TEST = re.compile(r'#\s*all-random\s+')
382 238
383 239 # Mark tests to be executed in an external process - currently unsupported.
384 240 _EXTERNAL_IP = re.compile(r'#\s*ipdoctest:\s*EXTERNAL')
385 241
386 242 def ip2py(self,source):
387 243 """Convert input IPython source into valid Python."""
388 244 block = _ip.input_transformer_manager.transform_cell(source)
389 245 if len(block.splitlines()) == 1:
390 246 return _ip.prefilter(block)
391 247 else:
392 248 return block
393 249
394 250 def parse(self, string, name='<string>'):
395 251 """
396 252 Divide the given string into examples and intervening text,
397 253 and return them as a list of alternating Examples and strings.
398 254 Line numbers for the Examples are 0-based. The optional
399 255 argument `name` is a name identifying this string, and is only
400 256 used for error messages.
401 257 """
402 258
403 259 #print 'Parse string:\n',string # dbg
404 260
405 261 string = string.expandtabs()
406 262 # If all lines begin with the same indentation, then strip it.
407 263 min_indent = self._min_indent(string)
408 264 if min_indent > 0:
409 265 string = '\n'.join([l[min_indent:] for l in string.split('\n')])
410 266
411 267 output = []
412 268 charno, lineno = 0, 0
413 269
414 270 # We make 'all random' tests by adding the '# random' mark to every
415 271 # block of output in the test.
416 272 if self._RANDOM_TEST.search(string):
417 273 random_marker = '\n# random'
418 274 else:
419 275 random_marker = ''
420 276
421 277 # Whether to convert the input from ipython to python syntax
422 278 ip2py = False
423 279 # Find all doctest examples in the string. First, try them as Python
424 280 # examples, then as IPython ones
425 281 terms = list(self._EXAMPLE_RE_PY.finditer(string))
426 282 if terms:
427 283 # Normal Python example
428 284 #print '-'*70 # dbg
429 285 #print 'PyExample, Source:\n',string # dbg
430 286 #print '-'*70 # dbg
431 287 Example = doctest.Example
432 288 else:
433 289 # It's an ipython example. Note that IPExamples are run
434 290 # in-process, so their syntax must be turned into valid python.
435 291 # IPExternalExamples are run out-of-process (via pexpect) so they
436 292 # don't need any filtering (a real ipython will be executing them).
437 293 terms = list(self._EXAMPLE_RE_IP.finditer(string))
438 294 if self._EXTERNAL_IP.search(string):
439 295 #print '-'*70 # dbg
440 296 #print 'IPExternalExample, Source:\n',string # dbg
441 297 #print '-'*70 # dbg
442 298 Example = IPExternalExample
443 299 else:
444 300 #print '-'*70 # dbg
445 301 #print 'IPExample, Source:\n',string # dbg
446 302 #print '-'*70 # dbg
447 303 Example = IPExample
448 304 ip2py = True
449 305
450 306 for m in terms:
451 307 # Add the pre-example text to `output`.
452 308 output.append(string[charno:m.start()])
453 309 # Update lineno (lines before this example)
454 310 lineno += string.count('\n', charno, m.start())
455 311 # Extract info from the regexp match.
456 312 (source, options, want, exc_msg) = \
457 313 self._parse_example(m, name, lineno,ip2py)
458 314
459 315 # Append the random-output marker (it defaults to empty in most
460 316 # cases, it's only non-empty for 'all-random' tests):
461 317 want += random_marker
462 318
463 319 if Example is IPExternalExample:
464 320 options[doctest.NORMALIZE_WHITESPACE] = True
465 321 want += '\n'
466 322
467 323 # Create an Example, and add it to the list.
468 324 if not self._IS_BLANK_OR_COMMENT(source):
469 325 output.append(Example(source, want, exc_msg,
470 326 lineno=lineno,
471 327 indent=min_indent+len(m.group('indent')),
472 328 options=options))
473 329 # Update lineno (lines inside this example)
474 330 lineno += string.count('\n', m.start(), m.end())
475 331 # Update charno.
476 332 charno = m.end()
477 333 # Add any remaining post-example text to `output`.
478 334 output.append(string[charno:])
479 335 return output
480 336
481 337 def _parse_example(self, m, name, lineno,ip2py=False):
482 338 """
483 339 Given a regular expression match from `_EXAMPLE_RE` (`m`),
484 340 return a pair `(source, want)`, where `source` is the matched
485 341 example's source code (with prompts and indentation stripped);
486 342 and `want` is the example's expected output (with indentation
487 343 stripped).
488 344
489 345 `name` is the string's name, and `lineno` is the line number
490 346 where the example starts; both are used for error messages.
491 347
492 348 Optional:
493 349 `ip2py`: if true, filter the input via IPython to convert the syntax
494 350 into valid python.
495 351 """
496 352
497 353 # Get the example's indentation level.
498 354 indent = len(m.group('indent'))
499 355
500 356 # Divide source into lines; check that they're properly
501 357 # indented; and then strip their indentation & prompts.
502 358 source_lines = m.group('source').split('\n')
503 359
504 360 # We're using variable-length input prompts
505 361 ps1 = m.group('ps1')
506 362 ps2 = m.group('ps2')
507 363 ps1_len = len(ps1)
508 364
509 365 self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
510 366 if ps2:
511 367 self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
512 368
513 369 source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines])
514 370
515 371 if ip2py:
516 372 # Convert source input from IPython into valid Python syntax
517 373 source = self.ip2py(source)
518 374
519 375 # Divide want into lines; check that it's properly indented; and
520 376 # then strip the indentation. Spaces before the last newline should
521 377 # be preserved, so plain rstrip() isn't good enough.
522 378 want = m.group('want')
523 379 want_lines = want.split('\n')
524 380 if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]):
525 381 del want_lines[-1] # forget final newline & spaces after it
526 382 self._check_prefix(want_lines, ' '*indent, name,
527 383 lineno + len(source_lines))
528 384
529 385 # Remove ipython output prompt that might be present in the first line
530 386 want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0])
531 387
532 388 want = '\n'.join([wl[indent:] for wl in want_lines])
533 389
534 390 # If `want` contains a traceback message, then extract it.
535 391 m = self._EXCEPTION_RE.match(want)
536 392 if m:
537 393 exc_msg = m.group('msg')
538 394 else:
539 395 exc_msg = None
540 396
541 397 # Extract options from the source.
542 398 options = self._find_options(source, name, lineno)
543 399
544 400 return source, options, want, exc_msg
545 401
546 402 def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
547 403 """
548 404 Given the lines of a source string (including prompts and
549 405 leading indentation), check to make sure that every prompt is
550 406 followed by a space character. If any line is not followed by
551 407 a space character, then raise ValueError.
552 408
553 409 Note: IPython-modified version which takes the input prompt length as a
554 410 parameter, so that prompts of variable length can be dealt with.
555 411 """
556 412 space_idx = indent+ps1_len
557 413 min_len = space_idx+1
558 414 for i, line in enumerate(lines):
559 415 if len(line) >= min_len and line[space_idx] != ' ':
560 416 raise ValueError('line %r of the docstring for %s '
561 417 'lacks blank after %s: %r' %
562 418 (lineno+i+1, name,
563 419 line[indent:space_idx], line))
564 420
565 421
566 422 SKIP = doctest.register_optionflag('SKIP')
567 423
568 424
569 425 class IPDocTestRunner(doctest.DocTestRunner,object):
570 426 """Test runner that synchronizes the IPython namespace with test globals.
571 427 """
572 428
573 429 def run(self, test, compileflags=None, out=None, clear_globs=True):
574 430
575 431 # Hack: ipython needs access to the execution context of the example,
576 432 # so that it can propagate user variables loaded by %run into
577 433 # test.globs. We put them here into our modified %run as a function
578 434 # attribute. Our new %run will then only make the namespace update
579 435 # when called (rather than unconditionally updating test.globs here
580 436 # for all examples, most of which won't be calling %run anyway).
581 437 #_ip._ipdoctest_test_globs = test.globs
582 438 #_ip._ipdoctest_test_filename = test.filename
583 439
584 440 test.globs.update(_ip.user_ns)
585 441
586 442 # Override terminal size to standardise traceback format
587 443 with modified_env({'COLUMNS': '80', 'LINES': '24'}):
588 444 return super(IPDocTestRunner,self).run(test,
589 445 compileflags,out,clear_globs)
590 446
591 447
592 448 class DocFileCase(doctest.DocFileCase):
593 449 """Overrides to provide filename
594 450 """
595 451 def address(self):
596 452 return (self._dt_test.filename, None, None)
597
598
599 class ExtensionDoctest(doctests.Doctest):
600 """Nose Plugin that supports doctests in extension modules.
601 """
602 name = 'extdoctest' # call nosetests with --with-extdoctest
603 enabled = True
604
605 def options(self, parser, env=os.environ):
606 Plugin.options(self, parser, env)
607 parser.add_option('--doctest-tests', action='store_true',
608 dest='doctest_tests',
609 default=env.get('NOSE_DOCTEST_TESTS',True),
610 help="Also look for doctests in test modules. "
611 "Note that classes, methods and functions should "
612 "have either doctests or non-doctest tests, "
613 "not both. [NOSE_DOCTEST_TESTS]")
614 parser.add_option('--doctest-extension', action="append",
615 dest="doctestExtension",
616 help="Also look for doctests in files with "
617 "this extension [NOSE_DOCTEST_EXTENSION]")
618 # Set the default as a list, if given in env; otherwise
619 # an additional value set on the command line will cause
620 # an error.
621 env_setting = env.get('NOSE_DOCTEST_EXTENSION')
622 if env_setting is not None:
623 parser.set_defaults(doctestExtension=tolist(env_setting))
624
625
626 def configure(self, options, config):
627 Plugin.configure(self, options, config)
628 # Pull standard doctest plugin out of config; we will do doctesting
629 config.plugins.plugins = [p for p in config.plugins.plugins
630 if p.name != 'doctest']
631 self.doctest_tests = options.doctest_tests
632 self.extension = tolist(options.doctestExtension)
633
634 self.parser = doctest.DocTestParser()
635 self.finder = DocTestFinder()
636 self.checker = IPDoctestOutputChecker()
637 self.globs = None
638 self.extraglobs = None
639
640
641 def loadTestsFromExtensionModule(self,filename):
642 bpath,mod = os.path.split(filename)
643 modname = os.path.splitext(mod)[0]
644 try:
645 sys.path.append(bpath)
646 module = import_module(modname)
647 tests = list(self.loadTestsFromModule(module))
648 finally:
649 sys.path.pop()
650 return tests
651
652 # NOTE: the method below is almost a copy of the original one in nose, with
653 # a few modifications to control output checking.
654
655 def loadTestsFromModule(self, module):
656 #print '*** ipdoctest - lTM',module # dbg
657
658 if not self.matches(module.__name__):
659 log.debug("Doctest doesn't want module %s", module)
660 return
661
662 tests = self.finder.find(module,globs=self.globs,
663 extraglobs=self.extraglobs)
664 if not tests:
665 return
666
667 # always use whitespace and ellipsis options
668 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
669
670 tests.sort()
671 module_file = module.__file__
672 if module_file[-4:] in ('.pyc', '.pyo'):
673 module_file = module_file[:-1]
674 for test in tests:
675 if not test.examples:
676 continue
677 if not test.filename:
678 test.filename = module_file
679
680 yield DocTestCase(test,
681 optionflags=optionflags,
682 checker=self.checker)
683
684
685 def loadTestsFromFile(self, filename):
686 #print "ipdoctest - from file", filename # dbg
687 if is_extension_module(filename):
688 for t in self.loadTestsFromExtensionModule(filename):
689 yield t
690 else:
691 if self.extension and anyp(filename.endswith, self.extension):
692 name = PurePath(filename).name
693 doc = Path(filename).read_text()
694 test = self.parser.get_doctest(
695 doc, globs={'__file__': filename}, name=name,
696 filename=filename, lineno=0)
697 if test.examples:
698 #print 'FileCase:',test.examples # dbg
699 yield DocFileCase(test)
700 else:
701 yield False # no tests to load
702
703
704 class IPythonDoctest(ExtensionDoctest):
705 """Nose Plugin that supports doctests in extension modules.
706 """
707 name = 'ipdoctest' # call nosetests with --with-ipdoctest
708 enabled = True
709
710 def makeTest(self, obj, parent):
711 """Look for doctests in the given object, which will be a
712 function, method or class.
713 """
714 #print 'Plugin analyzing:', obj, parent # dbg
715 # always use whitespace and ellipsis options
716 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
717
718 doctests = self.finder.find(obj, module=getmodule(parent))
719 if doctests:
720 for test in doctests:
721 if len(test.examples) == 0:
722 continue
723
724 yield DocTestCase(test, obj=obj,
725 optionflags=optionflags,
726 checker=self.checker)
727
728 def options(self, parser, env=os.environ):
729 #print "Options for nose plugin:", self.name # dbg
730 Plugin.options(self, parser, env)
731 parser.add_option('--ipdoctest-tests', action='store_true',
732 dest='ipdoctest_tests',
733 default=env.get('NOSE_IPDOCTEST_TESTS',True),
734 help="Also look for doctests in test modules. "
735 "Note that classes, methods and functions should "
736 "have either doctests or non-doctest tests, "
737 "not both. [NOSE_IPDOCTEST_TESTS]")
738 parser.add_option('--ipdoctest-extension', action="append",
739 dest="ipdoctest_extension",
740 help="Also look for doctests in files with "
741 "this extension [NOSE_IPDOCTEST_EXTENSION]")
742 # Set the default as a list, if given in env; otherwise
743 # an additional value set on the command line will cause
744 # an error.
745 env_setting = env.get('NOSE_IPDOCTEST_EXTENSION')
746 if env_setting is not None:
747 parser.set_defaults(ipdoctest_extension=tolist(env_setting))
748
749 def configure(self, options, config):
750 #print "Configuring nose plugin:", self.name # dbg
751 Plugin.configure(self, options, config)
752 # Pull standard doctest plugin out of config; we will do doctesting
753 config.plugins.plugins = [p for p in config.plugins.plugins
754 if p.name != 'doctest']
755 self.doctest_tests = options.ipdoctest_tests
756 self.extension = tolist(options.ipdoctest_extension)
757
758 self.parser = IPDocTestParser()
759 self.finder = DocTestFinder(parser=self.parser)
760 self.checker = IPDoctestOutputChecker()
761 self.globs = None
762 self.extraglobs = None
@@ -1,493 +1,502 b''
1 1 # encoding: utf-8
2 2 """Tests for IPython.utils.path.py"""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7 import os
8 8 import shutil
9 9 import sys
10 10 import tempfile
11 11 import unittest
12 12 from contextlib import contextmanager
13 13 from unittest.mock import patch
14 14 from os.path import join, abspath
15 15 from imp import reload
16 16
17 from nose import SkipTest, with_setup
18 17 import pytest
19 18
20 19 import IPython
21 20 from IPython import paths
22 21 from IPython.testing import decorators as dec
23 22 from IPython.testing.decorators import (skip_if_not_win32, skip_win32,
24 23 onlyif_unicode_paths,
25 24 skip_win32_py38,)
26 25 from IPython.testing.tools import make_tempfile
27 26 from IPython.utils import path
28 27 from IPython.utils.tempdir import TemporaryDirectory
29 28
30 29
31 30 # Platform-dependent imports
32 31 try:
33 32 import winreg as wreg
34 33 except ImportError:
35 34 #Fake _winreg module on non-windows platforms
36 35 import types
37 36 wr_name = "winreg"
38 37 sys.modules[wr_name] = types.ModuleType(wr_name)
39 38 try:
40 39 import winreg as wreg
41 40 except ImportError:
42 41 import _winreg as wreg
43 42 #Add entries that needs to be stubbed by the testing code
44 43 (wreg.OpenKey, wreg.QueryValueEx,) = (None, None)
45 44
46 45 #-----------------------------------------------------------------------------
47 46 # Globals
48 47 #-----------------------------------------------------------------------------
49 48 env = os.environ
50 49 TMP_TEST_DIR = tempfile.mkdtemp()
51 50 HOME_TEST_DIR = join(TMP_TEST_DIR, "home_test_dir")
52 51 #
53 52 # Setup/teardown functions/decorators
54 53 #
55 54
56 55 def setup_module():
57 56 """Setup testenvironment for the module:
58 57
59 58 - Adds dummy home dir tree
60 59 """
61 60 # Do not mask exceptions here. In particular, catching WindowsError is a
62 61 # problem because that exception is only defined on Windows...
63 62 os.makedirs(os.path.join(HOME_TEST_DIR, 'ipython'))
64 63
65 64
66 65 def teardown_module():
67 66 """Teardown testenvironment for the module:
68 67
69 68 - Remove dummy home dir tree
70 69 """
71 70 # Note: we remove the parent test dir, which is the root of all test
72 71 # subdirs we may have created. Use shutil instead of os.removedirs, so
73 72 # that non-empty directories are all recursively removed.
74 73 shutil.rmtree(TMP_TEST_DIR)
75 74
76 75
77 76 def setup_environment():
78 77 """Setup testenvironment for some functions that are tested
79 78 in this module. In particular this functions stores attributes
80 79 and other things that we need to stub in some test functions.
81 80 This needs to be done on a function level and not module level because
82 81 each testfunction needs a pristine environment.
83 82 """
84 83 global oldstuff, platformstuff
85 84 oldstuff = (env.copy(), os.name, sys.platform, path.get_home_dir, IPython.__file__, os.getcwd())
86 85
87 86 def teardown_environment():
88 87 """Restore things that were remembered by the setup_environment function
89 88 """
90 89 (oldenv, os.name, sys.platform, path.get_home_dir, IPython.__file__, old_wd) = oldstuff
91 90 os.chdir(old_wd)
92 91 reload(path)
93 92
94 93 for key in list(env):
95 94 if key not in oldenv:
96 95 del env[key]
97 96 env.update(oldenv)
98 97 if hasattr(sys, 'frozen'):
99 98 del sys.frozen
100 99
100
101 101 # Build decorator that uses the setup_environment/setup_environment
102 with_environment = with_setup(setup_environment, teardown_environment)
102 @pytest.fixture
103 def environment():
104 setup_environment()
105 yield
106 teardown_environment()
107
108
109 with_environment = pytest.mark.usefixtures("environment")
110
103 111
104 112 @skip_if_not_win32
105 113 @with_environment
106 114 def test_get_home_dir_1():
107 115 """Testcase for py2exe logic, un-compressed lib
108 116 """
109 117 unfrozen = path.get_home_dir()
110 118 sys.frozen = True
111 119
112 120 #fake filename for IPython.__init__
113 121 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py"))
114 122
115 123 home_dir = path.get_home_dir()
116 124 assert home_dir == unfrozen
117 125
118 126
119 127 @skip_if_not_win32
120 128 @with_environment
121 129 def test_get_home_dir_2():
122 130 """Testcase for py2exe logic, compressed lib
123 131 """
124 132 unfrozen = path.get_home_dir()
125 133 sys.frozen = True
126 134 #fake filename for IPython.__init__
127 135 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower()
128 136
129 137 home_dir = path.get_home_dir(True)
130 138 assert home_dir == unfrozen
131 139
132 140
133 141 @skip_win32_py38
134 142 @with_environment
135 143 def test_get_home_dir_3():
136 144 """get_home_dir() uses $HOME if set"""
137 145 env["HOME"] = HOME_TEST_DIR
138 146 home_dir = path.get_home_dir(True)
139 147 # get_home_dir expands symlinks
140 148 assert home_dir == os.path.realpath(env["HOME"])
141 149
142 150
143 151 @with_environment
144 152 def test_get_home_dir_4():
145 153 """get_home_dir() still works if $HOME is not set"""
146 154
147 155 if 'HOME' in env: del env['HOME']
148 156 # this should still succeed, but we don't care what the answer is
149 157 home = path.get_home_dir(False)
150 158
151 159 @skip_win32_py38
152 160 @with_environment
153 161 def test_get_home_dir_5():
154 162 """raise HomeDirError if $HOME is specified, but not a writable dir"""
155 163 env['HOME'] = abspath(HOME_TEST_DIR+'garbage')
156 164 # set os.name = posix, to prevent My Documents fallback on Windows
157 165 os.name = 'posix'
158 166 pytest.raises(path.HomeDirError, path.get_home_dir, True)
159 167
160 168 # Should we stub wreg fully so we can run the test on all platforms?
161 169 @skip_if_not_win32
162 170 @with_environment
163 171 def test_get_home_dir_8():
164 172 """Using registry hack for 'My Documents', os=='nt'
165 173
166 174 HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing.
167 175 """
168 176 os.name = 'nt'
169 177 # Remove from stub environment all keys that may be set
170 178 for key in ['HOME', 'HOMESHARE', 'HOMEDRIVE', 'HOMEPATH', 'USERPROFILE']:
171 179 env.pop(key, None)
172 180
173 181 class key:
174 182 def __enter__(self):
175 183 pass
176 184 def Close(self):
177 185 pass
178 186 def __exit__(*args, **kwargs):
179 187 pass
180 188
181 189 with patch.object(wreg, 'OpenKey', return_value=key()), \
182 190 patch.object(wreg, 'QueryValueEx', return_value=[abspath(HOME_TEST_DIR)]):
183 191 home_dir = path.get_home_dir()
184 192 assert home_dir == abspath(HOME_TEST_DIR)
185 193
186 194 @with_environment
187 195 def test_get_xdg_dir_0():
188 196 """test_get_xdg_dir_0, check xdg_dir"""
189 197 reload(path)
190 198 path._writable_dir = lambda path: True
191 199 path.get_home_dir = lambda : 'somewhere'
192 200 os.name = "posix"
193 201 sys.platform = "linux2"
194 202 env.pop('IPYTHON_DIR', None)
195 203 env.pop('IPYTHONDIR', None)
196 204 env.pop('XDG_CONFIG_HOME', None)
197 205
198 206 assert path.get_xdg_dir() == os.path.join("somewhere", ".config")
199 207
200 208
201 209 @with_environment
202 210 def test_get_xdg_dir_1():
203 211 """test_get_xdg_dir_1, check nonexistent xdg_dir"""
204 212 reload(path)
205 213 path.get_home_dir = lambda : HOME_TEST_DIR
206 214 os.name = "posix"
207 215 sys.platform = "linux2"
208 216 env.pop('IPYTHON_DIR', None)
209 217 env.pop('IPYTHONDIR', None)
210 218 env.pop('XDG_CONFIG_HOME', None)
211 219 assert path.get_xdg_dir() is None
212 220
213 221 @with_environment
214 222 def test_get_xdg_dir_2():
215 223 """test_get_xdg_dir_2, check xdg_dir default to ~/.config"""
216 224 reload(path)
217 225 path.get_home_dir = lambda : HOME_TEST_DIR
218 226 os.name = "posix"
219 227 sys.platform = "linux2"
220 228 env.pop('IPYTHON_DIR', None)
221 229 env.pop('IPYTHONDIR', None)
222 230 env.pop('XDG_CONFIG_HOME', None)
223 231 cfgdir=os.path.join(path.get_home_dir(), '.config')
224 232 if not os.path.exists(cfgdir):
225 233 os.makedirs(cfgdir)
226 234
227 235 assert path.get_xdg_dir() == cfgdir
228 236
229 237 @with_environment
230 238 def test_get_xdg_dir_3():
231 239 """test_get_xdg_dir_3, check xdg_dir not used on OS X"""
232 240 reload(path)
233 241 path.get_home_dir = lambda : HOME_TEST_DIR
234 242 os.name = "posix"
235 243 sys.platform = "darwin"
236 244 env.pop('IPYTHON_DIR', None)
237 245 env.pop('IPYTHONDIR', None)
238 246 env.pop('XDG_CONFIG_HOME', None)
239 247 cfgdir=os.path.join(path.get_home_dir(), '.config')
240 248 os.makedirs(cfgdir, exist_ok=True)
241 249
242 250 assert path.get_xdg_dir() is None
243 251
244 252 def test_filefind():
245 253 """Various tests for filefind"""
246 254 f = tempfile.NamedTemporaryFile()
247 255 # print 'fname:',f.name
248 256 alt_dirs = paths.get_ipython_dir()
249 257 t = path.filefind(f.name, alt_dirs)
250 258 # print 'found:',t
251 259
252 260
253 261 @dec.skip_if_not_win32
254 262 def test_get_long_path_name_win32():
255 263 with TemporaryDirectory() as tmpdir:
256 264
257 265 # Make a long path. Expands the path of tmpdir prematurely as it may already have a long
258 266 # path component, so ensure we include the long form of it
259 267 long_path = os.path.join(path.get_long_path_name(tmpdir), 'this is my long path name')
260 268 os.makedirs(long_path)
261 269
262 270 # Test to see if the short path evaluates correctly.
263 271 short_path = os.path.join(tmpdir, 'THISIS~1')
264 272 evaluated_path = path.get_long_path_name(short_path)
265 273 assert evaluated_path.lower() == long_path.lower()
266 274
267 275
268 276 @dec.skip_win32
269 277 def test_get_long_path_name():
270 278 p = path.get_long_path_name("/usr/local")
271 279 assert p == "/usr/local"
272 280
273 281
274 282 class TestRaiseDeprecation(unittest.TestCase):
275 283
276 284 @dec.skip_win32 # can't create not-user-writable dir on win
277 285 @with_environment
278 286 def test_not_writable_ipdir(self):
279 287 tmpdir = tempfile.mkdtemp()
280 288 os.name = "posix"
281 289 env.pop('IPYTHON_DIR', None)
282 290 env.pop('IPYTHONDIR', None)
283 291 env.pop('XDG_CONFIG_HOME', None)
284 292 env['HOME'] = tmpdir
285 293 ipdir = os.path.join(tmpdir, '.ipython')
286 294 os.mkdir(ipdir, 0o555)
287 295 try:
288 296 open(os.path.join(ipdir, "_foo_"), 'w').close()
289 297 except IOError:
290 298 pass
291 299 else:
292 300 # I can still write to an unwritable dir,
293 301 # assume I'm root and skip the test
294 raise SkipTest("I can't create directories that I can't write to")
302 pytest.skip("I can't create directories that I can't write to")
303
295 304 with self.assertWarnsRegex(UserWarning, 'is not a writable location'):
296 305 ipdir = paths.get_ipython_dir()
297 306 env.pop('IPYTHON_DIR', None)
298 307
299 308 @with_environment
300 309 def test_get_py_filename():
301 310 os.chdir(TMP_TEST_DIR)
302 311 with make_tempfile("foo.py"):
303 312 assert path.get_py_filename("foo.py") == "foo.py"
304 313 assert path.get_py_filename("foo") == "foo.py"
305 314 with make_tempfile("foo"):
306 315 assert path.get_py_filename("foo") == "foo"
307 316 pytest.raises(IOError, path.get_py_filename, "foo.py")
308 317 pytest.raises(IOError, path.get_py_filename, "foo")
309 318 pytest.raises(IOError, path.get_py_filename, "foo.py")
310 319 true_fn = "foo with spaces.py"
311 320 with make_tempfile(true_fn):
312 321 assert path.get_py_filename("foo with spaces") == true_fn
313 322 assert path.get_py_filename("foo with spaces.py") == true_fn
314 323 pytest.raises(IOError, path.get_py_filename, '"foo with spaces.py"')
315 324 pytest.raises(IOError, path.get_py_filename, "'foo with spaces.py'")
316 325
317 326 @onlyif_unicode_paths
318 327 def test_unicode_in_filename():
319 328 """When a file doesn't exist, the exception raised should be safe to call
320 329 str() on - i.e. in Python 2 it must only have ASCII characters.
321 330
322 331 https://github.com/ipython/ipython/issues/875
323 332 """
324 333 try:
325 334 # these calls should not throw unicode encode exceptions
326 335 path.get_py_filename('fooéè.py')
327 336 except IOError as ex:
328 337 str(ex)
329 338
330 339
331 340 class TestShellGlob(unittest.TestCase):
332 341
333 342 @classmethod
334 343 def setUpClass(cls):
335 344 cls.filenames_start_with_a = ['a0', 'a1', 'a2']
336 345 cls.filenames_end_with_b = ['0b', '1b', '2b']
337 346 cls.filenames = cls.filenames_start_with_a + cls.filenames_end_with_b
338 347 cls.tempdir = TemporaryDirectory()
339 348 td = cls.tempdir.name
340 349
341 350 with cls.in_tempdir():
342 351 # Create empty files
343 352 for fname in cls.filenames:
344 353 open(os.path.join(td, fname), 'w').close()
345 354
346 355 @classmethod
347 356 def tearDownClass(cls):
348 357 cls.tempdir.cleanup()
349 358
350 359 @classmethod
351 360 @contextmanager
352 361 def in_tempdir(cls):
353 362 save = os.getcwd()
354 363 try:
355 364 os.chdir(cls.tempdir.name)
356 365 yield
357 366 finally:
358 367 os.chdir(save)
359 368
360 369 def check_match(self, patterns, matches):
361 370 with self.in_tempdir():
362 371 # glob returns unordered list. that's why sorted is required.
363 372 assert sorted(path.shellglob(patterns)) == sorted(matches)
364 373
365 374 def common_cases(self):
366 375 return [
367 376 (['*'], self.filenames),
368 377 (['a*'], self.filenames_start_with_a),
369 378 (['*c'], ['*c']),
370 379 (['*', 'a*', '*b', '*c'], self.filenames
371 380 + self.filenames_start_with_a
372 381 + self.filenames_end_with_b
373 382 + ['*c']),
374 383 (['a[012]'], self.filenames_start_with_a),
375 384 ]
376 385
377 386 @skip_win32
378 387 def test_match_posix(self):
379 388 for (patterns, matches) in self.common_cases() + [
380 389 ([r'\*'], ['*']),
381 390 ([r'a\*', 'a*'], ['a*'] + self.filenames_start_with_a),
382 391 ([r'a\[012]'], ['a[012]']),
383 392 ]:
384 393 yield (self.check_match, patterns, matches)
385 394
386 395 @skip_if_not_win32
387 396 def test_match_windows(self):
388 397 for (patterns, matches) in self.common_cases() + [
389 398 # In windows, backslash is interpreted as path
390 399 # separator. Therefore, you can't escape glob
391 400 # using it.
392 401 ([r'a\*', 'a*'], [r'a\*'] + self.filenames_start_with_a),
393 402 ([r'a\[012]'], [r'a\[012]']),
394 403 ]:
395 404 yield (self.check_match, patterns, matches)
396 405
397 406
398 407 # TODO : pytest.mark.parametrise once nose is gone.
399 408 def test_unescape_glob():
400 409 assert path.unescape_glob(r"\*\[\!\]\?") == "*[!]?"
401 410 assert path.unescape_glob(r"\\*") == r"\*"
402 411 assert path.unescape_glob(r"\\\*") == r"\*"
403 412 assert path.unescape_glob(r"\\a") == r"\a"
404 413 assert path.unescape_glob(r"\a") == r"\a"
405 414
406 415
407 416 @onlyif_unicode_paths
408 417 def test_ensure_dir_exists():
409 418 with TemporaryDirectory() as td:
410 419 d = os.path.join(td, '∂ir')
411 420 path.ensure_dir_exists(d) # create it
412 421 assert os.path.isdir(d)
413 422 path.ensure_dir_exists(d) # no-op
414 423 f = os.path.join(td, 'ƒile')
415 424 open(f, 'w').close() # touch
416 425 with pytest.raises(IOError):
417 426 path.ensure_dir_exists(f)
418 427
419 428 class TestLinkOrCopy(unittest.TestCase):
420 429 def setUp(self):
421 430 self.tempdir = TemporaryDirectory()
422 431 self.src = self.dst("src")
423 432 with open(self.src, "w") as f:
424 433 f.write("Hello, world!")
425 434
426 435 def tearDown(self):
427 436 self.tempdir.cleanup()
428 437
429 438 def dst(self, *args):
430 439 return os.path.join(self.tempdir.name, *args)
431 440
432 441 def assert_inode_not_equal(self, a, b):
433 442 assert (
434 443 os.stat(a).st_ino != os.stat(b).st_ino
435 444 ), "%r and %r do reference the same indoes" % (a, b)
436 445
437 446 def assert_inode_equal(self, a, b):
438 447 assert (
439 448 os.stat(a).st_ino == os.stat(b).st_ino
440 449 ), "%r and %r do not reference the same indoes" % (a, b)
441 450
442 451 def assert_content_equal(self, a, b):
443 452 with open(a) as a_f:
444 453 with open(b) as b_f:
445 454 assert a_f.read() == b_f.read()
446 455
447 456 @skip_win32
448 457 def test_link_successful(self):
449 458 dst = self.dst("target")
450 459 path.link_or_copy(self.src, dst)
451 460 self.assert_inode_equal(self.src, dst)
452 461
453 462 @skip_win32
454 463 def test_link_into_dir(self):
455 464 dst = self.dst("some_dir")
456 465 os.mkdir(dst)
457 466 path.link_or_copy(self.src, dst)
458 467 expected_dst = self.dst("some_dir", os.path.basename(self.src))
459 468 self.assert_inode_equal(self.src, expected_dst)
460 469
461 470 @skip_win32
462 471 def test_target_exists(self):
463 472 dst = self.dst("target")
464 473 open(dst, "w").close()
465 474 path.link_or_copy(self.src, dst)
466 475 self.assert_inode_equal(self.src, dst)
467 476
468 477 @skip_win32
469 478 def test_no_link(self):
470 479 real_link = os.link
471 480 try:
472 481 del os.link
473 482 dst = self.dst("target")
474 483 path.link_or_copy(self.src, dst)
475 484 self.assert_content_equal(self.src, dst)
476 485 self.assert_inode_not_equal(self.src, dst)
477 486 finally:
478 487 os.link = real_link
479 488
480 489 @skip_if_not_win32
481 490 def test_windows(self):
482 491 dst = self.dst("target")
483 492 path.link_or_copy(self.src, dst)
484 493 self.assert_content_equal(self.src, dst)
485 494
486 495 def test_link_twice(self):
487 496 # Linking the same file twice shouldn't leave duplicates around.
488 497 # See https://github.com/ipython/ipython/issues/6450
489 498 dst = self.dst('target')
490 499 path.link_or_copy(self.src, dst)
491 500 path.link_or_copy(self.src, dst)
492 501 self.assert_inode_equal(self.src, dst)
493 502 assert sorted(os.listdir(self.tempdir.name)) == ["src", "target"]
@@ -1,39 +1,35 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 global:
7 7 COLUMNS: 120 # Appveyor web viwer window width is 130 chars
8 8
9 9 matrix:
10 10
11 11 - PYTHON: "C:\\Python37-x64"
12 12 PYTHON_VERSION: "3.7.x"
13 13 PYTHON_ARCH: "64"
14 14
15 15 - PYTHON: "C:\\Python38"
16 16 PYTHON_VERSION: "3.8.x"
17 17 PYTHON_ARCH: "32"
18 18
19 19 - PYTHON: "C:\\Python38-x64"
20 20 PYTHON_VERSION: "3.8.x"
21 21 PYTHON_ARCH: "64"
22 22
23 23 init:
24 24 - "ECHO %PYTHON% %PYTHON_VERSION% %PYTHON_ARCH%"
25 25
26 26 install:
27 27 - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
28 28 - python -m pip install --upgrade setuptools pip
29 - pip install nose coverage pytest pytest-cov pytest-trio matplotlib pandas
29 - pip install pytest pytest-cov pytest-trio matplotlib pandas
30 30 - pip install -e .[test]
31 - mkdir results
32 - cd results
33 31 test_script:
34 - iptest --coverage xml
35 - cd ..
36 32 - pytest --color=yes -ra --cov --cov-report=xml
37 33 on_finish:
38 34 - curl -Os https://uploader.codecov.io/latest/windows/codecov.exe
39 35 - codecov -e PYTHON_VERSION,PYTHON_ARCH
@@ -1,15 +1,14 b''
1 1 name: ipython_docs
2 2 dependencies:
3 3 - python=3.8
4 4 - setuptools>=18.5
5 5 - sphinx>=1.8
6 6 - sphinx_rtd_theme
7 7 - numpy
8 - nose
9 8 - testpath
10 9 - matplotlib
11 10 - pip:
12 11 - docrepr
13 12 - prompt_toolkit
14 13 - ipykernel
15 14 - stack_data
@@ -1,144 +1,143 b''
1 1 .. _install:
2 2
3 3 Installing IPython
4 4 ==================
5 5
6 6
7 7 IPython 6 requires Python ≥ 3.3. IPython 5.x can be installed on Python 2.
8 8
9 9
10 10 Quick Install
11 11 -------------
12 12
13 13 With ``pip`` already installed :
14 14
15 15 .. code-block:: bash
16 16
17 17 $ pip install ipython
18 18
19 19 This installs IPython as well as its dependencies.
20 20
21 21 If you want to use IPython with notebooks or the Qt console, you should also
22 22 install Jupyter ``pip install jupyter``.
23 23
24 24
25 25
26 26 Overview
27 27 --------
28 28
29 29 This document describes in detail the steps required to install IPython. For a
30 30 few quick ways to get started with package managers or full Python
31 31 distributions, see `the install page <https://ipython.org/install.html>`_ of the
32 32 IPython website.
33 33
34 34 Please let us know if you have problems installing IPython or any of its
35 35 dependencies.
36 36
37 37 IPython and most dependencies should be installed via :command:`pip`.
38 38 In many scenarios, this is the simplest method of installing Python packages.
39 39 More information about :mod:`pip` can be found on
40 40 `its PyPI page <https://pip.pypa.io>`__.
41 41
42 42
43 43 More general information about installing Python packages can be found in
44 44 `Python's documentation <http://docs.python.org>`_.
45 45
46 46 .. _dependencies:
47 47
48 48 Dependencies
49 49 ~~~~~~~~~~~~
50 50
51 51 IPython relies on a number of other Python packages. Installing using a package
52 52 manager like pip or conda will ensure the necessary packages are installed.
53 53 Manual installation without dependencies is possible, but not recommended.
54 54 The dependencies can be viewed with package manager commands,
55 55 such as :command:`pip show ipython` or :command:`conda info ipython`.
56 56
57 57
58 58 Installing IPython itself
59 59 ~~~~~~~~~~~~~~~~~~~~~~~~~
60 60
61 61 IPython requires several dependencies to work correctly, it is not recommended
62 62 to install IPython and all its dependencies manually as this can be quite long
63 63 and troublesome. You should use the python package manager ``pip``.
64 64
65 65 Installation using pip
66 66 ~~~~~~~~~~~~~~~~~~~~~~
67 67
68 68 Make sure you have the latest version of :mod:`pip` (the Python package
69 69 manager) installed. If you do not, head to `Pip documentation
70 70 <https://pip.pypa.io/en/stable/installing/>`_ and install :mod:`pip` first.
71 71
72 72 The quickest way to get up and running with IPython is to install it with pip:
73 73
74 74 .. code-block:: bash
75 75
76 76 $ pip install ipython
77 77
78 78 That's it.
79 79
80 80
81 81 Installation from source
82 82 ~~~~~~~~~~~~~~~~~~~~~~~~
83 83
84 84 To install IPython from source,
85 85 grab the latest stable tarball of IPython `from PyPI
86 86 <https://pypi.python.org/pypi/ipython>`__. Then do the following:
87 87
88 88 .. code-block:: bash
89 89
90 90 tar -xzf ipython-5.1.0.tar.gz
91 91 cd ipython-5.1.0
92 92 # The [test] extra ensures test dependencies are installed too:
93 93 pip install '.[test]'
94 94
95 95 Do not invoke ``setup.py`` directly as this can have undesirable consequences
96 96 for further upgrades. We do not recommend using ``easy_install`` either.
97 97
98 98 If you are installing to a location (like ``/usr/local``) that requires higher
99 99 permissions, you may need to run the last command with :command:`sudo`. You can
100 100 also install in user specific location by using the ``--user`` flag in
101 101 conjunction with pip.
102 102
103 To run IPython's test suite, use the :command:`iptest` command from outside of
104 the IPython source tree:
103 To run IPython's test suite, use the :command:`pytest` command:
105 104
106 105 .. code-block:: bash
107 106
108 $ iptest
107 $ pytest
109 108
110 109 .. _devinstall:
111 110
112 111 Installing the development version
113 112 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
114 113
115 114 It is also possible to install the development version of IPython from our
116 115 `Git <http://git-scm.com/>`_ source code repository. To do this you will
117 116 need to have Git installed on your system.
118 117
119 118
120 119 Then do:
121 120
122 121 .. code-block:: bash
123 122
124 123 $ git clone https://github.com/ipython/ipython.git
125 124 $ cd ipython
126 125 $ pip install -e '.[test]'
127 126
128 127 The :command:`pip install -e .` command allows users and developers to follow
129 128 the development branch as it changes by creating links in the right places and
130 129 installing the command line scripts to the appropriate locations.
131 130
132 131 Then, if you want to update your IPython at any time, do:
133 132
134 133 .. code-block:: bash
135 134
136 135 $ git pull
137 136
138 137 If the dependencies or entrypoints have changed, you may have to run
139 138
140 139 .. code-block:: bash
141 140
142 141 $ pip install -e .
143 142
144 143 again, but this is infrequent.
@@ -1,273 +1,272 b''
1 1 #!/usr/bin/env python3
2 2 # -*- coding: utf-8 -*-
3 3 """Setup script for IPython.
4 4
5 5 Under Posix environments it works like a typical setup.py script.
6 6 Under Windows, the command sdist is not supported, since IPython
7 7 requires utilities which are not available under Windows."""
8 8
9 9 #-----------------------------------------------------------------------------
10 10 # Copyright (c) 2008-2011, IPython Development Team.
11 11 # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu>
12 12 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
13 13 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
14 14 #
15 15 # Distributed under the terms of the Modified BSD License.
16 16 #
17 17 # The full license is in the file COPYING.rst, distributed with this software.
18 18 #-----------------------------------------------------------------------------
19 19
20 20 import os
21 21 import sys
22 22 from pathlib import Path
23 23
24 24 # **Python version check**
25 25 #
26 26 # This check is also made in IPython/__init__, don't forget to update both when
27 27 # changing Python version requirements.
28 28 if sys.version_info < (3, 7):
29 29 pip_message = 'This may be due to an out of date pip. Make sure you have pip >= 9.0.1.'
30 30 try:
31 31 import pip
32 32 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
33 33 if pip_version < (9, 0, 1) :
34 34 pip_message = 'Your pip version is out of date, please install pip >= 9.0.1. '\
35 35 'pip {} detected.'.format(pip.__version__)
36 36 else:
37 37 # pip is new enough - it must be something else
38 38 pip_message = ''
39 39 except Exception:
40 40 pass
41 41
42 42
43 43 error = """
44 44 IPython 7.17+ supports Python 3.7 and above, following NEP 29.
45 45 When using Python 2.7, please install IPython 5.x LTS Long Term Support version.
46 46 Python 3.3 and 3.4 were supported up to IPython 6.x.
47 47 Python 3.5 was supported with IPython 7.0 to 7.9.
48 48 Python 3.6 was supported with IPython up to 7.16.
49 49
50 50 See IPython `README.rst` file for more information:
51 51
52 52 https://github.com/ipython/ipython/blob/master/README.rst
53 53
54 54 Python {py} detected.
55 55 {pip}
56 56 """.format(py=sys.version_info, pip=pip_message )
57 57
58 58 print(error, file=sys.stderr)
59 59 sys.exit(1)
60 60
61 61 # At least we're on the python version we need, move on.
62 62
63 63 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
64 64 # update it when the contents of directories change.
65 65 if Path("MANIFEST").exists():
66 66 Path("MANIFEST").unlink()
67 67
68 68 from distutils.core import setup
69 69
70 70 # Our own imports
71 71 from setupbase import target_update
72 72
73 73 from setupbase import (
74 74 setup_args,
75 75 find_packages,
76 76 find_package_data,
77 77 check_package_data_first,
78 78 find_entry_points,
79 79 build_scripts_entrypt,
80 80 find_data_files,
81 81 git_prebuild,
82 82 install_symlinked,
83 83 install_lib_symlink,
84 84 install_scripts_for_symlink,
85 85 unsymlink,
86 86 )
87 87
88 88 #-------------------------------------------------------------------------------
89 89 # Handle OS specific things
90 90 #-------------------------------------------------------------------------------
91 91
92 92 if os.name in ('nt','dos'):
93 93 os_name = 'windows'
94 94 else:
95 95 os_name = os.name
96 96
97 97 # Under Windows, 'sdist' has not been supported. Now that the docs build with
98 98 # Sphinx it might work, but let's not turn it on until someone confirms that it
99 99 # actually works.
100 100 if os_name == 'windows' and 'sdist' in sys.argv:
101 101 print('The sdist command is not available under Windows. Exiting.')
102 102 sys.exit(1)
103 103
104 104
105 105 #-------------------------------------------------------------------------------
106 106 # Things related to the IPython documentation
107 107 #-------------------------------------------------------------------------------
108 108
109 109 # update the manuals when building a source dist
110 110 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
111 111
112 112 # List of things to be updated. Each entry is a triplet of args for
113 113 # target_update()
114 114 to_update = [
115 115 ('docs/man/ipython.1.gz',
116 116 ['docs/man/ipython.1'],
117 117 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'),
118 118 ]
119 119
120 120
121 121 [ target_update(*t) for t in to_update ]
122 122
123 123 #---------------------------------------------------------------------------
124 124 # Find all the packages, package data, and data_files
125 125 #---------------------------------------------------------------------------
126 126
127 127 packages = find_packages()
128 128 package_data = find_package_data()
129 129
130 130 data_files = find_data_files()
131 131
132 132 setup_args['packages'] = packages
133 133 setup_args['package_data'] = package_data
134 134 setup_args['data_files'] = data_files
135 135
136 136 #---------------------------------------------------------------------------
137 137 # custom distutils commands
138 138 #---------------------------------------------------------------------------
139 139 # imports here, so they are after setuptools import if there was one
140 140 from distutils.command.sdist import sdist
141 141
142 142 setup_args['cmdclass'] = {
143 143 'build_py': \
144 144 check_package_data_first(git_prebuild('IPython')),
145 145 'sdist' : git_prebuild('IPython', sdist),
146 146 'symlink': install_symlinked,
147 147 'install_lib_symlink': install_lib_symlink,
148 148 'install_scripts_sym': install_scripts_for_symlink,
149 149 'unsymlink': unsymlink,
150 150 }
151 151
152 152
153 153 #---------------------------------------------------------------------------
154 154 # Handle scripts, dependencies, and setuptools specific things
155 155 #---------------------------------------------------------------------------
156 156
157 157 # For some commands, use setuptools. Note that we do NOT list install here!
158 158 # If you want a setuptools-enhanced install, just run 'setupegg.py install'
159 159 needs_setuptools = {'develop', 'release', 'bdist_egg', 'bdist_rpm',
160 160 'bdist', 'bdist_dumb', 'bdist_wininst', 'bdist_wheel',
161 161 'egg_info', 'easy_install', 'upload', 'install_egg_info',
162 162 }
163 163
164 164 if len(needs_setuptools.intersection(sys.argv)) > 0:
165 165 import setuptools
166 166
167 167 # This dict is used for passing extra arguments that are setuptools
168 168 # specific to setup
169 169 setuptools_extra_args = {}
170 170
171 171 # setuptools requirements
172 172
173 173 extras_require = dict(
174 174 parallel=["ipyparallel"],
175 175 qtconsole=["qtconsole"],
176 176 doc=["Sphinx>=1.3"],
177 177 test=[
178 "nose>=0.10.1",
179 178 "pytest",
180 179 "requests",
181 180 "testpath",
182 181 "pygments",
183 182 "nbformat",
184 183 "ipykernel",
185 184 "numpy>=1.17",
186 185 ],
187 186 terminal=[],
188 187 kernel=["ipykernel"],
189 188 nbformat=["nbformat"],
190 189 notebook=["notebook", "ipywidgets"],
191 190 nbconvert=["nbconvert"],
192 191 )
193 192
194 193 install_requires = [
195 194 "setuptools>=18.5",
196 195 "jedi>=0.16",
197 196 "decorator",
198 197 "pickleshare",
199 198 "traitlets>=4.2",
200 199 "prompt_toolkit>=2.0.0,<3.1.0,!=3.0.0,!=3.0.1",
201 200 "pygments",
202 201 "backcall",
203 202 "stack_data",
204 203 "matplotlib-inline",
205 204 ]
206 205
207 206 # Platform-specific dependencies:
208 207 # This is the correct way to specify these,
209 208 # but requires pip >= 6. pip < 6 ignores these.
210 209
211 210 extras_require.update(
212 211 {
213 212 ':sys_platform != "win32"': ["pexpect>4.3"],
214 213 ':sys_platform == "darwin"': ["appnope"],
215 214 ':sys_platform == "win32"': ["colorama"],
216 215 }
217 216 )
218 217 # FIXME: re-specify above platform dependencies for pip < 6
219 218 # These would result in non-portable bdists.
220 219 if not any(arg.startswith('bdist') for arg in sys.argv):
221 220 if sys.platform == 'darwin':
222 221 install_requires.extend(['appnope'])
223 222
224 223 if not sys.platform.startswith("win"):
225 224 install_requires.append("pexpect>4.3")
226 225
227 226 # workaround pypa/setuptools#147, where setuptools misspells
228 227 # platform_python_implementation as python_implementation
229 228 if 'setuptools' in sys.modules:
230 229 for key in list(extras_require):
231 230 if 'platform_python_implementation' in key:
232 231 new_key = key.replace('platform_python_implementation', 'python_implementation')
233 232 extras_require[new_key] = extras_require.pop(key)
234 233
235 234 everything = set()
236 235 for key, deps in extras_require.items():
237 236 if ':' not in key:
238 237 everything.update(deps)
239 238 extras_require['all'] = list(sorted(everything))
240 239
241 240 if 'setuptools' in sys.modules:
242 241 setuptools_extra_args['python_requires'] = '>=3.7'
243 242 setuptools_extra_args['zip_safe'] = False
244 243 setuptools_extra_args['entry_points'] = {
245 244 'console_scripts': find_entry_points(),
246 245 'pygments.lexers': [
247 246 'ipythonconsole = IPython.lib.lexers:IPythonConsoleLexer',
248 247 'ipython = IPython.lib.lexers:IPythonLexer',
249 248 'ipython3 = IPython.lib.lexers:IPython3Lexer',
250 249 ],
251 250 }
252 251 setup_args['extras_require'] = extras_require
253 252 setup_args['install_requires'] = install_requires
254 253
255 254 else:
256 255 # scripts has to be a non-empty list, or install_scripts isn't called
257 256 setup_args['scripts'] = [e.split('=')[0].strip() for e in find_entry_points()]
258 257
259 258 setup_args['cmdclass']['build_scripts'] = build_scripts_entrypt
260 259
261 260 #---------------------------------------------------------------------------
262 261 # Do the actual setup now
263 262 #---------------------------------------------------------------------------
264 263
265 264 setup_args.update(setuptools_extra_args)
266 265
267 266
268 267
269 268 def main():
270 269 setup(**setup_args)
271 270
272 271 if __name__ == '__main__':
273 272 main()
@@ -1,408 +1,407 b''
1 1 # encoding: utf-8
2 2 """
3 3 This module defines the things that are used in setup.py for building IPython
4 4
5 5 This includes:
6 6
7 7 * The basic arguments to setup
8 8 * Functions for finding things like packages, package data, etc.
9 9 * A function for checking dependencies.
10 10 """
11 11
12 12 # Copyright (c) IPython Development Team.
13 13 # Distributed under the terms of the Modified BSD License.
14 14
15 15 import os
16 16 import re
17 17 import sys
18 18 from glob import glob
19 19 from logging import log
20 20
21 21 from setuptools import Command
22 22 from setuptools.command.build_py import build_py
23 23
24 24 # TODO: Replacement for this?
25 25 from distutils.command.build_scripts import build_scripts
26 26 from setuptools.command.install import install
27 27 from setuptools.command.install_scripts import install_scripts
28 28
29 29 from setupext import install_data_ext
30 30
31 31 #-------------------------------------------------------------------------------
32 32 # Useful globals and utility functions
33 33 #-------------------------------------------------------------------------------
34 34
35 35 # A few handy globals
36 36 isfile = os.path.isfile
37 37 pjoin = os.path.join
38 38 repo_root = os.path.dirname(os.path.abspath(__file__))
39 39
40 40 def execfile(fname, globs, locs=None):
41 41 locs = locs or globs
42 42 with open(fname) as f:
43 43 exec(compile(f.read(), fname, "exec"), globs, locs)
44 44
45 45 # A little utility we'll need below, since glob() does NOT allow you to do
46 46 # exclusion on multiple endings!
47 47 def file_doesnt_endwith(test,endings):
48 48 """Return true if test is a file and its name does NOT end with any
49 49 of the strings listed in endings."""
50 50 if not isfile(test):
51 51 return False
52 52 for e in endings:
53 53 if test.endswith(e):
54 54 return False
55 55 return True
56 56
57 57 #---------------------------------------------------------------------------
58 58 # Basic project information
59 59 #---------------------------------------------------------------------------
60 60
61 61 # release.py contains version, authors, license, url, keywords, etc.
62 62 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
63 63
64 64 # Create a dict with the basic information
65 65 # This dict is eventually passed to setup after additional keys are added.
66 66 setup_args = dict(
67 67 name = name,
68 68 version = version,
69 69 description = description,
70 70 long_description = long_description,
71 71 author = author,
72 72 author_email = author_email,
73 73 url = url,
74 74 license = license,
75 75 platforms = platforms,
76 76 keywords = keywords,
77 77 classifiers = classifiers,
78 78 cmdclass = {'install_data': install_data_ext},
79 79 project_urls={
80 80 'Documentation': 'https://ipython.readthedocs.io/',
81 81 'Funding' : 'https://numfocus.org/',
82 82 'Source' : 'https://github.com/ipython/ipython',
83 83 'Tracker' : 'https://github.com/ipython/ipython/issues',
84 84 }
85 85 )
86 86
87 87
88 88 #---------------------------------------------------------------------------
89 89 # Find packages
90 90 #---------------------------------------------------------------------------
91 91
92 92 def find_packages():
93 93 """
94 94 Find all of IPython's packages.
95 95 """
96 96 excludes = ['deathrow', 'quarantine']
97 97 packages = []
98 98 for directory, subdirs, files in os.walk("IPython"):
99 99 package = directory.replace(os.path.sep, ".")
100 100 if any(package.startswith("IPython." + exc) for exc in excludes):
101 101 # package is to be excluded (e.g. deathrow)
102 102 continue
103 103 if '__init__.py' not in files:
104 104 # not a package
105 105 continue
106 106 packages.append(package)
107 107 return packages
108 108
109 109 #---------------------------------------------------------------------------
110 110 # Find package data
111 111 #---------------------------------------------------------------------------
112 112
113 113 def find_package_data():
114 114 """
115 115 Find IPython's package_data.
116 116 """
117 117 # This is not enough for these things to appear in an sdist.
118 118 # We need to muck with the MANIFEST to get this to work
119 119
120 120 package_data = {
121 121 'IPython.core' : ['profile/README*'],
122 122 'IPython.core.tests' : ['*.png', '*.jpg', 'daft_extension/*.py'],
123 123 'IPython.lib.tests' : ['*.wav'],
124 124 'IPython.testing.plugin' : ['*.txt'],
125 125 }
126 126
127 127 return package_data
128 128
129 129
130 130 def check_package_data(package_data):
131 131 """verify that package_data globs make sense"""
132 132 print("checking package data")
133 133 for pkg, data in package_data.items():
134 134 pkg_root = pjoin(*pkg.split('.'))
135 135 for d in data:
136 136 path = pjoin(pkg_root, d)
137 137 if '*' in path:
138 138 assert len(glob(path)) > 0, "No files match pattern %s" % path
139 139 else:
140 140 assert os.path.exists(path), "Missing package data: %s" % path
141 141
142 142
143 143 def check_package_data_first(command):
144 144 """decorator for checking package_data before running a given command
145 145
146 146 Probably only needs to wrap build_py
147 147 """
148 148 class DecoratedCommand(command):
149 149 def run(self):
150 150 check_package_data(self.package_data)
151 151 command.run(self)
152 152 return DecoratedCommand
153 153
154 154
155 155 #---------------------------------------------------------------------------
156 156 # Find data files
157 157 #---------------------------------------------------------------------------
158 158
159 159 def find_data_files():
160 160 """
161 161 Find IPython's data_files.
162 162
163 163 Just man pages at this point.
164 164 """
165 165
166 166 if "freebsd" in sys.platform:
167 167 manpagebase = pjoin('man', 'man1')
168 168 else:
169 169 manpagebase = pjoin('share', 'man', 'man1')
170 170
171 171 # Simple file lists can be made by hand
172 172 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
173 173 if not manpages:
174 174 # When running from a source tree, the manpages aren't gzipped
175 175 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
176 176
177 177 # And assemble the entire output list
178 178 data_files = [ (manpagebase, manpages) ]
179 179
180 180 return data_files
181 181
182 182
183 183 # The two functions below are copied from IPython.utils.path, so we don't need
184 184 # to import IPython during setup, which fails on Python 3.
185 185
186 186 def target_outdated(target,deps):
187 187 """Determine whether a target is out of date.
188 188
189 189 target_outdated(target,deps) -> 1/0
190 190
191 191 deps: list of filenames which MUST exist.
192 192 target: single filename which may or may not exist.
193 193
194 194 If target doesn't exist or is older than any file listed in deps, return
195 195 true, otherwise return false.
196 196 """
197 197 try:
198 198 target_time = os.path.getmtime(target)
199 199 except os.error:
200 200 return 1
201 201 for dep in deps:
202 202 dep_time = os.path.getmtime(dep)
203 203 if dep_time > target_time:
204 204 #print "For target",target,"Dep failed:",dep # dbg
205 205 #print "times (dep,tar):",dep_time,target_time # dbg
206 206 return 1
207 207 return 0
208 208
209 209
210 210 def target_update(target,deps,cmd):
211 211 """Update a target with a given command given a list of dependencies.
212 212
213 213 target_update(target,deps,cmd) -> runs cmd if target is outdated.
214 214
215 215 This is just a wrapper around target_outdated() which calls the given
216 216 command if target is outdated."""
217 217
218 218 if target_outdated(target,deps):
219 219 os.system(cmd)
220 220
221 221 #---------------------------------------------------------------------------
222 222 # Find scripts
223 223 #---------------------------------------------------------------------------
224 224
225 225 def find_entry_points():
226 226 """Defines the command line entry points for IPython
227 227
228 228 This always uses setuptools-style entry points. When setuptools is not in
229 229 use, our own build_scripts_entrypt class below parses these and builds
230 230 command line scripts.
231 231
232 232 Each of our entry points gets both a plain name, e.g. ipython, and one
233 233 suffixed with the Python major version number, e.g. ipython3.
234 234 """
235 235 ep = [
236 236 'ipython%s = IPython:start_ipython',
237 'iptest%s = IPython.testing.iptestcontroller:main',
238 237 ]
239 238 suffix = str(sys.version_info[0])
240 239 return [e % '' for e in ep] + [e % suffix for e in ep]
241 240
242 241 script_src = """#!{executable}
243 242 # This script was automatically generated by setup.py
244 243 if __name__ == '__main__':
245 244 from {mod} import {func}
246 245 {func}()
247 246 """
248 247
249 248 class build_scripts_entrypt(build_scripts):
250 249 """Build the command line scripts
251 250
252 251 Parse setuptools style entry points and write simple scripts to run the
253 252 target functions.
254 253
255 254 On Windows, this also creates .cmd wrappers for the scripts so that you can
256 255 easily launch them from a command line.
257 256 """
258 257 def run(self):
259 258 self.mkpath(self.build_dir)
260 259 outfiles = []
261 260 for script in find_entry_points():
262 261 name, entrypt = script.split('=')
263 262 name = name.strip()
264 263 entrypt = entrypt.strip()
265 264 outfile = os.path.join(self.build_dir, name)
266 265 outfiles.append(outfile)
267 266 print('Writing script to', outfile)
268 267
269 268 mod, func = entrypt.split(':')
270 269 with open(outfile, 'w') as f:
271 270 f.write(script_src.format(executable=sys.executable,
272 271 mod=mod, func=func))
273 272
274 273 if sys.platform == 'win32':
275 274 # Write .cmd wrappers for Windows so 'ipython' etc. work at the
276 275 # command line
277 276 cmd_file = os.path.join(self.build_dir, name + '.cmd')
278 277 cmd = r'@"{python}" "%~dp0\{script}" %*\r\n'.format(
279 278 python=sys.executable, script=name)
280 279 log.info("Writing %s wrapper script" % cmd_file)
281 280 with open(cmd_file, 'w') as f:
282 281 f.write(cmd)
283 282
284 283 return outfiles, outfiles
285 284
286 285 class install_lib_symlink(Command):
287 286 user_options = [
288 287 ('install-dir=', 'd', "directory to install to"),
289 288 ]
290 289
291 290 def initialize_options(self):
292 291 self.install_dir = None
293 292
294 293 def finalize_options(self):
295 294 self.set_undefined_options('symlink',
296 295 ('install_lib', 'install_dir'),
297 296 )
298 297
299 298 def run(self):
300 299 if sys.platform == 'win32':
301 300 raise Exception("This doesn't work on Windows.")
302 301 pkg = os.path.join(os.getcwd(), 'IPython')
303 302 dest = os.path.join(self.install_dir, 'IPython')
304 303 if os.path.islink(dest):
305 304 print('removing existing symlink at %s' % dest)
306 305 os.unlink(dest)
307 306 print('symlinking %s -> %s' % (pkg, dest))
308 307 os.symlink(pkg, dest)
309 308
310 309 class unsymlink(install):
311 310 def run(self):
312 311 dest = os.path.join(self.install_lib, 'IPython')
313 312 if os.path.islink(dest):
314 313 print('removing symlink at %s' % dest)
315 314 os.unlink(dest)
316 315 else:
317 316 print('No symlink exists at %s' % dest)
318 317
319 318 class install_symlinked(install):
320 319 def run(self):
321 320 if sys.platform == 'win32':
322 321 raise Exception("This doesn't work on Windows.")
323 322
324 323 # Run all sub-commands (at least those that need to be run)
325 324 for cmd_name in self.get_sub_commands():
326 325 self.run_command(cmd_name)
327 326
328 327 # 'sub_commands': a list of commands this command might have to run to
329 328 # get its work done. See cmd.py for more info.
330 329 sub_commands = [('install_lib_symlink', lambda self:True),
331 330 ('install_scripts_sym', lambda self:True),
332 331 ]
333 332
334 333 class install_scripts_for_symlink(install_scripts):
335 334 """Redefined to get options from 'symlink' instead of 'install'.
336 335
337 336 I love distutils almost as much as I love setuptools.
338 337 """
339 338 def finalize_options(self):
340 339 self.set_undefined_options('build', ('build_scripts', 'build_dir'))
341 340 self.set_undefined_options('symlink',
342 341 ('install_scripts', 'install_dir'),
343 342 ('force', 'force'),
344 343 ('skip_build', 'skip_build'),
345 344 )
346 345
347 346
348 347 #---------------------------------------------------------------------------
349 348 # VCS related
350 349 #---------------------------------------------------------------------------
351 350
352 351
353 352 def git_prebuild(pkg_dir, build_cmd=build_py):
354 353 """Return extended build or sdist command class for recording commit
355 354
356 355 records git commit in IPython.utils._sysinfo.commit
357 356
358 357 for use in IPython.utils.sysinfo.sys_info() calls after installation.
359 358 """
360 359
361 360 class MyBuildPy(build_cmd):
362 361 ''' Subclass to write commit data into installation tree '''
363 362 def run(self):
364 363 # loose as `.dev` is suppose to be invalid
365 364 print("check version number")
366 365 loose_pep440re = re.compile(r'^(\d+)\.(\d+)\.(\d+((a|b|rc)\d+)?)(\.post\d+)?(\.dev\d*)?$')
367 366 if not loose_pep440re.match(version):
368 367 raise ValueError("Version number '%s' is not valid (should match [N!]N(.N)*[{a|b|rc}N][.postN][.devN])" % version)
369 368
370 369
371 370 build_cmd.run(self)
372 371 # this one will only fire for build commands
373 372 if hasattr(self, 'build_lib'):
374 373 self._record_commit(self.build_lib)
375 374
376 375 def make_release_tree(self, base_dir, files):
377 376 # this one will fire for sdist
378 377 build_cmd.make_release_tree(self, base_dir, files)
379 378 self._record_commit(base_dir)
380 379
381 380 def _record_commit(self, base_dir):
382 381 import subprocess
383 382 proc = subprocess.Popen('git rev-parse --short HEAD',
384 383 stdout=subprocess.PIPE,
385 384 stderr=subprocess.PIPE,
386 385 shell=True)
387 386 repo_commit, _ = proc.communicate()
388 387 repo_commit = repo_commit.strip().decode("ascii")
389 388
390 389 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
391 390 if os.path.isfile(out_pth) and not repo_commit:
392 391 # nothing to write, don't clobber
393 392 return
394 393
395 394 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
396 395
397 396 # remove to avoid overwriting original via hard link
398 397 try:
399 398 os.remove(out_pth)
400 399 except (IOError, OSError):
401 400 pass
402 401 with open(out_pth, 'w') as out_file:
403 402 out_file.writelines([
404 403 '# GENERATED BY setup.py\n',
405 404 'commit = u"%s"\n' % repo_commit,
406 405 ])
407 406 return MyBuildPy
408 407
@@ -1,239 +1,239 b''
1 1 # Simple tool to help for release
2 2 # when releasing with bash, simple source it to get asked questions.
3 3
4 4 # misc check before starting
5 5
6 6 python -c 'import keyring'
7 7 python -c 'import twine'
8 8 python -c 'import sphinx'
9 9 python -c 'import sphinx_rtd_theme'
10 python -c 'import nose'
10 python -c 'import pytest'
11 11
12 12
13 13 BLACK=$(tput setaf 1)
14 14 RED=$(tput setaf 1)
15 15 GREEN=$(tput setaf 2)
16 16 YELLOW=$(tput setaf 3)
17 17 BLUE=$(tput setaf 4)
18 18 MAGENTA=$(tput setaf 5)
19 19 CYAN=$(tput setaf 6)
20 20 WHITE=$(tput setaf 7)
21 21 NOR=$(tput sgr0)
22 22
23 23
24 24 echo "Will use $EDITOR to edit files when necessary"
25 25 echo -n "PREV_RELEASE (X.y.z) [$PREV_RELEASE]: "
26 26 read input
27 27 PREV_RELEASE=${input:-$PREV_RELEASE}
28 28 echo -n "MILESTONE (X.y) [$MILESTONE]: "
29 29 read input
30 30 MILESTONE=${input:-$MILESTONE}
31 31 echo -n "VERSION (X.y.z) [$VERSION]:"
32 32 read input
33 33 VERSION=${input:-$VERSION}
34 34 echo -n "BRANCH (master|X.y) [$BRANCH]:"
35 35 read input
36 36 BRANCH=${input:-$BRANCH}
37 37
38 38 ask_section(){
39 39 echo
40 40 echo $BLUE"$1"$NOR
41 41 echo -n $GREEN"Press Enter to continue, S to skip: "$NOR
42 42 read -n1 value
43 43 echo
44 44 if [ -z $value ] || [ $value = 'y' ] ; then
45 45 return 0
46 46 fi
47 47 return 1
48 48 }
49 49
50 50
51 51 maybe_edit(){
52 52 echo
53 53 echo $BLUE"$1"$NOR
54 54 echo -n $GREEN"Press e to Edit $1, any other keys to skip: "$NOR
55 55 read -n1 value
56 56 echo
57 57 if [ $value = 'e' ] ; then
58 58 $EDITOR $1
59 59 fi
60 60 }
61 61
62 62
63 63
64 64 echo
65 65 if ask_section "Updating what's new with information from docs/source/whatsnew/pr"
66 66 then
67 67 python tools/update_whatsnew.py
68 68
69 69 echo
70 70 echo $BLUE"please move the contents of "docs/source/whatsnew/development.rst" to version-X.rst"$NOR
71 71 echo $GREEN"Press enter to continue"$NOR
72 72 read
73 73 fi
74 74
75 75 if ask_section "Gen Stats, and authors"
76 76 then
77 77
78 78 echo
79 79 echo $BLUE"here are all the authors that contributed to this release:"$NOR
80 80 git log --format="%aN <%aE>" $PREV_RELEASE... | sort -u -f
81 81
82 82 echo
83 83 echo $BLUE"If you see any duplicates cancel (Ctrl-C), then edit .mailmap."
84 84 echo $GREEN"Press enter to continue:"$NOR
85 85 read
86 86
87 87 echo $BLUE"generating stats"$NOR
88 88 python tools/github_stats.py --milestone $MILESTONE > stats.rst
89 89
90 90 echo $BLUE"stats.rst files generated."$NOR
91 91 echo $GREEN"Please merge it with the right file (github-stats-X.rst) and commit."$NOR
92 92 echo $GREEN"press enter to continue."$NOR
93 93 read
94 94
95 95 fi
96 96
97 97 if ask_section "Generate API difference (using frapuccino)"
98 98 then
99 99 echo $BLUE"Checking out $PREV_RELEASE"$NOR
100 100 git checkout $PREV_RELEASE
101 101 echo $BLUE"Saving API to file $PREV_RELEASE"$NOR
102 102 frappuccino IPython --save IPython-$PREV_RELEASE.json
103 103 echo $BLUE"coming back to $BRANCH"$NOR
104 104 git checkout $BRANCH
105 105 echo $BLUE"comparing ..."$NOR
106 106 frappuccino IPython --compare IPython-$PREV_RELEASE.json
107 107 echo $GREEN"Use the above guideline to write an API changelog ..."$NOR
108 108 echo $GREEN"Press any keys to continue"$NOR
109 109 read
110 110 fi
111 111
112 112 echo "Cleaning repository"
113 113 git clean -xfdi
114 114
115 115 echo $GREEN"please update version number in ${RED}IPython/core/release.py${NOR} , Do not commit yet – we'll do it later."$NOR
116 116 echo $GREEN"I tried ${RED}sed -i bkp -e '/Uncomment/s/^# //g' IPython/core/release.py${NOR}"
117 117 sed -i bkp -e '/Uncomment/s/^# //g' IPython/core/release.py
118 118 rm IPython/core/release.pybkp
119 119 git diff
120 120 maybe_edit IPython/core/release.py
121 121
122 122 echo $GREEN"Press enter to continue"$NOR
123 123 read
124 124
125 125 if ask_section "Build the documentation ?"
126 126 then
127 127 make html -C docs
128 128 echo
129 129 echo $GREEN"Check the docs, press enter to continue"$NOR
130 130 read
131 131
132 132 fi
133 133
134 134 if ask_section "Should we commit, tag, push... etc ? "
135 135 then
136 136 echo
137 137 echo $BLUE"Let's commit : git commit -am \"release $VERSION\" -S"
138 138 echo $GREEN"Press enter to commit"$NOR
139 139 read
140 140 git commit -am "release $VERSION" -S
141 141
142 142 echo
143 143 echo $BLUE"git push origin \$BRANCH ($BRANCH)?"$NOR
144 144 echo $GREEN"Make sure you can push"$NOR
145 145 echo $GREEN"Press enter to continue"$NOR
146 146 read
147 147 git push origin $BRANCH
148 148
149 149 echo
150 150 echo "Let's tag : git tag -am \"release $VERSION\" \"$VERSION\" -s"
151 151 echo $GREEN"Press enter to tag commit"$NOR
152 152 read
153 153 git tag -am "release $VERSION" "$VERSION" -s
154 154
155 155 echo
156 156 echo $BLUE"And push the tag: git push origin \$VERSION ?"$NOR
157 157 echo $GREEN"Press enter to continue"$NOR
158 158 read
159 159 git push origin $VERSION
160 160
161 161
162 162 echo $GREEN"please update version number and back to .dev in ${RED}IPython/core/release.py"
163 163 echo $GREEN"I tried ${RED}sed -i bkp -e '/Uncomment/s/^/# /g' IPython/core/release.py${NOR}"
164 164 sed -i bkp -e '/Uncomment/s/^/# /g' IPython/core/release.py
165 165 rm IPython/core/release.pybkp
166 166 git diff
167 167 echo $GREEN"Please bump ${RED}the minor version number${NOR}"
168 168 maybe_edit IPython/core/release.py
169 169 echo ${BLUE}"Do not commit yet – we'll do it later."$NOR
170 170
171 171
172 172 echo $GREEN"Press enter to continue"$NOR
173 173 read
174 174
175 175 echo
176 176 echo "Let's commit : "$BLUE"git commit -am \"back to dev\""$NOR
177 177 echo $GREEN"Press enter to commit"$NOR
178 178 read
179 179 git commit -am "back to dev"
180 180
181 181 echo
182 182 echo $BLUE"git push origin \$BRANCH ($BRANCH)?"$NOR
183 183 echo $GREEN"Press enter to continue"$NOR
184 184 read
185 185 git push origin $BRANCH
186 186
187 187
188 188 echo
189 189 echo $BLUE"let's : git checkout $VERSION"$NOR
190 190 echo $GREEN"Press enter to continue"$NOR
191 191 read
192 192 git checkout $VERSION
193 193 fi
194 194
195 195 if ask_section "Should we build and release ?"
196 196 then
197 197
198 198 echo $BLUE"going to set SOURCE_DATE_EPOCH"$NOR
199 199 echo $BLUE'export SOURCE_DATE_EPOCH=$(git show -s --format=%ct HEAD)'$NOR
200 200 echo $GREEN"Press enter to continue"$NOR
201 201 read
202 202
203 203 export SOURCE_DATE_EPOCH=$(git show -s --format=%ct HEAD)
204 204
205 205 echo $BLUE"SOURCE_DATE_EPOCH set to $SOURCE_DATE_EPOCH"$NOR
206 206 echo $GREEN"Press enter to continue"$NOR
207 207 read
208 208
209 209
210 210
211 211 echo
212 212 echo $BLUE"Attempting to build package..."$NOR
213 213
214 214 tools/release
215 215
216 216
217 217 echo $RED'$ shasum -a 256 dist/*'
218 218 shasum -a 256 dist/*
219 219 echo $NOR
220 220
221 221 echo $BLUE"We are going to rebuild, node the hash above, and compare them to the rebuild"$NOR
222 222 echo $GREEN"Press enter to continue"$NOR
223 223 read
224 224
225 225 echo
226 226 echo $BLUE"Attempting to build package..."$NOR
227 227
228 228 tools/release
229 229
230 230 echo $RED"Check the shasum for SOURCE_DATE_EPOCH=$SOURCE_DATE_EPOCH"
231 231 echo $RED'$ shasum -a 256 dist/*'
232 232 shasum -a 256 dist/*
233 233 echo $NOR
234 234
235 235 if ask_section "upload packages ?"
236 236 then
237 237 tools/release upload
238 238 fi
239 239 fi
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now