Show More
@@ -0,0 +1,7 b'' | |||||
|
1 | Added shortcut for accepting auto suggestion | |||
|
2 | ============================================ | |||
|
3 | ||||
|
4 | Added End key shortcut for accepting auto-suggestion | |||
|
5 | This binding works in Vi mode too, provided | |||
|
6 | TerminalInteractiveShell.emacs_bindings_in_vi_insert_mode is set to be True. | |||
|
7 |
@@ -39,7 +39,7 b' jobs:' | |||||
39 | - name: Install and update Python dependencies |
|
39 | - name: Install and update Python dependencies | |
40 | run: | |
|
40 | run: | | |
41 | python -m pip install --upgrade -e file://$PWD#egg=ipython[test] |
|
41 | python -m pip install --upgrade -e file://$PWD#egg=ipython[test] | |
42 | # we must instal IPython after ipykernel to get the right versions. |
|
42 | # we must install IPython after ipykernel to get the right versions. | |
43 | python -m pip install --upgrade --upgrade-strategy eager flaky ipyparallel |
|
43 | python -m pip install --upgrade --upgrade-strategy eager flaky ipyparallel | |
44 | python -m pip install --upgrade 'pytest<7' |
|
44 | python -m pip install --upgrade 'pytest<7' | |
45 | - name: pytest |
|
45 | - name: pytest |
@@ -17,6 +17,7 b' jobs:' | |||||
17 | test: |
|
17 | test: | |
18 | runs-on: ${{ matrix.os }} |
|
18 | runs-on: ${{ matrix.os }} | |
19 | strategy: |
|
19 | strategy: | |
|
20 | fail-fast: false | |||
20 | matrix: |
|
21 | matrix: | |
21 | os: [ubuntu-latest, windows-latest] |
|
22 | os: [ubuntu-latest, windows-latest] | |
22 | python-version: ["3.8", "3.9", "3.10"] |
|
23 | python-version: ["3.8", "3.9", "3.10"] |
@@ -28,5 +28,13 b' __pycache__' | |||||
28 | .pytest_cache |
|
28 | .pytest_cache | |
29 | .python-version |
|
29 | .python-version | |
30 | venv*/ |
|
30 | venv*/ | |
31 | .idea/ |
|
|||
32 | .mypy_cache/ |
|
31 | .mypy_cache/ | |
|
32 | ||||
|
33 | # jetbrains ide stuff | |||
|
34 | *.iml | |||
|
35 | .idea/ | |||
|
36 | ||||
|
37 | # vscode ide stuff | |||
|
38 | *.code-workspace | |||
|
39 | .history | |||
|
40 | .vscode |
@@ -28,7 +28,7 b' import sys' | |||||
28 | # Don't forget to also update setup.py when this changes! |
|
28 | # Don't forget to also update setup.py when this changes! | |
29 | if sys.version_info < (3, 8): |
|
29 | if sys.version_info < (3, 8): | |
30 | raise ImportError( |
|
30 | raise ImportError( | |
31 | """ |
|
31 | """ | |
32 | IPython 8+ supports Python 3.8 and above, following NEP 29. |
|
32 | IPython 8+ supports Python 3.8 and above, following NEP 29. | |
33 | When using Python 2.7, please install IPython 5.x LTS Long Term Support version. |
|
33 | When using Python 2.7, please install IPython 5.x LTS Long Term Support version. | |
34 | Python 3.3 and 3.4 were supported up to IPython 6.x. |
|
34 | Python 3.3 and 3.4 were supported up to IPython 6.x. | |
@@ -40,7 +40,8 b' See IPython `README.rst` file for more information:' | |||||
40 |
|
40 | |||
41 | https://github.com/ipython/ipython/blob/master/README.rst |
|
41 | https://github.com/ipython/ipython/blob/master/README.rst | |
42 |
|
42 | |||
43 |
""" |
|
43 | """ | |
|
44 | ) | |||
44 |
|
45 | |||
45 | #----------------------------------------------------------------------------- |
|
46 | #----------------------------------------------------------------------------- | |
46 | # Setup the top level names |
|
47 | # Setup the top level names |
@@ -40,7 +40,7 b' class IPyAutocall(object):' | |||||
40 | self._ip = ip |
|
40 | self._ip = ip | |
41 |
|
41 | |||
42 | def set_ip(self, ip): |
|
42 | def set_ip(self, ip): | |
43 |
""" |
|
43 | """Will be used to set _ip point to current ipython instance b/f call | |
44 |
|
44 | |||
45 | Override this method if you don't want this to happen. |
|
45 | Override this method if you don't want this to happen. | |
46 |
|
46 |
@@ -41,7 +41,7 b' or using unicode completion:' | |||||
41 |
|
41 | |||
42 | Only valid Python identifiers will complete. Combining characters (like arrow or |
|
42 | Only valid Python identifiers will complete. Combining characters (like arrow or | |
43 | dots) are also available, unlike latex they need to be put after the their |
|
43 | dots) are also available, unlike latex they need to be put after the their | |
44 | counterpart that is to say, `F\\\\vec<tab>` is correct, not `\\\\vec<tab>F`. |
|
44 | counterpart that is to say, ``F\\\\vec<tab>`` is correct, not ``\\\\vec<tab>F``. | |
45 |
|
45 | |||
46 | Some browsers are known to display combining characters incorrectly. |
|
46 | Some browsers are known to display combining characters incorrectly. | |
47 |
|
47 | |||
@@ -50,7 +50,7 b' Backward latex completion' | |||||
50 |
|
50 | |||
51 | It is sometime challenging to know how to type a character, if you are using |
|
51 | It is sometime challenging to know how to type a character, if you are using | |
52 | IPython, or any compatible frontend you can prepend backslash to the character |
|
52 | IPython, or any compatible frontend you can prepend backslash to the character | |
53 | and press `<tab>` to expand it to its latex form. |
|
53 | and press ``<tab>`` to expand it to its latex form. | |
54 |
|
54 | |||
55 | .. code:: |
|
55 | .. code:: | |
56 |
|
56 | |||
@@ -589,7 +589,7 b' class Completer(Configurable):' | |||||
589 |
|
589 | |||
590 | This will enable completion on elements of lists, results of function calls, etc., |
|
590 | This will enable completion on elements of lists, results of function calls, etc., | |
591 | but can be unsafe because the code is actually evaluated on TAB. |
|
591 | but can be unsafe because the code is actually evaluated on TAB. | |
592 | """ |
|
592 | """, | |
593 | ).tag(config=True) |
|
593 | ).tag(config=True) | |
594 |
|
594 | |||
595 | use_jedi = Bool(default_value=JEDI_INSTALLED, |
|
595 | use_jedi = Bool(default_value=JEDI_INSTALLED, | |
@@ -1182,7 +1182,7 b' class IPCompleter(Completer):' | |||||
1182 |
|
1182 | |||
1183 | # This is a list of names of unicode characters that can be completed |
|
1183 | # This is a list of names of unicode characters that can be completed | |
1184 | # into their corresponding unicode value. The list is large, so we |
|
1184 | # into their corresponding unicode value. The list is large, so we | |
1185 |
# lazil |
|
1185 | # lazily initialize it on first use. Consuming code should access this | |
1186 | # attribute through the `@unicode_names` property. |
|
1186 | # attribute through the `@unicode_names` property. | |
1187 | self._unicode_names = None |
|
1187 | self._unicode_names = None | |
1188 |
|
1188 | |||
@@ -2070,7 +2070,7 b' class IPCompleter(Completer):' | |||||
2070 | indexed. |
|
2070 | indexed. | |
2071 | line_buffer : optional, str |
|
2071 | line_buffer : optional, str | |
2072 | The current line the cursor is in, this is mostly due to legacy |
|
2072 | The current line the cursor is in, this is mostly due to legacy | |
2073 |
reason that readline cou |
|
2073 | reason that readline could only give a us the single current line. | |
2074 | Prefer `full_text`. |
|
2074 | Prefer `full_text`. | |
2075 | text : str |
|
2075 | text : str | |
2076 | The current "token" the cursor is in, mostly also for historical |
|
2076 | The current "token" the cursor is in, mostly also for historical | |
@@ -2139,8 +2139,9 b' class IPCompleter(Completer):' | |||||
2139 | # different types of objects. The rlcomplete() method could then |
|
2139 | # different types of objects. The rlcomplete() method could then | |
2140 | # simply collapse the dict into a list for readline, but we'd have |
|
2140 | # simply collapse the dict into a list for readline, but we'd have | |
2141 | # richer completion semantics in other environments. |
|
2141 | # richer completion semantics in other environments. | |
2142 | completions:Iterable[Any] = [] |
|
2142 | is_magic_prefix = len(text) > 0 and text[0] == "%" | |
2143 | if self.use_jedi: |
|
2143 | completions: Iterable[Any] = [] | |
|
2144 | if self.use_jedi and not is_magic_prefix: | |||
2144 | if not full_text: |
|
2145 | if not full_text: | |
2145 | full_text = line_buffer |
|
2146 | full_text = line_buffer | |
2146 | completions = self._jedi_matches( |
|
2147 | completions = self._jedi_matches( |
@@ -101,7 +101,6 b' All the changes since then are under the same license as IPython.' | |||||
101 | # |
|
101 | # | |
102 | #***************************************************************************** |
|
102 | #***************************************************************************** | |
103 |
|
103 | |||
104 | import bdb |
|
|||
105 | import inspect |
|
104 | import inspect | |
106 | import linecache |
|
105 | import linecache | |
107 | import sys |
|
106 | import sys |
@@ -884,7 +884,7 b' class Image(DisplayObject):' | |||||
884 | a URL, or a filename from which to load image data. |
|
884 | a URL, or a filename from which to load image data. | |
885 | The result is always embedding image data for inline images. |
|
885 | The result is always embedding image data for inline images. | |
886 |
|
886 | |||
887 | >>> Image('http://www.google.fr/images/srpr/logo3w.png') |
|
887 | >>> Image('https://www.google.fr/images/srpr/logo3w.png') # doctest: +SKIP | |
888 | <IPython.core.display.Image object> |
|
888 | <IPython.core.display.Image object> | |
889 |
|
889 | |||
890 | >>> Image('/path/to/image.jpg') |
|
890 | >>> Image('/path/to/image.jpg') | |
@@ -897,7 +897,7 b' class Image(DisplayObject):' | |||||
897 | it only generates ``<img>`` tag with a link to the source. |
|
897 | it only generates ``<img>`` tag with a link to the source. | |
898 | This will not work in the qtconsole or offline. |
|
898 | This will not work in the qtconsole or offline. | |
899 |
|
899 | |||
900 | >>> Image(url='http://www.google.fr/images/srpr/logo3w.png') |
|
900 | >>> Image(url='https://www.google.fr/images/srpr/logo3w.png') | |
901 | <IPython.core.display.Image object> |
|
901 | <IPython.core.display.Image object> | |
902 |
|
902 | |||
903 | """ |
|
903 | """ |
@@ -166,7 +166,7 b' class HistoryAccessor(HistoryAccessorBase):' | |||||
166 | in which case there will be no stored history, no SQLite connection, |
|
166 | in which case there will be no stored history, no SQLite connection, | |
167 | and no background saving thread. This may be necessary in some |
|
167 | and no background saving thread. This may be necessary in some | |
168 | threaded environments where IPython is embedded. |
|
168 | threaded environments where IPython is embedded. | |
169 | """ |
|
169 | """, | |
170 | ).tag(config=True) |
|
170 | ).tag(config=True) | |
171 |
|
171 | |||
172 | connection_options = Dict( |
|
172 | connection_options = Dict( | |
@@ -296,8 +296,8 b' class HistoryAccessor(HistoryAccessorBase):' | |||||
296 | toget = "history.%s, output_history.output" % toget |
|
296 | toget = "history.%s, output_history.output" % toget | |
297 | if latest: |
|
297 | if latest: | |
298 | toget += ", MAX(session * 128 * 1024 + line)" |
|
298 | toget += ", MAX(session * 128 * 1024 + line)" | |
299 |
|
|
299 | this_querry = "SELECT session, line, %s FROM %s " % (toget, sqlfrom) + sql | |
300 | (toget, sqlfrom) + sql, params) |
|
300 | cur = self.db.execute(this_querry, params) | |
301 | if latest: |
|
301 | if latest: | |
302 | cur = (row[:-1] for row in cur) |
|
302 | cur = (row[:-1] for row in cur) | |
303 | if output: # Regroup into 3-tuples, and parse JSON |
|
303 | if output: # Regroup into 3-tuples, and parse JSON | |
@@ -344,6 +344,11 b' class HistoryAccessor(HistoryAccessorBase):' | |||||
344 | def get_tail(self, n=10, raw=True, output=False, include_latest=False): |
|
344 | def get_tail(self, n=10, raw=True, output=False, include_latest=False): | |
345 | """Get the last n lines from the history database. |
|
345 | """Get the last n lines from the history database. | |
346 |
|
346 | |||
|
347 | Most recent entry last. | |||
|
348 | ||||
|
349 | Completion will be reordered so that that the last ones are when | |||
|
350 | possible from current session. | |||
|
351 | ||||
347 | Parameters |
|
352 | Parameters | |
348 | ---------- |
|
353 | ---------- | |
349 | n : int |
|
354 | n : int | |
@@ -362,11 +367,31 b' class HistoryAccessor(HistoryAccessorBase):' | |||||
362 | self.writeout_cache() |
|
367 | self.writeout_cache() | |
363 | if not include_latest: |
|
368 | if not include_latest: | |
364 | n += 1 |
|
369 | n += 1 | |
365 | cur = self._run_sql("ORDER BY session DESC, line DESC LIMIT ?", |
|
370 | # cursor/line/entry | |
366 | (n,), raw=raw, output=output) |
|
371 | this_cur = list( | |
|
372 | self._run_sql( | |||
|
373 | "WHERE session == ? ORDER BY line DESC LIMIT ? ", | |||
|
374 | (self.session_number, n), | |||
|
375 | raw=raw, | |||
|
376 | output=output, | |||
|
377 | ) | |||
|
378 | ) | |||
|
379 | other_cur = list( | |||
|
380 | self._run_sql( | |||
|
381 | "WHERE session != ? ORDER BY session DESC, line DESC LIMIT ?", | |||
|
382 | (self.session_number, n), | |||
|
383 | raw=raw, | |||
|
384 | output=output, | |||
|
385 | ) | |||
|
386 | ) | |||
|
387 | ||||
|
388 | everything = this_cur + other_cur | |||
|
389 | ||||
|
390 | everything = everything[:n] | |||
|
391 | ||||
367 | if not include_latest: |
|
392 | if not include_latest: | |
368 |
return |
|
393 | return list(everything)[:0:-1] | |
369 |
return |
|
394 | return list(everything)[::-1] | |
370 |
|
395 | |||
371 | @catch_corrupt_db |
|
396 | @catch_corrupt_db | |
372 | def search(self, pattern="*", raw=True, search_raw=True, |
|
397 | def search(self, pattern="*", raw=True, search_raw=True, |
@@ -193,7 +193,7 b' def assemble_logical_lines():' | |||||
193 | line = ''.join(parts) |
|
193 | line = ''.join(parts) | |
194 |
|
194 | |||
195 | # Utilities |
|
195 | # Utilities | |
196 |
def _make_help_call(target, esc, lspace |
|
196 | def _make_help_call(target, esc, lspace): | |
197 | """Prepares a pinfo(2)/psearch call from a target name and the escape |
|
197 | """Prepares a pinfo(2)/psearch call from a target name and the escape | |
198 | (i.e. ? or ??)""" |
|
198 | (i.e. ? or ??)""" | |
199 | method = 'pinfo2' if esc == '??' \ |
|
199 | method = 'pinfo2' if esc == '??' \ | |
@@ -203,12 +203,13 b' def _make_help_call(target, esc, lspace, next_input=None):' | |||||
203 | #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args) |
|
203 | #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args) | |
204 | t_magic_name, _, t_magic_arg_s = arg.partition(' ') |
|
204 | t_magic_name, _, t_magic_arg_s = arg.partition(' ') | |
205 | t_magic_name = t_magic_name.lstrip(ESC_MAGIC) |
|
205 | t_magic_name = t_magic_name.lstrip(ESC_MAGIC) | |
206 | if next_input is None: |
|
206 | return "%sget_ipython().run_line_magic(%r, %r)" % ( | |
207 | return '%sget_ipython().run_line_magic(%r, %r)' % (lspace, t_magic_name, t_magic_arg_s) |
|
207 | lspace, | |
208 | else: |
|
208 | t_magic_name, | |
209 | return '%sget_ipython().set_next_input(%r);get_ipython().run_line_magic(%r, %r)' % \ |
|
209 | t_magic_arg_s, | |
210 | (lspace, next_input, t_magic_name, t_magic_arg_s) |
|
210 | ) | |
211 |
|
211 | |||
|
212 | ||||
212 | # These define the transformations for the different escape characters. |
|
213 | # These define the transformations for the different escape characters. | |
213 | def _tr_system(line_info): |
|
214 | def _tr_system(line_info): | |
214 | "Translate lines escaped with: !" |
|
215 | "Translate lines escaped with: !" | |
@@ -349,10 +350,7 b' def help_end(line):' | |||||
349 | esc = m.group(3) |
|
350 | esc = m.group(3) | |
350 | lspace = _initial_space_re.match(line).group(0) |
|
351 | lspace = _initial_space_re.match(line).group(0) | |
351 |
|
352 | |||
352 | # If we're mid-command, put it back on the next prompt for the user. |
|
353 | return _make_help_call(target, esc, lspace) | |
353 | next_input = line.rstrip('?') if line.strip() != m.group(0) else None |
|
|||
354 |
|
||||
355 | return _make_help_call(target, esc, lspace, next_input) |
|
|||
356 |
|
354 | |||
357 |
|
355 | |||
358 | @CoroutineInputTransformer.wrap |
|
356 | @CoroutineInputTransformer.wrap |
@@ -325,7 +325,7 b" ESC_PAREN = '/' # Call first argument with rest of line as arguments" | |||||
325 | ESCAPE_SINGLES = {'!', '?', '%', ',', ';', '/'} |
|
325 | ESCAPE_SINGLES = {'!', '?', '%', ',', ';', '/'} | |
326 | ESCAPE_DOUBLES = {'!!', '??'} # %% (cell magic) is handled separately |
|
326 | ESCAPE_DOUBLES = {'!!', '??'} # %% (cell magic) is handled separately | |
327 |
|
327 | |||
328 |
def _make_help_call(target, esc |
|
328 | def _make_help_call(target, esc): | |
329 | """Prepares a pinfo(2)/psearch call from a target name and the escape |
|
329 | """Prepares a pinfo(2)/psearch call from a target name and the escape | |
330 | (i.e. ? or ??)""" |
|
330 | (i.e. ? or ??)""" | |
331 | method = 'pinfo2' if esc == '??' \ |
|
331 | method = 'pinfo2' if esc == '??' \ | |
@@ -335,11 +335,8 b' def _make_help_call(target, esc, next_input=None):' | |||||
335 | #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args) |
|
335 | #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args) | |
336 | t_magic_name, _, t_magic_arg_s = arg.partition(' ') |
|
336 | t_magic_name, _, t_magic_arg_s = arg.partition(' ') | |
337 | t_magic_name = t_magic_name.lstrip(ESC_MAGIC) |
|
337 | t_magic_name = t_magic_name.lstrip(ESC_MAGIC) | |
338 | if next_input is None: |
|
338 | return "get_ipython().run_line_magic(%r, %r)" % (t_magic_name, t_magic_arg_s) | |
339 | return 'get_ipython().run_line_magic(%r, %r)' % (t_magic_name, t_magic_arg_s) |
|
339 | ||
340 | else: |
|
|||
341 | return 'get_ipython().set_next_input(%r);get_ipython().run_line_magic(%r, %r)' % \ |
|
|||
342 | (next_input, t_magic_name, t_magic_arg_s) |
|
|||
343 |
|
340 | |||
344 | def _tr_help(content): |
|
341 | def _tr_help(content): | |
345 | """Translate lines escaped with: ? |
|
342 | """Translate lines escaped with: ? | |
@@ -480,13 +477,8 b' class HelpEnd(TokenTransformBase):' | |||||
480 | target = m.group(1) |
|
477 | target = m.group(1) | |
481 | esc = m.group(3) |
|
478 | esc = m.group(3) | |
482 |
|
479 | |||
483 | # If we're mid-command, put it back on the next prompt for the user. |
|
|||
484 | next_input = None |
|
|||
485 | if (not lines_before) and (not lines_after) \ |
|
|||
486 | and content.strip() != m.group(0): |
|
|||
487 | next_input = content.rstrip('?\n') |
|
|||
488 |
|
480 | |||
489 |
call = _make_help_call(target, esc |
|
481 | call = _make_help_call(target, esc) | |
490 | new_line = indent + call + '\n' |
|
482 | new_line = indent + call + '\n' | |
491 |
|
483 | |||
492 | return lines_before + [new_line] + lines_after |
|
484 | return lines_before + [new_line] + lines_after |
@@ -14,40 +14,61 b'' | |||||
14 | import abc |
|
14 | import abc | |
15 | import ast |
|
15 | import ast | |
16 | import atexit |
|
16 | import atexit | |
|
17 | import bdb | |||
17 | import builtins as builtin_mod |
|
18 | import builtins as builtin_mod | |
|
19 | import dis | |||
18 | import functools |
|
20 | import functools | |
19 | import inspect |
|
21 | import inspect | |
20 | import os |
|
22 | import os | |
21 | import re |
|
23 | import re | |
22 | import runpy |
|
24 | import runpy | |
|
25 | import subprocess | |||
23 | import sys |
|
26 | import sys | |
24 | import tempfile |
|
27 | import tempfile | |
25 | import traceback |
|
28 | import traceback | |
26 | import types |
|
29 | import types | |
27 | import subprocess |
|
|||
28 | import warnings |
|
30 | import warnings | |
|
31 | from ast import stmt | |||
29 | from io import open as io_open |
|
32 | from io import open as io_open | |
30 |
|
33 | from logging import error | ||
31 | from pathlib import Path |
|
34 | from pathlib import Path | |
32 | from pickleshare import PickleShareDB |
|
35 | from typing import Callable | |
|
36 | from typing import List as ListType | |||
|
37 | from typing import Optional, Tuple | |||
|
38 | from warnings import warn | |||
33 |
|
39 | |||
|
40 | from pickleshare import PickleShareDB | |||
|
41 | from tempfile import TemporaryDirectory | |||
|
42 | from traitlets import ( | |||
|
43 | Any, | |||
|
44 | Bool, | |||
|
45 | CaselessStrEnum, | |||
|
46 | Dict, | |||
|
47 | Enum, | |||
|
48 | Instance, | |||
|
49 | Integer, | |||
|
50 | List, | |||
|
51 | Type, | |||
|
52 | Unicode, | |||
|
53 | default, | |||
|
54 | observe, | |||
|
55 | validate, | |||
|
56 | ) | |||
34 | from traitlets.config.configurable import SingletonConfigurable |
|
57 | from traitlets.config.configurable import SingletonConfigurable | |
35 | from traitlets.utils.importstring import import_item |
|
58 | from traitlets.utils.importstring import import_item | |
36 | from IPython.core import oinspect |
|
59 | ||
37 | from IPython.core import magic |
|
60 | import IPython.core.hooks | |
38 | from IPython.core import page |
|
61 | from IPython.core import magic, oinspect, page, prefilter, ultratb | |
39 | from IPython.core import prefilter |
|
|||
40 | from IPython.core import ultratb |
|
|||
41 | from IPython.core.alias import Alias, AliasManager |
|
62 | from IPython.core.alias import Alias, AliasManager | |
42 | from IPython.core.autocall import ExitAutocall |
|
63 | from IPython.core.autocall import ExitAutocall | |
43 | from IPython.core.builtin_trap import BuiltinTrap |
|
64 | from IPython.core.builtin_trap import BuiltinTrap | |
44 | from IPython.core.events import EventManager, available_events |
|
|||
45 | from IPython.core.compilerop import CachingCompiler, check_linecache_ipython |
|
65 | from IPython.core.compilerop import CachingCompiler, check_linecache_ipython | |
46 | from IPython.core.debugger import InterruptiblePdb |
|
66 | from IPython.core.debugger import InterruptiblePdb | |
47 | from IPython.core.display_trap import DisplayTrap |
|
67 | from IPython.core.display_trap import DisplayTrap | |
48 | from IPython.core.displayhook import DisplayHook |
|
68 | from IPython.core.displayhook import DisplayHook | |
49 | from IPython.core.displaypub import DisplayPublisher |
|
69 | from IPython.core.displaypub import DisplayPublisher | |
50 | from IPython.core.error import InputRejected, UsageError |
|
70 | from IPython.core.error import InputRejected, UsageError | |
|
71 | from IPython.core.events import EventManager, available_events | |||
51 | from IPython.core.extensions import ExtensionManager |
|
72 | from IPython.core.extensions import ExtensionManager | |
52 | from IPython.core.formatters import DisplayFormatter |
|
73 | from IPython.core.formatters import DisplayFormatter | |
53 | from IPython.core.history import HistoryManager |
|
74 | from IPython.core.history import HistoryManager | |
@@ -59,31 +80,17 b' from IPython.core.prefilter import PrefilterManager' | |||||
59 | from IPython.core.profiledir import ProfileDir |
|
80 | from IPython.core.profiledir import ProfileDir | |
60 | from IPython.core.usage import default_banner |
|
81 | from IPython.core.usage import default_banner | |
61 | from IPython.display import display |
|
82 | from IPython.display import display | |
|
83 | from IPython.paths import get_ipython_dir | |||
62 | from IPython.testing.skipdoctest import skip_doctest |
|
84 | from IPython.testing.skipdoctest import skip_doctest | |
63 | from IPython.utils import PyColorize |
|
85 | from IPython.utils import PyColorize, io, openpy, py3compat | |
64 | from IPython.utils import io |
|
|||
65 | from IPython.utils import py3compat |
|
|||
66 | from IPython.utils import openpy |
|
|||
67 | from IPython.utils.decorators import undoc |
|
86 | from IPython.utils.decorators import undoc | |
68 | from IPython.utils.io import ask_yes_no |
|
87 | from IPython.utils.io import ask_yes_no | |
69 | from IPython.utils.ipstruct import Struct |
|
88 | from IPython.utils.ipstruct import Struct | |
70 | from IPython.paths import get_ipython_dir |
|
89 | from IPython.utils.path import ensure_dir_exists, get_home_dir, get_py_filename | |
71 | from IPython.utils.path import get_home_dir, get_py_filename, ensure_dir_exists |
|
90 | from IPython.utils.process import getoutput, system | |
72 | from IPython.utils.process import system, getoutput |
|
|||
73 | from IPython.utils.strdispatch import StrDispatch |
|
91 | from IPython.utils.strdispatch import StrDispatch | |
74 | from IPython.utils.syspathcontext import prepended_to_syspath |
|
92 | from IPython.utils.syspathcontext import prepended_to_syspath | |
75 |
from IPython.utils.text import |
|
93 | from IPython.utils.text import DollarFormatter, LSString, SList, format_screen | |
76 | from IPython.utils.tempdir import TemporaryDirectory |
|
|||
77 | from traitlets import ( |
|
|||
78 | Integer, Bool, CaselessStrEnum, Enum, List, Dict, Unicode, Instance, Type, |
|
|||
79 | observe, default, validate, Any |
|
|||
80 | ) |
|
|||
81 | from warnings import warn |
|
|||
82 | from logging import error |
|
|||
83 | import IPython.core.hooks |
|
|||
84 |
|
||||
85 | from typing import List as ListType, Tuple, Optional, Callable |
|
|||
86 | from ast import stmt |
|
|||
87 |
|
94 | |||
88 | sphinxify: Optional[Callable] |
|
95 | sphinxify: Optional[Callable] | |
89 |
|
96 | |||
@@ -122,8 +129,13 b' _single_targets_nodes = (ast.AugAssign, ast.AnnAssign)' | |||||
122 |
|
129 | |||
123 | # we still need to run things using the asyncio eventloop, but there is no |
|
130 | # we still need to run things using the asyncio eventloop, but there is no | |
124 | # async integration |
|
131 | # async integration | |
125 | from .async_helpers import _asyncio_runner, _pseudo_sync_runner |
|
132 | from .async_helpers import ( | |
126 | from .async_helpers import _curio_runner, _trio_runner, _should_be_async |
|
133 | _asyncio_runner, | |
|
134 | _curio_runner, | |||
|
135 | _pseudo_sync_runner, | |||
|
136 | _should_be_async, | |||
|
137 | _trio_runner, | |||
|
138 | ) | |||
127 |
|
139 | |||
128 | #----------------------------------------------------------------------------- |
|
140 | #----------------------------------------------------------------------------- | |
129 | # Globals |
|
141 | # Globals | |
@@ -188,19 +200,29 b' class ExecutionInfo(object):' | |||||
188 | store_history = False |
|
200 | store_history = False | |
189 | silent = False |
|
201 | silent = False | |
190 | shell_futures = True |
|
202 | shell_futures = True | |
|
203 | cell_id = None | |||
191 |
|
204 | |||
192 | def __init__(self, raw_cell, store_history, silent, shell_futures): |
|
205 | def __init__(self, raw_cell, store_history, silent, shell_futures, cell_id): | |
193 | self.raw_cell = raw_cell |
|
206 | self.raw_cell = raw_cell | |
194 | self.store_history = store_history |
|
207 | self.store_history = store_history | |
195 | self.silent = silent |
|
208 | self.silent = silent | |
196 | self.shell_futures = shell_futures |
|
209 | self.shell_futures = shell_futures | |
|
210 | self.cell_id = cell_id | |||
197 |
|
211 | |||
198 | def __repr__(self): |
|
212 | def __repr__(self): | |
199 | name = self.__class__.__qualname__ |
|
213 | name = self.__class__.__qualname__ | |
200 | raw_cell = ((self.raw_cell[:50] + '..') |
|
214 | raw_cell = ( | |
201 |
|
|
215 | (self.raw_cell[:50] + "..") if len(self.raw_cell) > 50 else self.raw_cell | |
202 | return '<%s object at %x, raw_cell="%s" store_history=%s silent=%s shell_futures=%s>' %\ |
|
216 | ) | |
203 | (name, id(self), raw_cell, self.store_history, self.silent, self.shell_futures) |
|
217 | return '<%s object at %x, raw_cell="%s" store_history=%s silent=%s shell_futures=%s cell_id=%s>' % ( | |
|
218 | name, | |||
|
219 | id(self), | |||
|
220 | raw_cell, | |||
|
221 | self.store_history, | |||
|
222 | self.silent, | |||
|
223 | self.shell_futures, | |||
|
224 | self.cell_id, | |||
|
225 | ) | |||
204 |
|
226 | |||
205 |
|
227 | |||
206 | class ExecutionResult(object): |
|
228 | class ExecutionResult(object): | |
@@ -747,6 +769,33 b' class InteractiveShell(SingletonConfigurable):' | |||||
747 | # the appropriate time. |
|
769 | # the appropriate time. | |
748 | self.display_trap = DisplayTrap(hook=self.displayhook) |
|
770 | self.display_trap = DisplayTrap(hook=self.displayhook) | |
749 |
|
771 | |||
|
772 | @staticmethod | |||
|
773 | def get_path_links(p: Path): | |||
|
774 | """Gets path links including all symlinks | |||
|
775 | ||||
|
776 | Examples | |||
|
777 | -------- | |||
|
778 | In [1]: from IPython.core.interactiveshell import InteractiveShell | |||
|
779 | ||||
|
780 | In [2]: import sys, pathlib | |||
|
781 | ||||
|
782 | In [3]: paths = InteractiveShell.get_path_links(pathlib.Path(sys.executable)) | |||
|
783 | ||||
|
784 | In [4]: len(paths) == len(set(paths)) | |||
|
785 | Out[4]: True | |||
|
786 | ||||
|
787 | In [5]: bool(paths) | |||
|
788 | Out[5]: True | |||
|
789 | """ | |||
|
790 | paths = [p] | |||
|
791 | while p.is_symlink(): | |||
|
792 | new_path = Path(os.readlink(p)) | |||
|
793 | if not new_path.is_absolute(): | |||
|
794 | new_path = p.parent / new_path | |||
|
795 | p = new_path | |||
|
796 | paths.append(p) | |||
|
797 | return paths | |||
|
798 | ||||
750 | def init_virtualenv(self): |
|
799 | def init_virtualenv(self): | |
751 | """Add the current virtualenv to sys.path so the user can import modules from it. |
|
800 | """Add the current virtualenv to sys.path so the user can import modules from it. | |
752 | This isn't perfect: it doesn't use the Python interpreter with which the |
|
801 | This isn't perfect: it doesn't use the Python interpreter with which the | |
@@ -772,10 +821,7 b' class InteractiveShell(SingletonConfigurable):' | |||||
772 | # stdlib venv may symlink sys.executable, so we can't use realpath. |
|
821 | # stdlib venv may symlink sys.executable, so we can't use realpath. | |
773 | # but others can symlink *to* the venv Python, so we can't just use sys.executable. |
|
822 | # but others can symlink *to* the venv Python, so we can't just use sys.executable. | |
774 | # So we just check every item in the symlink tree (generally <= 3) |
|
823 | # So we just check every item in the symlink tree (generally <= 3) | |
775 | paths = [p] |
|
824 | paths = self.get_path_links(p) | |
776 | while p.is_symlink(): |
|
|||
777 | p = Path(os.readlink(p)) |
|
|||
778 | paths.append(p.resolve()) |
|
|||
779 |
|
825 | |||
780 | # In Cygwin paths like "c:\..." and '\cygdrive\c\...' are possible |
|
826 | # In Cygwin paths like "c:\..." and '\cygdrive\c\...' are possible | |
781 | if p_venv.parts[1] == "cygdrive": |
|
827 | if p_venv.parts[1] == "cygdrive": | |
@@ -1941,10 +1987,19 b' class InteractiveShell(SingletonConfigurable):' | |||||
1941 | # Exception classes can customise their traceback - we |
|
1987 | # Exception classes can customise their traceback - we | |
1942 | # use this in IPython.parallel for exceptions occurring |
|
1988 | # use this in IPython.parallel for exceptions occurring | |
1943 | # in the engines. This should return a list of strings. |
|
1989 | # in the engines. This should return a list of strings. | |
1944 |
|
|
1990 | if hasattr(value, "_render_traceback_"): | |
|
1991 | stb = value._render_traceback_() | |||
|
1992 | else: | |||
|
1993 | stb = self.InteractiveTB.structured_traceback( | |||
|
1994 | etype, value, tb, tb_offset=tb_offset | |||
|
1995 | ) | |||
|
1996 | ||||
1945 | except Exception: |
|
1997 | except Exception: | |
1946 | stb = self.InteractiveTB.structured_traceback(etype, |
|
1998 | print( | |
1947 | value, tb, tb_offset=tb_offset) |
|
1999 | "Unexpected exception formatting exception. Falling back to standard exception" | |
|
2000 | ) | |||
|
2001 | traceback.print_exc() | |||
|
2002 | return None | |||
1948 |
|
2003 | |||
1949 | self._showtraceback(etype, value, stb) |
|
2004 | self._showtraceback(etype, value, stb) | |
1950 | if self.call_pdb: |
|
2005 | if self.call_pdb: | |
@@ -2034,8 +2089,12 b' class InteractiveShell(SingletonConfigurable):' | |||||
2034 | (typically over the network by remote frontends). |
|
2089 | (typically over the network by remote frontends). | |
2035 | """ |
|
2090 | """ | |
2036 | from IPython.core.completer import IPCompleter |
|
2091 | from IPython.core.completer import IPCompleter | |
2037 |
from IPython.core.completerlib import ( |
|
2092 | from IPython.core.completerlib import ( | |
2038 | magic_run_completer, cd_completer, reset_completer) |
|
2093 | cd_completer, | |
|
2094 | magic_run_completer, | |||
|
2095 | module_completer, | |||
|
2096 | reset_completer, | |||
|
2097 | ) | |||
2039 |
|
2098 | |||
2040 | self.Completer = IPCompleter(shell=self, |
|
2099 | self.Completer = IPCompleter(shell=self, | |
2041 | namespace=self.user_ns, |
|
2100 | namespace=self.user_ns, | |
@@ -2414,14 +2473,9 b' class InteractiveShell(SingletonConfigurable):' | |||||
2414 | cmd = self.var_expand(cmd, depth=1) |
|
2473 | cmd = self.var_expand(cmd, depth=1) | |
2415 | # warn if there is an IPython magic alternative. |
|
2474 | # warn if there is an IPython magic alternative. | |
2416 | main_cmd = cmd.split()[0] |
|
2475 | main_cmd = cmd.split()[0] | |
2417 |
has_magic_alternatives = ("pip", "conda", "cd" |
|
2476 | has_magic_alternatives = ("pip", "conda", "cd") | |
2418 |
|
2477 | |||
2419 | # had to check if the command was an alias expanded because of `ls` |
|
2478 | if main_cmd in has_magic_alternatives: | |
2420 | is_alias_expanded = self.alias_manager.is_alias(main_cmd) and ( |
|
|||
2421 | self.alias_manager.retrieve_alias(main_cmd).strip() == cmd.strip() |
|
|||
2422 | ) |
|
|||
2423 |
|
||||
2424 | if main_cmd in has_magic_alternatives and not is_alias_expanded: |
|
|||
2425 | warnings.warn( |
|
2479 | warnings.warn( | |
2426 | ( |
|
2480 | ( | |
2427 | "You executed the system command !{0} which may not work " |
|
2481 | "You executed the system command !{0} which may not work " | |
@@ -2791,7 +2845,14 b' class InteractiveShell(SingletonConfigurable):' | |||||
2791 | self.showtraceback() |
|
2845 | self.showtraceback() | |
2792 | warn('Unknown failure executing module: <%s>' % mod_name) |
|
2846 | warn('Unknown failure executing module: <%s>' % mod_name) | |
2793 |
|
2847 | |||
2794 | def run_cell(self, raw_cell, store_history=False, silent=False, shell_futures=True): |
|
2848 | def run_cell( | |
|
2849 | self, | |||
|
2850 | raw_cell, | |||
|
2851 | store_history=False, | |||
|
2852 | silent=False, | |||
|
2853 | shell_futures=True, | |||
|
2854 | cell_id=None, | |||
|
2855 | ): | |||
2795 | """Run a complete IPython cell. |
|
2856 | """Run a complete IPython cell. | |
2796 |
|
2857 | |||
2797 | Parameters |
|
2858 | Parameters | |
@@ -2818,14 +2879,22 b' class InteractiveShell(SingletonConfigurable):' | |||||
2818 | result = None |
|
2879 | result = None | |
2819 | try: |
|
2880 | try: | |
2820 | result = self._run_cell( |
|
2881 | result = self._run_cell( | |
2821 |
raw_cell, store_history, silent, shell_futures |
|
2882 | raw_cell, store_history, silent, shell_futures, cell_id | |
|
2883 | ) | |||
2822 | finally: |
|
2884 | finally: | |
2823 | self.events.trigger('post_execute') |
|
2885 | self.events.trigger('post_execute') | |
2824 | if not silent: |
|
2886 | if not silent: | |
2825 | self.events.trigger('post_run_cell', result) |
|
2887 | self.events.trigger('post_run_cell', result) | |
2826 | return result |
|
2888 | return result | |
2827 |
|
2889 | |||
2828 | def _run_cell(self, raw_cell:str, store_history:bool, silent:bool, shell_futures:bool) -> ExecutionResult: |
|
2890 | def _run_cell( | |
|
2891 | self, | |||
|
2892 | raw_cell: str, | |||
|
2893 | store_history: bool, | |||
|
2894 | silent: bool, | |||
|
2895 | shell_futures: bool, | |||
|
2896 | cell_id: str, | |||
|
2897 | ) -> ExecutionResult: | |||
2829 | """Internal method to run a complete IPython cell.""" |
|
2898 | """Internal method to run a complete IPython cell.""" | |
2830 |
|
2899 | |||
2831 | # we need to avoid calling self.transform_cell multiple time on the same thing |
|
2900 | # we need to avoid calling self.transform_cell multiple time on the same thing | |
@@ -2845,6 +2914,7 b' class InteractiveShell(SingletonConfigurable):' | |||||
2845 | shell_futures=shell_futures, |
|
2914 | shell_futures=shell_futures, | |
2846 | transformed_cell=transformed_cell, |
|
2915 | transformed_cell=transformed_cell, | |
2847 | preprocessing_exc_tuple=preprocessing_exc_tuple, |
|
2916 | preprocessing_exc_tuple=preprocessing_exc_tuple, | |
|
2917 | cell_id=cell_id, | |||
2848 | ) |
|
2918 | ) | |
2849 |
|
2919 | |||
2850 | # run_cell_async is async, but may not actually need an eventloop. |
|
2920 | # run_cell_async is async, but may not actually need an eventloop. | |
@@ -2865,7 +2935,9 b' class InteractiveShell(SingletonConfigurable):' | |||||
2865 | try: |
|
2935 | try: | |
2866 | return runner(coro) |
|
2936 | return runner(coro) | |
2867 | except BaseException as e: |
|
2937 | except BaseException as e: | |
2868 | info = ExecutionInfo(raw_cell, store_history, silent, shell_futures) |
|
2938 | info = ExecutionInfo( | |
|
2939 | raw_cell, store_history, silent, shell_futures, cell_id | |||
|
2940 | ) | |||
2869 | result = ExecutionResult(info) |
|
2941 | result = ExecutionResult(info) | |
2870 | result.error_in_exec = e |
|
2942 | result.error_in_exec = e | |
2871 | self.showtraceback(running_compiled_code=True) |
|
2943 | self.showtraceback(running_compiled_code=True) | |
@@ -2921,7 +2993,8 b' class InteractiveShell(SingletonConfigurable):' | |||||
2921 | shell_futures=True, |
|
2993 | shell_futures=True, | |
2922 | *, |
|
2994 | *, | |
2923 | transformed_cell: Optional[str] = None, |
|
2995 | transformed_cell: Optional[str] = None, | |
2924 | preprocessing_exc_tuple: Optional[Any] = None |
|
2996 | preprocessing_exc_tuple: Optional[Any] = None, | |
|
2997 | cell_id=None, | |||
2925 | ) -> ExecutionResult: |
|
2998 | ) -> ExecutionResult: | |
2926 | """Run a complete IPython cell asynchronously. |
|
2999 | """Run a complete IPython cell asynchronously. | |
2927 |
|
3000 | |||
@@ -2952,8 +3025,7 b' class InteractiveShell(SingletonConfigurable):' | |||||
2952 |
|
3025 | |||
2953 | .. versionadded:: 7.0 |
|
3026 | .. versionadded:: 7.0 | |
2954 | """ |
|
3027 | """ | |
2955 | info = ExecutionInfo( |
|
3028 | info = ExecutionInfo(raw_cell, store_history, silent, shell_futures, cell_id) | |
2956 | raw_cell, store_history, silent, shell_futures) |
|
|||
2957 | result = ExecutionResult(info) |
|
3029 | result = ExecutionResult(info) | |
2958 |
|
3030 | |||
2959 | if (not raw_cell) or raw_cell.isspace(): |
|
3031 | if (not raw_cell) or raw_cell.isspace(): | |
@@ -3008,9 +3080,8 b' class InteractiveShell(SingletonConfigurable):' | |||||
3008 | cell = raw_cell |
|
3080 | cell = raw_cell | |
3009 |
|
3081 | |||
3010 | # Store raw and processed history |
|
3082 | # Store raw and processed history | |
3011 | if store_history: |
|
3083 | if store_history and raw_cell.strip(" %") != "paste": | |
3012 | self.history_manager.store_inputs(self.execution_count, |
|
3084 | self.history_manager.store_inputs(self.execution_count, cell, raw_cell) | |
3013 | cell, raw_cell) |
|
|||
3014 | if not silent: |
|
3085 | if not silent: | |
3015 | self.logger.log(cell, raw_cell) |
|
3086 | self.logger.log(cell, raw_cell) | |
3016 |
|
3087 | |||
@@ -3141,6 +3212,29 b' class InteractiveShell(SingletonConfigurable):' | |||||
3141 | ast.fix_missing_locations(node) |
|
3212 | ast.fix_missing_locations(node) | |
3142 | return node |
|
3213 | return node | |
3143 |
|
3214 | |||
|
3215 | def _update_code_co_name(self, code): | |||
|
3216 | """Python 3.10 changed the behaviour so that whenever a code object | |||
|
3217 | is assembled in the compile(ast) the co_firstlineno would be == 1. | |||
|
3218 | ||||
|
3219 | This makes pydevd/debugpy think that all cells invoked are the same | |||
|
3220 | since it caches information based on (co_firstlineno, co_name, co_filename). | |||
|
3221 | ||||
|
3222 | Given that, this function changes the code 'co_name' to be unique | |||
|
3223 | based on the first real lineno of the code (which also has a nice | |||
|
3224 | side effect of customizing the name so that it's not always <module>). | |||
|
3225 | ||||
|
3226 | See: https://github.com/ipython/ipykernel/issues/841 | |||
|
3227 | """ | |||
|
3228 | if not hasattr(code, "replace"): | |||
|
3229 | # It may not be available on older versions of Python (only | |||
|
3230 | # available for 3.8 onwards). | |||
|
3231 | return code | |||
|
3232 | try: | |||
|
3233 | first_real_line = next(dis.findlinestarts(code))[1] | |||
|
3234 | except StopIteration: | |||
|
3235 | return code | |||
|
3236 | return code.replace(co_name="<cell line: %s>" % (first_real_line,)) | |||
|
3237 | ||||
3144 | async def run_ast_nodes( |
|
3238 | async def run_ast_nodes( | |
3145 | self, |
|
3239 | self, | |
3146 | nodelist: ListType[stmt], |
|
3240 | nodelist: ListType[stmt], | |
@@ -3239,6 +3333,7 b' class InteractiveShell(SingletonConfigurable):' | |||||
3239 | else 0x0 |
|
3333 | else 0x0 | |
3240 | ): |
|
3334 | ): | |
3241 | code = compiler(mod, cell_name, mode) |
|
3335 | code = compiler(mod, cell_name, mode) | |
|
3336 | code = self._update_code_co_name(code) | |||
3242 | asy = compare(code) |
|
3337 | asy = compare(code) | |
3243 | if await self.run_code(code, result, async_=asy): |
|
3338 | if await self.run_code(code, result, async_=asy): | |
3244 | return True |
|
3339 | return True | |
@@ -3309,6 +3404,11 b' class InteractiveShell(SingletonConfigurable):' | |||||
3309 | result.error_in_exec = e |
|
3404 | result.error_in_exec = e | |
3310 | self.showtraceback(exception_only=True) |
|
3405 | self.showtraceback(exception_only=True) | |
3311 | warn("To exit: use 'exit', 'quit', or Ctrl-D.", stacklevel=1) |
|
3406 | warn("To exit: use 'exit', 'quit', or Ctrl-D.", stacklevel=1) | |
|
3407 | except bdb.BdbQuit: | |||
|
3408 | etype, value, tb = sys.exc_info() | |||
|
3409 | if result is not None: | |||
|
3410 | result.error_in_exec = value | |||
|
3411 | # the BdbQuit stops here | |||
3312 | except self.custom_exceptions: |
|
3412 | except self.custom_exceptions: | |
3313 | etype, value, tb = sys.exc_info() |
|
3413 | etype, value, tb = sys.exc_info() | |
3314 | if result is not None: |
|
3414 | if result is not None: | |
@@ -3375,8 +3475,9 b' class InteractiveShell(SingletonConfigurable):' | |||||
3375 | make sense in all contexts, for example a terminal ipython can't |
|
3475 | make sense in all contexts, for example a terminal ipython can't | |
3376 | display figures inline. |
|
3476 | display figures inline. | |
3377 | """ |
|
3477 | """ | |
3378 | from IPython.core import pylabtools as pt |
|
|||
3379 | from matplotlib_inline.backend_inline import configure_inline_support |
|
3478 | from matplotlib_inline.backend_inline import configure_inline_support | |
|
3479 | ||||
|
3480 | from IPython.core import pylabtools as pt | |||
3380 | gui, backend = pt.find_gui_and_backend(gui, self.pylab_gui_select) |
|
3481 | gui, backend = pt.find_gui_and_backend(gui, self.pylab_gui_select) | |
3381 |
|
3482 | |||
3382 | if gui != 'inline': |
|
3483 | if gui != 'inline': | |
@@ -3679,6 +3780,10 b' class InteractiveShell(SingletonConfigurable):' | |||||
3679 | pass |
|
3780 | pass | |
3680 | del self.tempdirs |
|
3781 | del self.tempdirs | |
3681 |
|
3782 | |||
|
3783 | # Restore user's cursor | |||
|
3784 | if hasattr(self, "editing_mode") and self.editing_mode == "vi": | |||
|
3785 | sys.stdout.write("\x1b[0 q") | |||
|
3786 | sys.stdout.flush() | |||
3682 |
|
3787 | |||
3683 | def cleanup(self): |
|
3788 | def cleanup(self): | |
3684 | self.restore_sys_module_state() |
|
3789 | self.restore_sys_module_state() |
@@ -311,7 +311,7 b' class MagicsManager(Configurable):' | |||||
311 |
|
311 | |||
312 | For example:: |
|
312 | For example:: | |
313 |
|
313 | |||
314 | c.MagicsManger.lazy_magics = { |
|
314 | c.MagicsManager.lazy_magics = { | |
315 | "my_magic": "slow.to.import", |
|
315 | "my_magic": "slow.to.import", | |
316 | "my_other_magic": "also.slow", |
|
316 | "my_other_magic": "also.slow", | |
317 | } |
|
317 | } |
@@ -511,7 +511,7 b' class ExecutionMagics(Magics):' | |||||
511 | """Run the named file inside IPython as a program. |
|
511 | """Run the named file inside IPython as a program. | |
512 |
|
512 | |||
513 | Usage:: |
|
513 | Usage:: | |
514 |
|
514 | |||
515 | %run [-n -i -e -G] |
|
515 | %run [-n -i -e -G] | |
516 | [( -t [-N<N>] | -d [-b<N>] | -p [profile options] )] |
|
516 | [( -t [-N<N>] | -d [-b<N>] | -p [profile options] )] | |
517 | ( -m mod | filename ) [args] |
|
517 | ( -m mod | filename ) [args] | |
@@ -552,7 +552,7 b' class ExecutionMagics(Magics):' | |||||
552 | *two* back slashes (e.g. ``\\\\*``) to suppress expansions. |
|
552 | *two* back slashes (e.g. ``\\\\*``) to suppress expansions. | |
553 | To completely disable these expansions, you can use -G flag. |
|
553 | To completely disable these expansions, you can use -G flag. | |
554 |
|
554 | |||
555 |
On Windows systems, the use of single quotes `'` when specifying |
|
555 | On Windows systems, the use of single quotes `'` when specifying | |
556 | a file is not supported. Use double quotes `"`. |
|
556 | a file is not supported. Use double quotes `"`. | |
557 |
|
557 | |||
558 | Options: |
|
558 | Options: |
@@ -127,7 +127,7 b' class OSMagics(Magics):' | |||||
127 | Aliases expand Python variables just like system calls using ! or !! |
|
127 | Aliases expand Python variables just like system calls using ! or !! | |
128 | do: all expressions prefixed with '$' get expanded. For details of |
|
128 | do: all expressions prefixed with '$' get expanded. For details of | |
129 | the semantic rules, see PEP-215: |
|
129 | the semantic rules, see PEP-215: | |
130 |
http |
|
130 | https://peps.python.org/pep-0215/. This is the library used by | |
131 | IPython for variable expansion. If you want to access a true shell |
|
131 | IPython for variable expansion. If you want to access a true shell | |
132 | variable, an extra $ is necessary to prevent its expansion by |
|
132 | variable, an extra $ is necessary to prevent its expansion by | |
133 | IPython:: |
|
133 | IPython:: |
@@ -58,8 +58,8 b' def script_args(f):' | |||||
58 | '--no-raise-error', action="store_false", dest='raise_error', |
|
58 | '--no-raise-error', action="store_false", dest='raise_error', | |
59 | help="""Whether you should raise an error message in addition to |
|
59 | help="""Whether you should raise an error message in addition to | |
60 | a stream on stderr if you get a nonzero exit code. |
|
60 | a stream on stderr if you get a nonzero exit code. | |
61 | """ |
|
61 | """, | |
62 | ) |
|
62 | ), | |
63 | ] |
|
63 | ] | |
64 | for arg in args: |
|
64 | for arg in args: | |
65 | f = arg(f) |
|
65 | f = arg(f) |
@@ -518,12 +518,12 b' class Inspector(Colorable):' | |||||
518 | """Return a mime bundle representation of the input text. |
|
518 | """Return a mime bundle representation of the input text. | |
519 |
|
519 | |||
520 | - if `formatter` is None, the returned mime bundle has |
|
520 | - if `formatter` is None, the returned mime bundle has | |
521 | a `text/plain` field, with the input text. |
|
521 | a ``text/plain`` field, with the input text. | |
522 | a `text/html` field with a `<pre>` tag containing the input text. |
|
522 | a ``text/html`` field with a ``<pre>`` tag containing the input text. | |
523 |
|
523 | |||
524 | - if `formatter` is not None, it must be a callable transforming the |
|
524 | - if ``formatter`` is not None, it must be a callable transforming the | |
525 | input text into a mime bundle. Default values for `text/plain` and |
|
525 | input text into a mime bundle. Default values for ``text/plain`` and | |
526 | `text/html` representations are the ones described above. |
|
526 | ``text/html`` representations are the ones described above. | |
527 |
|
527 | |||
528 | Note: |
|
528 | Note: | |
529 |
|
529 |
@@ -16,7 +16,7 b'' | |||||
16 | # release. 'dev' as a _version_extra string means this is a development |
|
16 | # release. 'dev' as a _version_extra string means this is a development | |
17 | # version |
|
17 | # version | |
18 | _version_major = 8 |
|
18 | _version_major = 8 | |
19 |
_version_minor = |
|
19 | _version_minor = 4 | |
20 | _version_patch = 0 |
|
20 | _version_patch = 0 | |
21 | _version_extra = ".dev" |
|
21 | _version_extra = ".dev" | |
22 | # _version_extra = "rc1" |
|
22 | # _version_extra = "rc1" |
@@ -43,11 +43,11 b' def test_alias_args_error():' | |||||
43 |
|
43 | |||
44 | def test_alias_args_commented(): |
|
44 | def test_alias_args_commented(): | |
45 | """Check that alias correctly ignores 'commented out' args""" |
|
45 | """Check that alias correctly ignores 'commented out' args""" | |
46 |
_ip.magic( |
|
46 | _ip.run_line_magic("alias", "commentarg echo this is %%s a commented out arg") | |
47 |
|
47 | |||
48 | with capture_output() as cap: |
|
48 | with capture_output() as cap: | |
49 |
_ip.run_cell( |
|
49 | _ip.run_cell("commentarg") | |
50 |
|
50 | |||
51 | # strip() is for pytest compat; testing via iptest patch IPython shell |
|
51 | # strip() is for pytest compat; testing via iptest patch IPython shell | |
52 | # in testing.globalipapp and replace the system call which messed up the |
|
52 | # in testing.globalipapp and replace the system call which messed up the | |
53 | # \r\n |
|
53 | # \r\n |
@@ -4,11 +4,11 b'' | |||||
4 | import os |
|
4 | import os | |
5 | import tempfile |
|
5 | import tempfile | |
6 |
|
6 | |||
|
7 | from tempfile import TemporaryDirectory | |||
7 | from traitlets import Unicode |
|
8 | from traitlets import Unicode | |
8 |
|
9 | |||
9 | from IPython.core.application import BaseIPythonApplication |
|
10 | from IPython.core.application import BaseIPythonApplication | |
10 | from IPython.testing import decorators as dec |
|
11 | from IPython.testing import decorators as dec | |
11 | from IPython.utils.tempdir import TemporaryDirectory |
|
|||
12 |
|
12 | |||
13 |
|
13 | |||
14 | @dec.onlyif_unicode_paths |
|
14 | @dec.onlyif_unicode_paths |
@@ -1262,3 +1262,14 b' class TestCompleter(unittest.TestCase):' | |||||
1262 | _, matches = ip.complete(None, "test.meth(") |
|
1262 | _, matches = ip.complete(None, "test.meth(") | |
1263 | self.assertIn("meth_arg1=", matches) |
|
1263 | self.assertIn("meth_arg1=", matches) | |
1264 | self.assertNotIn("meth2_arg1=", matches) |
|
1264 | self.assertNotIn("meth2_arg1=", matches) | |
|
1265 | ||||
|
1266 | def test_percent_symbol_restrict_to_magic_completions(self): | |||
|
1267 | ip = get_ipython() | |||
|
1268 | completer = ip.Completer | |||
|
1269 | text = "%a" | |||
|
1270 | ||||
|
1271 | with provisionalcompleter(): | |||
|
1272 | completer.use_jedi = True | |||
|
1273 | completions = completer.completions(text, len(text)) | |||
|
1274 | for c in completions: | |||
|
1275 | self.assertEqual(c.text[0], "%") |
@@ -14,8 +14,9 b' import tempfile' | |||||
14 | import unittest |
|
14 | import unittest | |
15 | from os.path import join |
|
15 | from os.path import join | |
16 |
|
16 | |||
|
17 | from tempfile import TemporaryDirectory | |||
|
18 | ||||
17 | from IPython.core.completerlib import magic_run_completer, module_completion, try_import |
|
19 | from IPython.core.completerlib import magic_run_completer, module_completion, try_import | |
18 | from IPython.utils.tempdir import TemporaryDirectory |
|
|||
19 | from IPython.testing.decorators import onlyif_unicode_paths |
|
20 | from IPython.testing.decorators import onlyif_unicode_paths | |
20 |
|
21 | |||
21 |
|
22 |
@@ -1,8 +1,9 b'' | |||||
1 | import os.path |
|
1 | import os.path | |
2 |
|
2 | |||
|
3 | from tempfile import TemporaryDirectory | |||
|
4 | ||||
3 | import IPython.testing.tools as tt |
|
5 | import IPython.testing.tools as tt | |
4 | from IPython.utils.syspathcontext import prepended_to_syspath |
|
6 | from IPython.utils.syspathcontext import prepended_to_syspath | |
5 | from IPython.utils.tempdir import TemporaryDirectory |
|
|||
6 |
|
7 | |||
7 | ext1_content = """ |
|
8 | ext1_content = """ | |
8 | def load_ipython_extension(ip): |
|
9 | def load_ipython_extension(ip): |
@@ -56,36 +56,42 b' def test_handlers():' | |||||
56 | ip.user_ns['autocallable'] = autocallable |
|
56 | ip.user_ns['autocallable'] = autocallable | |
57 |
|
57 | |||
58 | # auto |
|
58 | # auto | |
59 |
ip.magic( |
|
59 | ip.run_line_magic("autocall", "0") | |
60 | # Only explicit escapes or instances of IPyAutocallable should get |
|
60 | # Only explicit escapes or instances of IPyAutocallable should get | |
61 | # expanded |
|
61 | # expanded | |
62 |
run( |
|
62 | run( | |
63 | ('len "abc"', 'len "abc"'), |
|
63 | [ | |
64 | ('autocallable', 'autocallable()'), |
|
64 | ('len "abc"', 'len "abc"'), | |
65 | # Don't add extra brackets (gh-1117) |
|
65 | ("autocallable", "autocallable()"), | |
66 | ('autocallable()', 'autocallable()'), |
|
66 | # Don't add extra brackets (gh-1117) | |
67 | ]) |
|
67 | ("autocallable()", "autocallable()"), | |
68 | ip.magic('autocall 1') |
|
68 | ] | |
69 | run([ |
|
69 | ) | |
70 | ('len "abc"', 'len("abc")'), |
|
70 | ip.run_line_magic("autocall", "1") | |
71 | ('len "abc";', 'len("abc");'), # ; is special -- moves out of parens |
|
71 | run( | |
72 | # Autocall is turned off if first arg is [] and the object |
|
72 | [ | |
73 | # is both callable and indexable. Like so: |
|
73 | ('len "abc"', 'len("abc")'), | |
74 | ('len [1,2]', 'len([1,2])'), # len doesn't support __getitem__... |
|
74 | ('len "abc";', 'len("abc");'), # ; is special -- moves out of parens | |
75 | ('call_idx [1]', 'call_idx [1]'), # call_idx *does*.. |
|
75 | # Autocall is turned off if first arg is [] and the object | |
76 | ('call_idx 1', 'call_idx(1)'), |
|
76 | # is both callable and indexable. Like so: | |
77 | ('len', 'len'), # only at 2 does it auto-call on single args |
|
77 | ("len [1,2]", "len([1,2])"), # len doesn't support __getitem__... | |
78 | ]) |
|
78 | ("call_idx [1]", "call_idx [1]"), # call_idx *does*.. | |
79 | ip.magic('autocall 2') |
|
79 | ("call_idx 1", "call_idx(1)"), | |
80 | run([ |
|
80 | ("len", "len"), # only at 2 does it auto-call on single args | |
81 | ('len "abc"', 'len("abc")'), |
|
81 | ] | |
82 | ('len "abc";', 'len("abc");'), |
|
82 | ) | |
83 | ('len [1,2]', 'len([1,2])'), |
|
83 | ip.run_line_magic("autocall", "2") | |
84 | ('call_idx [1]', 'call_idx [1]'), |
|
84 | run( | |
85 | ('call_idx 1', 'call_idx(1)'), |
|
85 | [ | |
86 | # This is what's different: |
|
86 | ('len "abc"', 'len("abc")'), | |
87 | ('len', 'len()'), # only at 2 does it auto-call on single args |
|
87 | ('len "abc";', 'len("abc");'), | |
88 | ]) |
|
88 | ("len [1,2]", "len([1,2])"), | |
89 | ip.magic('autocall 1') |
|
89 | ("call_idx [1]", "call_idx [1]"), | |
|
90 | ("call_idx 1", "call_idx(1)"), | |||
|
91 | # This is what's different: | |||
|
92 | ("len", "len()"), # only at 2 does it auto-call on single args | |||
|
93 | ] | |||
|
94 | ) | |||
|
95 | ip.run_line_magic("autocall", "1") | |||
90 |
|
96 | |||
91 | assert failures == [] |
|
97 | assert failures == [] |
@@ -7,17 +7,19 b'' | |||||
7 |
|
7 | |||
8 | # stdlib |
|
8 | # stdlib | |
9 | import io |
|
9 | import io | |
10 | from pathlib import Path |
|
10 | import sqlite3 | |
11 | import sys |
|
11 | import sys | |
12 | import tempfile |
|
12 | import tempfile | |
13 | from datetime import datetime |
|
13 | from datetime import datetime | |
14 | import sqlite3 |
|
14 | from pathlib import Path | |
15 |
|
15 | |||
|
16 | from tempfile import TemporaryDirectory | |||
16 | # our own packages |
|
17 | # our own packages | |
17 | from traitlets.config.loader import Config |
|
18 | from traitlets.config.loader import Config | |
18 | from IPython.utils.tempdir import TemporaryDirectory |
|
19 | ||
19 | from IPython.core.history import HistoryManager, extract_hist_ranges |
|
20 | from IPython.core.history import HistoryManager, extract_hist_ranges | |
20 |
|
21 | |||
|
22 | ||||
21 | def test_proper_default_encoding(): |
|
23 | def test_proper_default_encoding(): | |
22 | assert sys.getdefaultencoding() == "utf-8" |
|
24 | assert sys.getdefaultencoding() == "utf-8" | |
23 |
|
25 | |||
@@ -50,13 +52,13 b' def test_history():' | |||||
50 |
|
52 | |||
51 | # Check whether specifying a range beyond the end of the current |
|
53 | # Check whether specifying a range beyond the end of the current | |
52 | # session results in an error (gh-804) |
|
54 | # session results in an error (gh-804) | |
53 |
ip.magic( |
|
55 | ip.run_line_magic("hist", "2-500") | |
54 |
|
56 | |||
55 | # Check that we can write non-ascii characters to a file |
|
57 | # Check that we can write non-ascii characters to a file | |
56 |
ip.magic(" |
|
58 | ip.run_line_magic("hist", "-f %s" % (tmp_path / "test1")) | |
57 |
ip.magic(" |
|
59 | ip.run_line_magic("hist", "-pf %s" % (tmp_path / "test2")) | |
58 |
ip.magic(" |
|
60 | ip.run_line_magic("hist", "-nf %s" % (tmp_path / "test3")) | |
59 |
ip.magic(" |
|
61 | ip.run_line_magic("save", "%s 1-10" % (tmp_path / "test4")) | |
60 |
|
62 | |||
61 | # New session |
|
63 | # New session | |
62 | ip.history_manager.reset() |
|
64 | ip.history_manager.reset() | |
@@ -122,7 +124,7 b' def test_history():' | |||||
122 |
|
124 | |||
123 | # Cross testing: check that magic %save can get previous session. |
|
125 | # Cross testing: check that magic %save can get previous session. | |
124 | testfilename = (tmp_path / "test.py").resolve() |
|
126 | testfilename = (tmp_path / "test.py").resolve() | |
125 |
ip.magic("save |
|
127 | ip.run_line_magic("save", str(testfilename) + " ~1/1-3") | |
126 | with io.open(testfilename, encoding="utf-8") as testfile: |
|
128 | with io.open(testfilename, encoding="utf-8") as testfile: | |
127 | assert testfile.read() == "# coding: utf-8\n" + "\n".join(hist) + "\n" |
|
129 | assert testfile.read() == "# coding: utf-8\n" + "\n".join(hist) + "\n" | |
128 |
|
130 |
@@ -59,108 +59,93 b' syntax = \\' | |||||
59 | ('x=1', 'x=1'), # normal input is unmodified |
|
59 | ('x=1', 'x=1'), # normal input is unmodified | |
60 | (' ',' '), # blank lines are kept intact |
|
60 | (' ',' '), # blank lines are kept intact | |
61 | ("a, b = %foo", "a, b = get_ipython().run_line_magic('foo', '')"), |
|
61 | ("a, b = %foo", "a, b = get_ipython().run_line_magic('foo', '')"), | |
62 |
|
|
62 | ], | |
63 |
|
63 | classic_prompt=[ | ||
64 | classic_prompt = |
|
64 | (">>> x=1", "x=1"), | |
65 | [('>>> x=1', 'x=1'), |
|
65 | ("x=1", "x=1"), # normal input is unmodified | |
66 | ('x=1', 'x=1'), # normal input is unmodified |
|
66 | (" ", " "), # blank lines are kept intact | |
67 | (' ', ' '), # blank lines are kept intact |
|
67 | ], | |
68 | ], |
|
68 | ipy_prompt=[ | |
69 |
|
69 | ("In [1]: x=1", "x=1"), | ||
70 | ipy_prompt = |
|
70 | ("x=1", "x=1"), # normal input is unmodified | |
71 | [('In [1]: x=1', 'x=1'), |
|
71 | (" ", " "), # blank lines are kept intact | |
72 | ('x=1', 'x=1'), # normal input is unmodified |
|
72 | ], | |
73 | (' ',' '), # blank lines are kept intact |
|
73 | # Tests for the escape transformer to leave normal code alone | |
74 | ], |
|
74 | escaped_noesc=[ | |
75 |
|
75 | (" ", " "), | ||
76 | # Tests for the escape transformer to leave normal code alone |
|
76 | ("x=1", "x=1"), | |
77 | escaped_noesc = |
|
77 | ], | |
78 | [ (' ', ' '), |
|
78 | # System calls | |
79 | ('x=1', 'x=1'), |
|
79 | escaped_shell=[ | |
80 | ], |
|
80 | ("!ls", "get_ipython().system('ls')"), | |
81 |
|
81 | # Double-escape shell, this means to capture the output of the | ||
82 | # System calls |
|
82 | # subprocess and return it | |
83 | escaped_shell = |
|
83 | ("!!ls", "get_ipython().getoutput('ls')"), | |
84 | [ ('!ls', "get_ipython().system('ls')"), |
|
84 | ], | |
85 | # Double-escape shell, this means to capture the output of the |
|
85 | # Help/object info | |
86 | # subprocess and return it |
|
86 | escaped_help=[ | |
87 |
|
|
87 | ("?", "get_ipython().show_usage()"), | |
88 | ], |
|
88 | ("?x1", "get_ipython().run_line_magic('pinfo', 'x1')"), | |
89 |
|
89 | ("??x2", "get_ipython().run_line_magic('pinfo2', 'x2')"), | ||
90 | # Help/object info |
|
90 | ("?a.*s", "get_ipython().run_line_magic('psearch', 'a.*s')"), | |
91 | escaped_help = |
|
91 | ("?%hist1", "get_ipython().run_line_magic('pinfo', '%hist1')"), | |
92 | [ ('?', 'get_ipython().show_usage()'), |
|
92 | ("?%%hist2", "get_ipython().run_line_magic('pinfo', '%%hist2')"), | |
93 |
|
|
93 | ("?abc = qwe", "get_ipython().run_line_magic('pinfo', 'abc')"), | |
94 | ('??x2', "get_ipython().run_line_magic('pinfo2', 'x2')"), |
|
94 | ], | |
95 | ('?a.*s', "get_ipython().run_line_magic('psearch', 'a.*s')"), |
|
95 | end_help=[ | |
96 |
|
|
96 | ("x3?", "get_ipython().run_line_magic('pinfo', 'x3')"), | |
97 |
|
|
97 | ("x4??", "get_ipython().run_line_magic('pinfo2', 'x4')"), | |
98 |
|
|
98 | ("%hist1?", "get_ipython().run_line_magic('pinfo', '%hist1')"), | |
99 | ], |
|
99 | ("%hist2??", "get_ipython().run_line_magic('pinfo2', '%hist2')"), | |
100 |
|
100 | ("%%hist3?", "get_ipython().run_line_magic('pinfo', '%%hist3')"), | ||
101 | end_help = |
|
101 | ("%%hist4??", "get_ipython().run_line_magic('pinfo2', '%%hist4')"), | |
102 |
|
|
102 | ("π.foo?", "get_ipython().run_line_magic('pinfo', 'π.foo')"), | |
103 |
( |
|
103 | ("f*?", "get_ipython().run_line_magic('psearch', 'f*')"), | |
104 |
( |
|
104 | ("ax.*aspe*?", "get_ipython().run_line_magic('psearch', 'ax.*aspe*')"), | |
105 |
( |
|
105 | ("a = abc?", "get_ipython().run_line_magic('pinfo', 'abc')"), | |
106 |
( |
|
106 | ("a = abc.qe??", "get_ipython().run_line_magic('pinfo2', 'abc.qe')"), | |
107 |
( |
|
107 | ("a = *.items?", "get_ipython().run_line_magic('psearch', '*.items')"), | |
108 |
( |
|
108 | ("plot(a?", "get_ipython().run_line_magic('pinfo', 'a')"), | |
109 | ('f*?', "get_ipython().run_line_magic('psearch', 'f*')"), |
|
109 | ("a*2 #comment?", "a*2 #comment?"), | |
110 | ('ax.*aspe*?', "get_ipython().run_line_magic('psearch', 'ax.*aspe*')"), |
|
110 | ], | |
111 | ('a = abc?', "get_ipython().set_next_input('a = abc');" |
|
111 | # Explicit magic calls | |
112 | "get_ipython().run_line_magic('pinfo', 'abc')"), |
|
112 | escaped_magic=[ | |
113 | ('a = abc.qe??', "get_ipython().set_next_input('a = abc.qe');" |
|
113 | ("%cd", "get_ipython().run_line_magic('cd', '')"), | |
114 |
|
|
114 | ("%cd /home", "get_ipython().run_line_magic('cd', '/home')"), | |
115 | ('a = *.items?', "get_ipython().set_next_input('a = *.items');" |
|
115 | # Backslashes need to be escaped. | |
116 |
|
|
116 | ("%cd C:\\User", "get_ipython().run_line_magic('cd', 'C:\\\\User')"), | |
117 | ('plot(a?', "get_ipython().set_next_input('plot(a');" |
|
117 | (" %magic", " get_ipython().run_line_magic('magic', '')"), | |
118 | "get_ipython().run_line_magic('pinfo', 'a')"), |
|
118 | ], | |
119 | ('a*2 #comment?', 'a*2 #comment?'), |
|
119 | # Quoting with separate arguments | |
120 | ], |
|
120 | escaped_quote=[ | |
121 |
|
121 | (",f", 'f("")'), | ||
122 | # Explicit magic calls |
|
122 | (",f x", 'f("x")'), | |
123 | escaped_magic = |
|
123 | (" ,f y", ' f("y")'), | |
124 | [ ('%cd', "get_ipython().run_line_magic('cd', '')"), |
|
124 | (",f a b", 'f("a", "b")'), | |
125 | ('%cd /home', "get_ipython().run_line_magic('cd', '/home')"), |
|
125 | ], | |
126 | # Backslashes need to be escaped. |
|
126 | # Quoting with single argument | |
127 | ('%cd C:\\User', "get_ipython().run_line_magic('cd', 'C:\\\\User')"), |
|
127 | escaped_quote2=[ | |
128 | (' %magic', " get_ipython().run_line_magic('magic', '')"), |
|
128 | (";f", 'f("")'), | |
129 | ], |
|
129 | (";f x", 'f("x")'), | |
130 |
|
130 | (" ;f y", ' f("y")'), | ||
131 | # Quoting with separate arguments |
|
131 | (";f a b", 'f("a b")'), | |
132 | escaped_quote = |
|
132 | ], | |
133 | [ (',f', 'f("")'), |
|
133 | # Simply apply parens | |
134 | (',f x', 'f("x")'), |
|
134 | escaped_paren=[ | |
135 |
|
|
135 | ("/f", "f()"), | |
136 |
|
|
136 | ("/f x", "f(x)"), | |
137 | ], |
|
137 | (" /f y", " f(y)"), | |
138 |
|
138 | ("/f a b", "f(a, b)"), | ||
139 | # Quoting with single argument |
|
139 | ], | |
140 | escaped_quote2 = |
|
140 | # Check that we transform prompts before other transforms | |
141 | [ (';f', 'f("")'), |
|
141 | mixed=[ | |
142 | (';f x', 'f("x")'), |
|
142 | ("In [1]: %lsmagic", "get_ipython().run_line_magic('lsmagic', '')"), | |
143 | (' ;f y', ' f("y")'), |
|
143 | (">>> %lsmagic", "get_ipython().run_line_magic('lsmagic', '')"), | |
144 | (';f a b', 'f("a b")'), |
|
144 | ("In [2]: !ls", "get_ipython().system('ls')"), | |
145 | ], |
|
145 | ("In [3]: abs?", "get_ipython().run_line_magic('pinfo', 'abs')"), | |
146 |
|
146 | ("In [4]: b = %who", "b = get_ipython().run_line_magic('who', '')"), | ||
147 | # Simply apply parens |
|
147 | ], | |
148 | escaped_paren = |
|
148 | ) | |
149 | [ ('/f', 'f()'), |
|
|||
150 | ('/f x', 'f(x)'), |
|
|||
151 | (' /f y', ' f(y)'), |
|
|||
152 | ('/f a b', 'f(a, b)'), |
|
|||
153 | ], |
|
|||
154 |
|
||||
155 | # Check that we transform prompts before other transforms |
|
|||
156 | mixed = |
|
|||
157 | [ ('In [1]: %lsmagic', "get_ipython().run_line_magic('lsmagic', '')"), |
|
|||
158 | ('>>> %lsmagic', "get_ipython().run_line_magic('lsmagic', '')"), |
|
|||
159 | ('In [2]: !ls', "get_ipython().system('ls')"), |
|
|||
160 | ('In [3]: abs?', "get_ipython().run_line_magic('pinfo', 'abs')"), |
|
|||
161 | ('In [4]: b = %who', "b = get_ipython().run_line_magic('who', '')"), |
|
|||
162 | ], |
|
|||
163 | ) |
|
|||
164 |
|
149 | |||
165 | # multiline syntax examples. Each of these should be a list of lists, with |
|
150 | # multiline syntax examples. Each of these should be a list of lists, with | |
166 | # each entry itself having pairs of raw/transformed input. The union (with |
|
151 | # each entry itself having pairs of raw/transformed input. The union (with |
@@ -14,45 +14,65 b' import pytest' | |||||
14 | from IPython.core import inputtransformer2 as ipt2 |
|
14 | from IPython.core import inputtransformer2 as ipt2 | |
15 | from IPython.core.inputtransformer2 import _find_assign_op, make_tokens_by_line |
|
15 | from IPython.core.inputtransformer2 import _find_assign_op, make_tokens_by_line | |
16 |
|
16 | |||
17 |
MULTILINE_MAGIC = ( |
|
17 | MULTILINE_MAGIC = ( | |
|
18 | """\ | |||
18 |
|
|
19 | a = f() | |
19 | %foo \\ |
|
20 | %foo \\ | |
20 |
|
|
21 | bar | |
21 |
|
|
22 | g() | |
22 | """.splitlines(keepends=True), (2, 0), """\ |
|
23 | """.splitlines( | |
|
24 | keepends=True | |||
|
25 | ), | |||
|
26 | (2, 0), | |||
|
27 | """\ | |||
23 |
|
|
28 | a = f() | |
24 | get_ipython().run_line_magic('foo', ' bar') |
|
29 | get_ipython().run_line_magic('foo', ' bar') | |
25 |
|
|
30 | g() | |
26 |
|
|
31 | """.splitlines( | |
|
32 | keepends=True | |||
|
33 | ), | |||
|
34 | ) | |||
27 |
|
35 | |||
28 |
INDENTED_MAGIC = ( |
|
36 | INDENTED_MAGIC = ( | |
|
37 | """\ | |||
29 |
|
|
38 | for a in range(5): | |
30 | %ls |
|
39 | %ls | |
31 | """.splitlines(keepends=True), (2, 4), """\ |
|
40 | """.splitlines( | |
|
41 | keepends=True | |||
|
42 | ), | |||
|
43 | (2, 4), | |||
|
44 | """\ | |||
32 |
|
|
45 | for a in range(5): | |
33 | get_ipython().run_line_magic('ls', '') |
|
46 | get_ipython().run_line_magic('ls', '') | |
34 |
|
|
47 | """.splitlines( | |
|
48 | keepends=True | |||
|
49 | ), | |||
|
50 | ) | |||
35 |
|
51 | |||
36 |
CRLF_MAGIC = ( |
|
52 | CRLF_MAGIC = ( | |
37 | "a = f()\n", |
|
53 | ["a = f()\n", "%ls\r\n", "g()\n"], | |
38 | "%ls\r\n", |
|
54 | (2, 0), | |
39 | "g()\n" |
|
55 | ["a = f()\n", "get_ipython().run_line_magic('ls', '')\n", "g()\n"], | |
40 | ], (2, 0), [ |
|
56 | ) | |
41 | "a = f()\n", |
|
57 | ||
42 | "get_ipython().run_line_magic('ls', '')\n", |
|
58 | MULTILINE_MAGIC_ASSIGN = ( | |
43 |
" |
|
59 | """\ | |
44 | ]) |
|
|||
45 |
|
||||
46 | MULTILINE_MAGIC_ASSIGN = ("""\ |
|
|||
47 |
|
|
60 | a = f() | |
48 | b = %foo \\ |
|
61 | b = %foo \\ | |
49 |
|
|
62 | bar | |
50 |
|
|
63 | g() | |
51 | """.splitlines(keepends=True), (2, 4), """\ |
|
64 | """.splitlines( | |
|
65 | keepends=True | |||
|
66 | ), | |||
|
67 | (2, 4), | |||
|
68 | """\ | |||
52 |
|
|
69 | a = f() | |
53 | b = get_ipython().run_line_magic('foo', ' bar') |
|
70 | b = get_ipython().run_line_magic('foo', ' bar') | |
54 |
|
|
71 | g() | |
55 |
|
|
72 | """.splitlines( | |
|
73 | keepends=True | |||
|
74 | ), | |||
|
75 | ) | |||
56 |
|
76 | |||
57 | MULTILINE_SYSTEM_ASSIGN = ("""\ |
|
77 | MULTILINE_SYSTEM_ASSIGN = ("""\ | |
58 | a = f() |
|
78 | a = f() | |
@@ -67,73 +87,76 b' g()' | |||||
67 |
|
87 | |||
68 | ##### |
|
88 | ##### | |
69 |
|
89 | |||
70 |
MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT = ( |
|
90 | MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT = ( | |
|
91 | """\ | |||
71 |
|
|
92 | def test(): | |
72 |
|
|
93 | for i in range(1): | |
73 |
|
|
94 | print(i) | |
74 |
|
|
95 | res =! ls | |
75 | """.splitlines(keepends=True), (4, 7), '''\ |
|
96 | """.splitlines( | |
|
97 | keepends=True | |||
|
98 | ), | |||
|
99 | (4, 7), | |||
|
100 | """\ | |||
76 |
|
|
101 | def test(): | |
77 |
|
|
102 | for i in range(1): | |
78 |
|
|
103 | print(i) | |
79 | res =get_ipython().getoutput(\' ls\') |
|
104 | res =get_ipython().getoutput(\' ls\') | |
80 | '''.splitlines(keepends=True)) |
|
105 | """.splitlines( | |
|
106 | keepends=True | |||
|
107 | ), | |||
|
108 | ) | |||
81 |
|
109 | |||
82 | ###### |
|
110 | ###### | |
83 |
|
111 | |||
84 | AUTOCALL_QUOTE = ( |
|
112 | AUTOCALL_QUOTE = ([",f 1 2 3\n"], (1, 0), ['f("1", "2", "3")\n']) | |
85 | [",f 1 2 3\n"], (1, 0), |
|
|||
86 | ['f("1", "2", "3")\n'] |
|
|||
87 | ) |
|
|||
88 |
|
113 | |||
89 | AUTOCALL_QUOTE2 = ( |
|
114 | AUTOCALL_QUOTE2 = ([";f 1 2 3\n"], (1, 0), ['f("1 2 3")\n']) | |
90 | [";f 1 2 3\n"], (1, 0), |
|
|||
91 | ['f("1 2 3")\n'] |
|
|||
92 | ) |
|
|||
93 |
|
115 | |||
94 | AUTOCALL_PAREN = ( |
|
116 | AUTOCALL_PAREN = (["/f 1 2 3\n"], (1, 0), ["f(1, 2, 3)\n"]) | |
95 | ["/f 1 2 3\n"], (1, 0), |
|
|||
96 | ['f(1, 2, 3)\n'] |
|
|||
97 | ) |
|
|||
98 |
|
117 | |||
99 | SIMPLE_HELP = ( |
|
118 | SIMPLE_HELP = (["foo?\n"], (1, 0), ["get_ipython().run_line_magic('pinfo', 'foo')\n"]) | |
100 | ["foo?\n"], (1, 0), |
|
|||
101 | ["get_ipython().run_line_magic('pinfo', 'foo')\n"] |
|
|||
102 | ) |
|
|||
103 |
|
119 | |||
104 | DETAILED_HELP = ( |
|
120 | DETAILED_HELP = ( | |
105 |
["foo??\n"], |
|
121 | ["foo??\n"], | |
106 | ["get_ipython().run_line_magic('pinfo2', 'foo')\n"] |
|
122 | (1, 0), | |
|
123 | ["get_ipython().run_line_magic('pinfo2', 'foo')\n"], | |||
107 | ) |
|
124 | ) | |
108 |
|
125 | |||
109 | MAGIC_HELP = ( |
|
126 | MAGIC_HELP = (["%foo?\n"], (1, 0), ["get_ipython().run_line_magic('pinfo', '%foo')\n"]) | |
110 | ["%foo?\n"], (1, 0), |
|
|||
111 | ["get_ipython().run_line_magic('pinfo', '%foo')\n"] |
|
|||
112 | ) |
|
|||
113 |
|
127 | |||
114 | HELP_IN_EXPR = ( |
|
128 | HELP_IN_EXPR = ( | |
115 |
["a = b + c?\n"], |
|
129 | ["a = b + c?\n"], | |
116 | ["get_ipython().set_next_input('a = b + c');" |
|
130 | (1, 0), | |
117 |
|
|
131 | ["get_ipython().run_line_magic('pinfo', 'c')\n"], | |
118 | ) |
|
132 | ) | |
119 |
|
133 | |||
120 |
HELP_CONTINUED_LINE = ( |
|
134 | HELP_CONTINUED_LINE = ( | |
|
135 | """\ | |||
121 | a = \\ |
|
136 | a = \\ | |
122 |
|
|
137 | zip? | |
123 | """.splitlines(keepends=True), (1, 0), |
|
138 | """.splitlines( | |
124 | [r"get_ipython().set_next_input('a = \\\nzip');get_ipython().run_line_magic('pinfo', 'zip')" + "\n"] |
|
139 | keepends=True | |
|
140 | ), | |||
|
141 | (1, 0), | |||
|
142 | [r"get_ipython().run_line_magic('pinfo', 'zip')" + "\n"], | |||
125 | ) |
|
143 | ) | |
126 |
|
144 | |||
127 |
HELP_MULTILINE = ( |
|
145 | HELP_MULTILINE = ( | |
|
146 | """\ | |||
128 |
|
|
147 | (a, | |
129 |
|
|
148 | b) = zip? | |
130 | """.splitlines(keepends=True), (1, 0), |
|
149 | """.splitlines( | |
131 | [r"get_ipython().set_next_input('(a,\nb) = zip');get_ipython().run_line_magic('pinfo', 'zip')" + "\n"] |
|
150 | keepends=True | |
|
151 | ), | |||
|
152 | (1, 0), | |||
|
153 | [r"get_ipython().run_line_magic('pinfo', 'zip')" + "\n"], | |||
132 | ) |
|
154 | ) | |
133 |
|
155 | |||
134 | HELP_UNICODE = ( |
|
156 | HELP_UNICODE = ( | |
135 |
["π.foo?\n"], |
|
157 | ["π.foo?\n"], | |
136 | ["get_ipython().run_line_magic('pinfo', 'π.foo')\n"] |
|
158 | (1, 0), | |
|
159 | ["get_ipython().run_line_magic('pinfo', 'π.foo')\n"], | |||
137 | ) |
|
160 | ) | |
138 |
|
161 | |||
139 |
|
162 | |||
@@ -149,6 +172,7 b' def test_check_make_token_by_line_never_ends_empty():' | |||||
149 | Check that not sequence of single or double characters ends up leading to en empty list of tokens |
|
172 | Check that not sequence of single or double characters ends up leading to en empty list of tokens | |
150 | """ |
|
173 | """ | |
151 | from string import printable |
|
174 | from string import printable | |
|
175 | ||||
152 | for c in printable: |
|
176 | for c in printable: | |
153 | assert make_tokens_by_line(c)[-1] != [] |
|
177 | assert make_tokens_by_line(c)[-1] != [] | |
154 | for k in printable: |
|
178 | for k in printable: | |
@@ -156,7 +180,7 b' def test_check_make_token_by_line_never_ends_empty():' | |||||
156 |
|
180 | |||
157 |
|
181 | |||
158 | def check_find(transformer, case, match=True): |
|
182 | def check_find(transformer, case, match=True): | |
159 |
sample, expected_start, _ |
|
183 | sample, expected_start, _ = case | |
160 | tbl = make_tokens_by_line(sample) |
|
184 | tbl = make_tokens_by_line(sample) | |
161 | res = transformer.find(tbl) |
|
185 | res = transformer.find(tbl) | |
162 | if match: |
|
186 | if match: | |
@@ -166,25 +190,30 b' def check_find(transformer, case, match=True):' | |||||
166 | else: |
|
190 | else: | |
167 | assert res is None |
|
191 | assert res is None | |
168 |
|
192 | |||
|
193 | ||||
169 | def check_transform(transformer_cls, case): |
|
194 | def check_transform(transformer_cls, case): | |
170 | lines, start, expected = case |
|
195 | lines, start, expected = case | |
171 | transformer = transformer_cls(start) |
|
196 | transformer = transformer_cls(start) | |
172 | assert transformer.transform(lines) == expected |
|
197 | assert transformer.transform(lines) == expected | |
173 |
|
198 | |||
|
199 | ||||
174 | def test_continued_line(): |
|
200 | def test_continued_line(): | |
175 | lines = MULTILINE_MAGIC_ASSIGN[0] |
|
201 | lines = MULTILINE_MAGIC_ASSIGN[0] | |
176 | assert ipt2.find_end_of_continued_line(lines, 1) == 2 |
|
202 | assert ipt2.find_end_of_continued_line(lines, 1) == 2 | |
177 |
|
203 | |||
178 | assert ipt2.assemble_continued_line(lines, (1, 5), 2) == "foo bar" |
|
204 | assert ipt2.assemble_continued_line(lines, (1, 5), 2) == "foo bar" | |
179 |
|
205 | |||
|
206 | ||||
180 | def test_find_assign_magic(): |
|
207 | def test_find_assign_magic(): | |
181 | check_find(ipt2.MagicAssign, MULTILINE_MAGIC_ASSIGN) |
|
208 | check_find(ipt2.MagicAssign, MULTILINE_MAGIC_ASSIGN) | |
182 | check_find(ipt2.MagicAssign, MULTILINE_SYSTEM_ASSIGN, match=False) |
|
209 | check_find(ipt2.MagicAssign, MULTILINE_SYSTEM_ASSIGN, match=False) | |
183 | check_find(ipt2.MagicAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT, match=False) |
|
210 | check_find(ipt2.MagicAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT, match=False) | |
184 |
|
211 | |||
|
212 | ||||
185 | def test_transform_assign_magic(): |
|
213 | def test_transform_assign_magic(): | |
186 | check_transform(ipt2.MagicAssign, MULTILINE_MAGIC_ASSIGN) |
|
214 | check_transform(ipt2.MagicAssign, MULTILINE_MAGIC_ASSIGN) | |
187 |
|
215 | |||
|
216 | ||||
188 | def test_find_assign_system(): |
|
217 | def test_find_assign_system(): | |
189 | check_find(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN) |
|
218 | check_find(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN) | |
190 | check_find(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT) |
|
219 | check_find(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT) | |
@@ -192,30 +221,36 b' def test_find_assign_system():' | |||||
192 | check_find(ipt2.SystemAssign, (["a=!ls\n"], (1, 2), None)) |
|
221 | check_find(ipt2.SystemAssign, (["a=!ls\n"], (1, 2), None)) | |
193 | check_find(ipt2.SystemAssign, MULTILINE_MAGIC_ASSIGN, match=False) |
|
222 | check_find(ipt2.SystemAssign, MULTILINE_MAGIC_ASSIGN, match=False) | |
194 |
|
223 | |||
|
224 | ||||
195 | def test_transform_assign_system(): |
|
225 | def test_transform_assign_system(): | |
196 | check_transform(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN) |
|
226 | check_transform(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN) | |
197 | check_transform(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT) |
|
227 | check_transform(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT) | |
198 |
|
228 | |||
|
229 | ||||
199 | def test_find_magic_escape(): |
|
230 | def test_find_magic_escape(): | |
200 | check_find(ipt2.EscapedCommand, MULTILINE_MAGIC) |
|
231 | check_find(ipt2.EscapedCommand, MULTILINE_MAGIC) | |
201 | check_find(ipt2.EscapedCommand, INDENTED_MAGIC) |
|
232 | check_find(ipt2.EscapedCommand, INDENTED_MAGIC) | |
202 | check_find(ipt2.EscapedCommand, MULTILINE_MAGIC_ASSIGN, match=False) |
|
233 | check_find(ipt2.EscapedCommand, MULTILINE_MAGIC_ASSIGN, match=False) | |
203 |
|
234 | |||
|
235 | ||||
204 | def test_transform_magic_escape(): |
|
236 | def test_transform_magic_escape(): | |
205 | check_transform(ipt2.EscapedCommand, MULTILINE_MAGIC) |
|
237 | check_transform(ipt2.EscapedCommand, MULTILINE_MAGIC) | |
206 | check_transform(ipt2.EscapedCommand, INDENTED_MAGIC) |
|
238 | check_transform(ipt2.EscapedCommand, INDENTED_MAGIC) | |
207 | check_transform(ipt2.EscapedCommand, CRLF_MAGIC) |
|
239 | check_transform(ipt2.EscapedCommand, CRLF_MAGIC) | |
208 |
|
240 | |||
|
241 | ||||
209 | def test_find_autocalls(): |
|
242 | def test_find_autocalls(): | |
210 | for case in [AUTOCALL_QUOTE, AUTOCALL_QUOTE2, AUTOCALL_PAREN]: |
|
243 | for case in [AUTOCALL_QUOTE, AUTOCALL_QUOTE2, AUTOCALL_PAREN]: | |
211 | print("Testing %r" % case[0]) |
|
244 | print("Testing %r" % case[0]) | |
212 | check_find(ipt2.EscapedCommand, case) |
|
245 | check_find(ipt2.EscapedCommand, case) | |
213 |
|
246 | |||
|
247 | ||||
214 | def test_transform_autocall(): |
|
248 | def test_transform_autocall(): | |
215 | for case in [AUTOCALL_QUOTE, AUTOCALL_QUOTE2, AUTOCALL_PAREN]: |
|
249 | for case in [AUTOCALL_QUOTE, AUTOCALL_QUOTE2, AUTOCALL_PAREN]: | |
216 | print("Testing %r" % case[0]) |
|
250 | print("Testing %r" % case[0]) | |
217 | check_transform(ipt2.EscapedCommand, case) |
|
251 | check_transform(ipt2.EscapedCommand, case) | |
218 |
|
252 | |||
|
253 | ||||
219 | def test_find_help(): |
|
254 | def test_find_help(): | |
220 | for case in [SIMPLE_HELP, DETAILED_HELP, MAGIC_HELP, HELP_IN_EXPR]: |
|
255 | for case in [SIMPLE_HELP, DETAILED_HELP, MAGIC_HELP, HELP_IN_EXPR]: | |
221 | check_find(ipt2.HelpEnd, case) |
|
256 | check_find(ipt2.HelpEnd, case) | |
@@ -233,6 +268,7 b' def test_find_help():' | |||||
233 | # Nor in a string |
|
268 | # Nor in a string | |
234 | check_find(ipt2.HelpEnd, (["foo = '''bar?\n"], None, None), match=False) |
|
269 | check_find(ipt2.HelpEnd, (["foo = '''bar?\n"], None, None), match=False) | |
235 |
|
270 | |||
|
271 | ||||
236 | def test_transform_help(): |
|
272 | def test_transform_help(): | |
237 | tf = ipt2.HelpEnd((1, 0), (1, 9)) |
|
273 | tf = ipt2.HelpEnd((1, 0), (1, 9)) | |
238 | assert tf.transform(HELP_IN_EXPR[0]) == HELP_IN_EXPR[2] |
|
274 | assert tf.transform(HELP_IN_EXPR[0]) == HELP_IN_EXPR[2] | |
@@ -246,10 +282,12 b' def test_transform_help():' | |||||
246 | tf = ipt2.HelpEnd((1, 0), (1, 0)) |
|
282 | tf = ipt2.HelpEnd((1, 0), (1, 0)) | |
247 | assert tf.transform(HELP_UNICODE[0]) == HELP_UNICODE[2] |
|
283 | assert tf.transform(HELP_UNICODE[0]) == HELP_UNICODE[2] | |
248 |
|
284 | |||
|
285 | ||||
249 | def test_find_assign_op_dedent(): |
|
286 | def test_find_assign_op_dedent(): | |
250 | """ |
|
287 | """ | |
251 | be careful that empty token like dedent are not counted as parens |
|
288 | be careful that empty token like dedent are not counted as parens | |
252 | """ |
|
289 | """ | |
|
290 | ||||
253 | class Tk: |
|
291 | class Tk: | |
254 | def __init__(self, s): |
|
292 | def __init__(self, s): | |
255 | self.string = s |
|
293 | self.string = s | |
@@ -302,21 +340,23 b' def test_check_complete_param(code, expected, number):' | |||||
302 | def test_check_complete(): |
|
340 | def test_check_complete(): | |
303 | cc = ipt2.TransformerManager().check_complete |
|
341 | cc = ipt2.TransformerManager().check_complete | |
304 |
|
342 | |||
305 |
example = dedent( |
|
343 | example = dedent( | |
|
344 | """ | |||
306 |
|
|
345 | if True: | |
307 |
|
|
346 | a=1""" | |
|
347 | ) | |||
308 |
|
348 | |||
309 | assert cc(example) == ("incomplete", 4) |
|
349 | assert cc(example) == ("incomplete", 4) | |
310 | assert cc(example + "\n") == ("complete", None) |
|
350 | assert cc(example + "\n") == ("complete", None) | |
311 | assert cc(example + "\n ") == ("complete", None) |
|
351 | assert cc(example + "\n ") == ("complete", None) | |
312 |
|
352 | |||
313 | # no need to loop on all the letters/numbers. |
|
353 | # no need to loop on all the letters/numbers. | |
314 |
short = |
|
354 | short = "12abAB" + string.printable[62:] | |
315 | for c in short: |
|
355 | for c in short: | |
316 | # test does not raise: |
|
356 | # test does not raise: | |
317 | cc(c) |
|
357 | cc(c) | |
318 | for k in short: |
|
358 | for k in short: | |
319 | cc(c+k) |
|
359 | cc(c + k) | |
320 |
|
360 | |||
321 | assert cc("def f():\n x=0\n \\\n ") == ("incomplete", 2) |
|
361 | assert cc("def f():\n x=0\n \\\n ") == ("incomplete", 2) | |
322 |
|
362 | |||
@@ -371,10 +411,9 b' def test_null_cleanup_transformer():' | |||||
371 | assert manager.transform_cell("") == "" |
|
411 | assert manager.transform_cell("") == "" | |
372 |
|
412 | |||
373 |
|
413 | |||
374 |
|
||||
375 |
|
||||
376 | def test_side_effects_I(): |
|
414 | def test_side_effects_I(): | |
377 | count = 0 |
|
415 | count = 0 | |
|
416 | ||||
378 | def counter(lines): |
|
417 | def counter(lines): | |
379 | nonlocal count |
|
418 | nonlocal count | |
380 | count += 1 |
|
419 | count += 1 | |
@@ -384,14 +423,13 b' def test_side_effects_I():' | |||||
384 |
|
423 | |||
385 | manager = ipt2.TransformerManager() |
|
424 | manager = ipt2.TransformerManager() | |
386 | manager.cleanup_transforms.insert(0, counter) |
|
425 | manager.cleanup_transforms.insert(0, counter) | |
387 |
assert manager.check_complete("a=1\n") == ( |
|
426 | assert manager.check_complete("a=1\n") == ("complete", None) | |
388 | assert count == 0 |
|
427 | assert count == 0 | |
389 |
|
428 | |||
390 |
|
429 | |||
391 |
|
||||
392 |
|
||||
393 | def test_side_effects_II(): |
|
430 | def test_side_effects_II(): | |
394 | count = 0 |
|
431 | count = 0 | |
|
432 | ||||
395 | def counter(lines): |
|
433 | def counter(lines): | |
396 | nonlocal count |
|
434 | nonlocal count | |
397 | count += 1 |
|
435 | count += 1 | |
@@ -401,5 +439,5 b' def test_side_effects_II():' | |||||
401 |
|
439 | |||
402 | manager = ipt2.TransformerManager() |
|
440 | manager = ipt2.TransformerManager() | |
403 | manager.line_transforms.insert(0, counter) |
|
441 | manager.line_transforms.insert(0, counter) | |
404 |
assert manager.check_complete("b=1\n") == ( |
|
442 | assert manager.check_complete("b=1\n") == ("complete", None) | |
405 | assert count == 0 |
|
443 | assert count == 0 |
@@ -380,7 +380,8 b' class InteractiveShellTestCase(unittest.TestCase):' | |||||
380 | class A(object): |
|
380 | class A(object): | |
381 | @property |
|
381 | @property | |
382 | def foo(self): |
|
382 | def foo(self): | |
383 | raise NotImplementedError() |
|
383 | raise NotImplementedError() # pragma: no cover | |
|
384 | ||||
384 | a = A() |
|
385 | a = A() | |
385 |
|
386 | |||
386 | found = ip._ofind('a.foo', [('locals', locals())]) |
|
387 | found = ip._ofind('a.foo', [('locals', locals())]) | |
@@ -392,7 +393,7 b' class InteractiveShellTestCase(unittest.TestCase):' | |||||
392 | class A(object): |
|
393 | class A(object): | |
393 | @property |
|
394 | @property | |
394 | def foo(self): |
|
395 | def foo(self): | |
395 | raise NotImplementedError() |
|
396 | raise NotImplementedError() # pragma: no cover | |
396 |
|
397 | |||
397 | a = A() |
|
398 | a = A() | |
398 | a.a = A() |
|
399 | a.a = A() | |
@@ -546,7 +547,7 b' class TestSafeExecfileNonAsciiPath(unittest.TestCase):' | |||||
546 | self.TESTDIR = join(self.BASETESTDIR, u"åäö") |
|
547 | self.TESTDIR = join(self.BASETESTDIR, u"åäö") | |
547 | os.mkdir(self.TESTDIR) |
|
548 | os.mkdir(self.TESTDIR) | |
548 | with open( |
|
549 | with open( | |
549 |
join(self.TESTDIR, |
|
550 | join(self.TESTDIR, "åäötestscript.py"), "w", encoding="utf-8" | |
550 | ) as sfile: |
|
551 | ) as sfile: | |
551 | sfile.write("pass\n") |
|
552 | sfile.write("pass\n") | |
552 | self.oldpath = os.getcwd() |
|
553 | self.oldpath = os.getcwd() | |
@@ -585,9 +586,9 b' class ExitCodeChecks(tt.TempFileMixin):' | |||||
585 | self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM) |
|
586 | self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM) | |
586 |
|
587 | |||
587 | @onlyif_cmds_exist("csh") |
|
588 | @onlyif_cmds_exist("csh") | |
588 | def test_exit_code_signal_csh(self): |
|
589 | def test_exit_code_signal_csh(self): # pragma: no cover | |
589 |
SHELL = os.environ.get( |
|
590 | SHELL = os.environ.get("SHELL", None) | |
590 |
os.environ[ |
|
591 | os.environ["SHELL"] = find_cmd("csh") | |
591 | try: |
|
592 | try: | |
592 | self.test_exit_code_signal() |
|
593 | self.test_exit_code_signal() | |
593 | finally: |
|
594 | finally: | |
@@ -615,7 +616,7 b' class TestSystemRaw(ExitCodeChecks):' | |||||
615 | def test_control_c(self, *mocks): |
|
616 | def test_control_c(self, *mocks): | |
616 | try: |
|
617 | try: | |
617 | self.system("sleep 1 # wont happen") |
|
618 | self.system("sleep 1 # wont happen") | |
618 | except KeyboardInterrupt: |
|
619 | except KeyboardInterrupt: # pragma: no cove | |
619 | self.fail( |
|
620 | self.fail( | |
620 | "system call should intercept " |
|
621 | "system call should intercept " | |
621 | "keyboard interrupt from subprocess.call" |
|
622 | "keyboard interrupt from subprocess.call" | |
@@ -623,7 +624,7 b' class TestSystemRaw(ExitCodeChecks):' | |||||
623 | self.assertEqual(ip.user_ns["_exit_code"], -signal.SIGINT) |
|
624 | self.assertEqual(ip.user_ns["_exit_code"], -signal.SIGINT) | |
624 |
|
625 | |||
625 | def test_magic_warnings(self): |
|
626 | def test_magic_warnings(self): | |
626 |
for magic_cmd in ( |
|
627 | for magic_cmd in ("pip", "conda", "cd"): | |
627 | with self.assertWarnsRegex(Warning, "You executed the system command"): |
|
628 | with self.assertWarnsRegex(Warning, "You executed the system command"): | |
628 | ip.system_raw(magic_cmd) |
|
629 | ip.system_raw(magic_cmd) | |
629 |
|
630 | |||
@@ -679,16 +680,20 b' class TestAstTransform(unittest.TestCase):' | |||||
679 |
|
680 | |||
680 | def tearDown(self): |
|
681 | def tearDown(self): | |
681 | ip.ast_transformers.remove(self.negator) |
|
682 | ip.ast_transformers.remove(self.negator) | |
682 |
|
683 | |||
|
684 | def test_non_int_const(self): | |||
|
685 | with tt.AssertPrints("hello"): | |||
|
686 | ip.run_cell('print("hello")') | |||
|
687 | ||||
683 | def test_run_cell(self): |
|
688 | def test_run_cell(self): | |
684 |
with tt.AssertPrints( |
|
689 | with tt.AssertPrints("-34"): | |
685 |
ip.run_cell( |
|
690 | ip.run_cell("print(12 + 22)") | |
686 |
|
691 | |||
687 | # A named reference to a number shouldn't be transformed. |
|
692 | # A named reference to a number shouldn't be transformed. | |
688 |
ip.user_ns[ |
|
693 | ip.user_ns["n"] = 55 | |
689 |
with tt.AssertNotPrints( |
|
694 | with tt.AssertNotPrints("-55"): | |
690 |
ip.run_cell( |
|
695 | ip.run_cell("print(n)") | |
691 |
|
696 | |||
692 | def test_timeit(self): |
|
697 | def test_timeit(self): | |
693 | called = set() |
|
698 | called = set() | |
694 | def f(x): |
|
699 | def f(x): | |
@@ -796,7 +801,11 b' class TestAstTransform2(unittest.TestCase):' | |||||
796 | # This shouldn't throw an error |
|
801 | # This shouldn't throw an error | |
797 | ip.run_cell("o = 2.0") |
|
802 | ip.run_cell("o = 2.0") | |
798 | self.assertEqual(ip.user_ns['o'], 2.0) |
|
803 | self.assertEqual(ip.user_ns['o'], 2.0) | |
799 |
|
804 | |||
|
805 | def test_run_cell_non_int(self): | |||
|
806 | ip.run_cell("n = 'a'") | |||
|
807 | assert self.calls == [] | |||
|
808 | ||||
800 | def test_timeit(self): |
|
809 | def test_timeit(self): | |
801 | called = set() |
|
810 | called = set() | |
802 | def f(x): |
|
811 | def f(x): | |
@@ -815,14 +824,9 b' class TestAstTransform2(unittest.TestCase):' | |||||
815 | class ErrorTransformer(ast.NodeTransformer): |
|
824 | class ErrorTransformer(ast.NodeTransformer): | |
816 | """Throws an error when it sees a number.""" |
|
825 | """Throws an error when it sees a number.""" | |
817 |
|
826 | |||
818 | # for Python 3.7 and earlier |
|
|||
819 | def visit_Num(self, node): |
|
|||
820 | raise ValueError("test") |
|
|||
821 |
|
||||
822 | # for Python 3.8+ |
|
|||
823 | def visit_Constant(self, node): |
|
827 | def visit_Constant(self, node): | |
824 | if isinstance(node.value, int): |
|
828 | if isinstance(node.value, int): | |
825 | return self.visit_Num(node) |
|
829 | raise ValueError("test") | |
826 | return node |
|
830 | return node | |
827 |
|
831 | |||
828 |
|
832 | |||
@@ -845,10 +849,6 b' class StringRejector(ast.NodeTransformer):' | |||||
845 | not be executed by throwing an InputRejected. |
|
849 | not be executed by throwing an InputRejected. | |
846 | """ |
|
850 | """ | |
847 |
|
851 | |||
848 | #for python 3.7 and earlier |
|
|||
849 | def visit_Str(self, node): |
|
|||
850 | raise InputRejected("test") |
|
|||
851 |
|
||||
852 | # 3.8 only |
|
852 | # 3.8 only | |
853 | def visit_Constant(self, node): |
|
853 | def visit_Constant(self, node): | |
854 | if isinstance(node.value, str): |
|
854 | if isinstance(node.value, str): |
@@ -236,7 +236,8 b' def test_run_cell():' | |||||
236 | if 4: |
|
236 | if 4: | |
237 | print "bar" |
|
237 | print "bar" | |
238 |
|
238 | |||
239 |
""" |
|
239 | """ | |
|
240 | ) | |||
240 | # Simply verifies that this kind of input is run |
|
241 | # Simply verifies that this kind of input is run | |
241 | ip.run_cell(complex) |
|
242 | ip.run_cell(complex) | |
242 |
|
243 |
@@ -2,9 +2,10 b'' | |||||
2 | """Test IPython.core.logger""" |
|
2 | """Test IPython.core.logger""" | |
3 |
|
3 | |||
4 | import os.path |
|
4 | import os.path | |
|
5 | ||||
5 | import pytest |
|
6 | import pytest | |
|
7 | from tempfile import TemporaryDirectory | |||
6 |
|
8 | |||
7 | from IPython.utils.tempdir import TemporaryDirectory |
|
|||
8 |
|
9 | |||
9 | def test_logstart_inaccessible_file(): |
|
10 | def test_logstart_inaccessible_file(): | |
10 | with pytest.raises(IOError): |
|
11 | with pytest.raises(IOError): |
@@ -448,7 +448,9 b' def test_multiline_time():' | |||||
448 | ip = get_ipython() |
|
448 | ip = get_ipython() | |
449 | ip.user_ns.pop('run', None) |
|
449 | ip.user_ns.pop('run', None) | |
450 |
|
450 | |||
451 |
ip.run_cell( |
|
451 | ip.run_cell( | |
|
452 | dedent( | |||
|
453 | """\ | |||
452 | %%time |
|
454 | %%time | |
453 | a = "ho" |
|
455 | a = "ho" | |
454 | b = "hey" |
|
456 | b = "hey" |
@@ -122,7 +122,8 b' class PasteTestCase(TestCase):' | |||||
122 | ip.user_ns.pop("x") |
|
122 | ip.user_ns.pop("x") | |
123 |
|
123 | |||
124 | def test_paste_py_multi(self): |
|
124 | def test_paste_py_multi(self): | |
125 |
self.paste( |
|
125 | self.paste( | |
|
126 | """ | |||
126 |
|
|
127 | >>> x = [1,2,3] | |
127 |
|
|
128 | >>> y = [] | |
128 |
|
|
129 | >>> for i in x: | |
@@ -145,7 +146,8 b' class PasteTestCase(TestCase):' | |||||
145 |
|
146 | |||
146 | def test_paste_email(self): |
|
147 | def test_paste_email(self): | |
147 | "Test pasting of email-quoted contents" |
|
148 | "Test pasting of email-quoted contents" | |
148 |
self.paste( |
|
149 | self.paste( | |
|
150 | """\ | |||
149 |
|
|
151 | >> def foo(x): | |
150 |
|
|
152 | >> return x + 1 | |
151 |
|
|
153 | >> xx = foo(1.1)""" | |
@@ -154,7 +156,8 b' class PasteTestCase(TestCase):' | |||||
154 |
|
156 | |||
155 | def test_paste_email2(self): |
|
157 | def test_paste_email2(self): | |
156 | "Email again; some programs add a space also at each quoting level" |
|
158 | "Email again; some programs add a space also at each quoting level" | |
157 |
self.paste( |
|
159 | self.paste( | |
|
160 | """\ | |||
158 |
|
|
161 | > > def foo(x): | |
159 |
|
|
162 | > > return x + 1 | |
160 |
|
|
163 | > > yy = foo(2.1) """ | |
@@ -163,7 +166,8 b' class PasteTestCase(TestCase):' | |||||
163 |
|
166 | |||
164 | def test_paste_email_py(self): |
|
167 | def test_paste_email_py(self): | |
165 | "Email quoting of interactive input" |
|
168 | "Email quoting of interactive input" | |
166 |
self.paste( |
|
169 | self.paste( | |
|
170 | """\ | |||
167 |
|
|
171 | >> >>> def f(x): | |
168 |
|
|
172 | >> ... return x+1 | |
169 |
|
|
173 | >> ... |
@@ -6,11 +6,11 b' import tempfile' | |||||
6 | import warnings |
|
6 | import warnings | |
7 | from unittest.mock import patch |
|
7 | from unittest.mock import patch | |
8 |
|
8 | |||
9 | from testpath import modified_env, assert_isdir, assert_isfile |
|
9 | from tempfile import TemporaryDirectory | |
|
10 | from testpath import assert_isdir, assert_isfile, modified_env | |||
10 |
|
11 | |||
11 | from IPython import paths |
|
12 | from IPython import paths | |
12 | from IPython.testing.decorators import skip_win32 |
|
13 | from IPython.testing.decorators import skip_win32 | |
13 | from IPython.utils.tempdir import TemporaryDirectory |
|
|||
14 |
|
14 | |||
15 | TMP_TEST_DIR = os.path.realpath(tempfile.mkdtemp()) |
|
15 | TMP_TEST_DIR = os.path.realpath(tempfile.mkdtemp()) | |
16 | HOME_TEST_DIR = os.path.join(TMP_TEST_DIR, "home_test_dir") |
|
16 | HOME_TEST_DIR = os.path.join(TMP_TEST_DIR, "home_test_dir") |
@@ -23,17 +23,16 b' Authors' | |||||
23 | import shutil |
|
23 | import shutil | |
24 | import sys |
|
24 | import sys | |
25 | import tempfile |
|
25 | import tempfile | |
26 |
|
||||
27 | from pathlib import Path |
|
26 | from pathlib import Path | |
28 | from unittest import TestCase |
|
27 | from unittest import TestCase | |
29 |
|
28 | |||
30 | from IPython.core.profileapp import list_profiles_in, list_bundled_profiles |
|
29 | from tempfile import TemporaryDirectory | |
31 | from IPython.core.profiledir import ProfileDir |
|
|||
32 |
|
30 | |||
|
31 | from IPython.core.profileapp import list_bundled_profiles, list_profiles_in | |||
|
32 | from IPython.core.profiledir import ProfileDir | |||
33 | from IPython.testing import decorators as dec |
|
33 | from IPython.testing import decorators as dec | |
34 | from IPython.testing import tools as tt |
|
34 | from IPython.testing import tools as tt | |
35 | from IPython.utils.process import getoutput |
|
35 | from IPython.utils.process import getoutput | |
36 | from IPython.utils.tempdir import TemporaryDirectory |
|
|||
37 |
|
36 | |||
38 | #----------------------------------------------------------------------------- |
|
37 | #----------------------------------------------------------------------------- | |
39 | # Globals |
|
38 | # Globals | |
@@ -109,7 +108,7 b' def test_list_profiles_in():' | |||||
109 | for name in ("profile_foo", "profile_hello", "not_a_profile"): |
|
108 | for name in ("profile_foo", "profile_hello", "not_a_profile"): | |
110 | Path(td / name).mkdir(parents=True) |
|
109 | Path(td / name).mkdir(parents=True) | |
111 | if dec.unicode_paths: |
|
110 | if dec.unicode_paths: | |
112 |
Path(td / |
|
111 | Path(td / "profile_ünicode").mkdir(parents=True) | |
113 |
|
112 | |||
114 | with open(td / "profile_file", "w", encoding="utf-8") as f: |
|
113 | with open(td / "profile_file", "w", encoding="utf-8") as f: | |
115 | f.write("I am not a profile directory") |
|
114 | f.write("I am not a profile directory") |
@@ -19,21 +19,22 b' as otherwise it may influence later tests.' | |||||
19 | import functools |
|
19 | import functools | |
20 | import os |
|
20 | import os | |
21 | import platform |
|
21 | import platform | |
22 | from os.path import join as pjoin |
|
|||
23 | import random |
|
22 | import random | |
24 | import string |
|
23 | import string | |
25 | import sys |
|
24 | import sys | |
26 | import textwrap |
|
25 | import textwrap | |
27 | import unittest |
|
26 | import unittest | |
|
27 | from os.path import join as pjoin | |||
28 | from unittest.mock import patch |
|
28 | from unittest.mock import patch | |
29 |
|
29 | |||
30 | import pytest |
|
30 | import pytest | |
|
31 | from tempfile import TemporaryDirectory | |||
31 |
|
32 | |||
|
33 | from IPython.core import debugger | |||
32 | from IPython.testing import decorators as dec |
|
34 | from IPython.testing import decorators as dec | |
33 | from IPython.testing import tools as tt |
|
35 | from IPython.testing import tools as tt | |
34 | from IPython.utils.io import capture_output |
|
36 | from IPython.utils.io import capture_output | |
35 | from IPython.utils.tempdir import TemporaryDirectory |
|
37 | ||
36 | from IPython.core import debugger |
|
|||
37 |
|
38 | |||
38 | def doctest_refbug(): |
|
39 | def doctest_refbug(): | |
39 | """Very nasty problem with references held by multiple runs of a script. |
|
40 | """Very nasty problem with references held by multiple runs of a script. | |
@@ -411,6 +412,7 b' tclass.py: deleting object: C-third' | |||||
411 | """Test %run notebook.ipynb error""" |
|
412 | """Test %run notebook.ipynb error""" | |
412 | pytest.importorskip("nbformat") |
|
413 | pytest.importorskip("nbformat") | |
413 | from nbformat import v4, writes |
|
414 | from nbformat import v4, writes | |
|
415 | ||||
414 | # %run when a file name isn't provided |
|
416 | # %run when a file name isn't provided | |
415 | pytest.raises(Exception, _ip.magic, "run") |
|
417 | pytest.raises(Exception, _ip.magic, "run") | |
416 |
|
418 |
@@ -3,21 +3,20 b'' | |||||
3 | """ |
|
3 | """ | |
4 | import io |
|
4 | import io | |
5 | import logging |
|
5 | import logging | |
|
6 | import os.path | |||
6 | import platform |
|
7 | import platform | |
7 | import re |
|
8 | import re | |
8 | import sys |
|
9 | import sys | |
9 | import os.path |
|
|||
10 | from textwrap import dedent |
|
|||
11 | import traceback |
|
10 | import traceback | |
12 | import unittest |
|
11 | import unittest | |
|
12 | from textwrap import dedent | |||
13 |
|
13 | |||
14 | from IPython.core.ultratb import ColorTB, VerboseTB |
|
14 | from tempfile import TemporaryDirectory | |
15 |
|
||||
16 |
|
15 | |||
|
16 | from IPython.core.ultratb import ColorTB, VerboseTB | |||
17 | from IPython.testing import tools as tt |
|
17 | from IPython.testing import tools as tt | |
18 | from IPython.testing.decorators import onlyif_unicode_paths |
|
18 | from IPython.testing.decorators import onlyif_unicode_paths | |
19 | from IPython.utils.syspathcontext import prepended_to_syspath |
|
19 | from IPython.utils.syspathcontext import prepended_to_syspath | |
20 | from IPython.utils.tempdir import TemporaryDirectory |
|
|||
21 |
|
20 | |||
22 | file_1 = """1 |
|
21 | file_1 = """1 | |
23 | 2 |
|
22 | 2 |
@@ -207,7 +207,16 b' class TBTools(colorable.Colorable):' | |||||
207 | # Number of frames to skip when reporting tracebacks |
|
207 | # Number of frames to skip when reporting tracebacks | |
208 | tb_offset = 0 |
|
208 | tb_offset = 0 | |
209 |
|
209 | |||
210 | def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None, parent=None, config=None): |
|
210 | def __init__( | |
|
211 | self, | |||
|
212 | color_scheme="NoColor", | |||
|
213 | call_pdb=False, | |||
|
214 | ostream=None, | |||
|
215 | parent=None, | |||
|
216 | config=None, | |||
|
217 | *, | |||
|
218 | debugger_cls=None, | |||
|
219 | ): | |||
211 | # Whether to call the interactive pdb debugger after printing |
|
220 | # Whether to call the interactive pdb debugger after printing | |
212 | # tracebacks or not |
|
221 | # tracebacks or not | |
213 | super(TBTools, self).__init__(parent=parent, config=config) |
|
222 | super(TBTools, self).__init__(parent=parent, config=config) | |
@@ -227,9 +236,10 b' class TBTools(colorable.Colorable):' | |||||
227 |
|
236 | |||
228 | self.set_colors(color_scheme) |
|
237 | self.set_colors(color_scheme) | |
229 | self.old_scheme = color_scheme # save initial value for toggles |
|
238 | self.old_scheme = color_scheme # save initial value for toggles | |
|
239 | self.debugger_cls = debugger_cls or debugger.Pdb | |||
230 |
|
240 | |||
231 | if call_pdb: |
|
241 | if call_pdb: | |
232 |
self.pdb = debugger |
|
242 | self.pdb = debugger_cls() | |
233 | else: |
|
243 | else: | |
234 | self.pdb = None |
|
244 | self.pdb = None | |
235 |
|
245 | |||
@@ -350,9 +360,6 b' class ListTB(TBTools):' | |||||
350 | Because they are meant to be called without a full traceback (only a |
|
360 | Because they are meant to be called without a full traceback (only a | |
351 | list), instances of this class can't call the interactive pdb debugger.""" |
|
361 | list), instances of this class can't call the interactive pdb debugger.""" | |
352 |
|
362 | |||
353 | def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None, parent=None, config=None): |
|
|||
354 | TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb, |
|
|||
355 | ostream=ostream, parent=parent,config=config) |
|
|||
356 |
|
363 | |||
357 | def __call__(self, etype, value, elist): |
|
364 | def __call__(self, etype, value, elist): | |
358 | self.ostream.flush() |
|
365 | self.ostream.flush() | |
@@ -628,8 +635,15 b' class VerboseTB(TBTools):' | |||||
628 | tb_offset=1 allows use of this handler in interpreters which will have |
|
635 | tb_offset=1 allows use of this handler in interpreters which will have | |
629 | their own code at the top of the traceback (VerboseTB will first |
|
636 | their own code at the top of the traceback (VerboseTB will first | |
630 | remove that frame before printing the traceback info).""" |
|
637 | remove that frame before printing the traceback info).""" | |
631 | TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb, |
|
638 | TBTools.__init__( | |
632 | ostream=ostream, parent=parent, config=config) |
|
639 | self, | |
|
640 | color_scheme=color_scheme, | |||
|
641 | call_pdb=call_pdb, | |||
|
642 | ostream=ostream, | |||
|
643 | parent=parent, | |||
|
644 | config=config, | |||
|
645 | debugger_cls=debugger_cls, | |||
|
646 | ) | |||
633 | self.tb_offset = tb_offset |
|
647 | self.tb_offset = tb_offset | |
634 | self.long_header = long_header |
|
648 | self.long_header = long_header | |
635 | self.include_vars = include_vars |
|
649 | self.include_vars = include_vars | |
@@ -642,7 +656,6 b' class VerboseTB(TBTools):' | |||||
642 | check_cache = linecache.checkcache |
|
656 | check_cache = linecache.checkcache | |
643 | self.check_cache = check_cache |
|
657 | self.check_cache = check_cache | |
644 |
|
658 | |||
645 | self.debugger_cls = debugger_cls or debugger.Pdb |
|
|||
646 | self.skip_hidden = True |
|
659 | self.skip_hidden = True | |
647 |
|
660 | |||
648 | def format_record(self, frame_info): |
|
661 | def format_record(self, frame_info): | |
@@ -763,7 +776,7 b' class VerboseTB(TBTools):' | |||||
763 | self, |
|
776 | self, | |
764 | etype: type, |
|
777 | etype: type, | |
765 | evalue: BaseException, |
|
778 | evalue: BaseException, | |
766 | etb: TracebackType, |
|
779 | etb: Optional[TracebackType], | |
767 | number_of_lines_of_context, |
|
780 | number_of_lines_of_context, | |
768 | tb_offset: Optional[int], |
|
781 | tb_offset: Optional[int], | |
769 | ): |
|
782 | ): | |
@@ -772,7 +785,6 b' class VerboseTB(TBTools):' | |||||
772 | This may be called multiple times by Python 3 exception chaining |
|
785 | This may be called multiple times by Python 3 exception chaining | |
773 | (PEP 3134). |
|
786 | (PEP 3134). | |
774 | """ |
|
787 | """ | |
775 | assert etb is not None |
|
|||
776 | # some locals |
|
788 | # some locals | |
777 | orig_etype = etype |
|
789 | orig_etype = etype | |
778 | try: |
|
790 | try: | |
@@ -783,7 +795,9 b' class VerboseTB(TBTools):' | |||||
783 | tb_offset = self.tb_offset if tb_offset is None else tb_offset |
|
795 | tb_offset = self.tb_offset if tb_offset is None else tb_offset | |
784 | assert isinstance(tb_offset, int) |
|
796 | assert isinstance(tb_offset, int) | |
785 | head = self.prepare_header(etype, self.long_header) |
|
797 | head = self.prepare_header(etype, self.long_header) | |
786 | records = self.get_records(etb, number_of_lines_of_context, tb_offset) |
|
798 | records = ( | |
|
799 | self.get_records(etb, number_of_lines_of_context, tb_offset) if etb else [] | |||
|
800 | ) | |||
787 |
|
801 | |||
788 | frames = [] |
|
802 | frames = [] | |
789 | skipped = 0 |
|
803 | skipped = 0 | |
@@ -822,6 +836,7 b' class VerboseTB(TBTools):' | |||||
822 | def get_records( |
|
836 | def get_records( | |
823 | self, etb: TracebackType, number_of_lines_of_context: int, tb_offset: int |
|
837 | self, etb: TracebackType, number_of_lines_of_context: int, tb_offset: int | |
824 | ): |
|
838 | ): | |
|
839 | assert etb is not None | |||
825 | context = number_of_lines_of_context - 1 |
|
840 | context = number_of_lines_of_context - 1 | |
826 | after = context // 2 |
|
841 | after = context // 2 | |
827 | before = context - after |
|
842 | before = context - after | |
@@ -836,19 +851,17 b' class VerboseTB(TBTools):' | |||||
836 | after=after, |
|
851 | after=after, | |
837 | pygments_formatter=formatter, |
|
852 | pygments_formatter=formatter, | |
838 | ) |
|
853 | ) | |
839 | assert etb is not None |
|
|||
840 | return list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:] |
|
854 | return list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:] | |
841 |
|
855 | |||
842 | def structured_traceback( |
|
856 | def structured_traceback( | |
843 | self, |
|
857 | self, | |
844 | etype: type, |
|
858 | etype: type, | |
845 | evalue: Optional[BaseException], |
|
859 | evalue: Optional[BaseException], | |
846 | etb: TracebackType, |
|
860 | etb: Optional[TracebackType], | |
847 | tb_offset: Optional[int] = None, |
|
861 | tb_offset: Optional[int] = None, | |
848 | number_of_lines_of_context: int = 5, |
|
862 | number_of_lines_of_context: int = 5, | |
849 | ): |
|
863 | ): | |
850 | """Return a nice text document describing the traceback.""" |
|
864 | """Return a nice text document describing the traceback.""" | |
851 | assert etb is not None |
|
|||
852 | formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context, |
|
865 | formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context, | |
853 | tb_offset) |
|
866 | tb_offset) | |
854 |
|
867 |
@@ -33,7 +33,7 b' from IPython.testing.decorators import skipif_not_numpy' | |||||
33 |
|
33 | |||
34 | if platform.python_implementation() == "PyPy": |
|
34 | if platform.python_implementation() == "PyPy": | |
35 | pytest.skip( |
|
35 | pytest.skip( | |
36 | "Current autoreload implementation is extremly slow on PyPy", |
|
36 | "Current autoreload implementation is extremely slow on PyPy", | |
37 | allow_module_level=True, |
|
37 | allow_module_level=True, | |
38 | ) |
|
38 | ) | |
39 |
|
39 |
@@ -66,7 +66,9 b' class ImportDenier(importlib.abc.MetaPathFinder):' | |||||
66 | """ |
|
66 | """ | |
67 | Importing %s disabled by IPython, which has |
|
67 | Importing %s disabled by IPython, which has | |
68 | already imported an Incompatible QT Binding: %s |
|
68 | already imported an Incompatible QT Binding: %s | |
69 | """ % (fullname, loaded_api())) |
|
69 | """ | |
|
70 | % (fullname, loaded_api()) | |||
|
71 | ) | |||
70 |
|
72 | |||
71 |
|
73 | |||
72 | ID = ImportDenier() |
|
74 | ID = ImportDenier() |
@@ -9,13 +9,3 b' Extra capabilities for IPython' | |||||
9 | # Distributed under the terms of the BSD License. The full license is in |
|
9 | # Distributed under the terms of the BSD License. The full license is in | |
10 | # the file COPYING, distributed as part of this software. |
|
10 | # the file COPYING, distributed as part of this software. | |
11 | #----------------------------------------------------------------------------- |
|
11 | #----------------------------------------------------------------------------- | |
12 |
|
||||
13 | #----------------------------------------------------------------------------- |
|
|||
14 | # Imports |
|
|||
15 | #----------------------------------------------------------------------------- |
|
|||
16 |
|
||||
17 | from IPython.lib.security import passwd |
|
|||
18 |
|
||||
19 | #----------------------------------------------------------------------------- |
|
|||
20 | # Code |
|
|||
21 | #----------------------------------------------------------------------------- |
|
@@ -92,8 +92,8 b' class Audio(DisplayObject):' | |||||
92 |
|
92 | |||
93 | From a File: |
|
93 | From a File: | |
94 |
|
94 | |||
95 |
>>> Audio(' |
|
95 | >>> Audio('IPython/lib/tests/test.wav') # doctest: +SKIP | |
96 |
>>> Audio(filename=' |
|
96 | >>> Audio(filename='IPython/lib/tests/test.wav') # doctest: +SKIP | |
97 |
|
97 | |||
98 | From Bytes: |
|
98 | From Bytes: | |
99 |
|
99 | |||
@@ -103,9 +103,9 b' class Audio(DisplayObject):' | |||||
103 | See Also |
|
103 | See Also | |
104 | -------- |
|
104 | -------- | |
105 | ipywidgets.Audio |
|
105 | ipywidgets.Audio | |
106 |
|
106 | |||
107 |
A |
|
107 | Audio widget with more more flexibility and options. | |
108 |
|
108 | |||
109 | """ |
|
109 | """ | |
110 | _read_flags = 'rb' |
|
110 | _read_flags = 'rb' | |
111 |
|
111 | |||
@@ -510,12 +510,10 b' class FileLinks(FileLink):' | |||||
510 |
|
510 | |||
511 | self.recursive = recursive |
|
511 | self.recursive = recursive | |
512 |
|
512 | |||
513 |
def _get_display_formatter( |
|
513 | def _get_display_formatter( | |
514 | dirname_output_format, |
|
514 | self, dirname_output_format, fname_output_format, fp_format, fp_cleaner=None | |
515 | fname_output_format, |
|
515 | ): | |
516 | fp_format, |
|
516 | """generate built-in formatter function | |
517 | fp_cleaner=None): |
|
|||
518 | """ generate built-in formatter function |
|
|||
519 |
|
517 | |||
520 | this is used to define both the notebook and terminal built-in |
|
518 | this is used to define both the notebook and terminal built-in | |
521 | formatters as they only differ by some wrapper text for each entry |
|
519 | formatters as they only differ by some wrapper text for each entry |
@@ -4,14 +4,15 b'' | |||||
4 | # Copyright (c) IPython Development Team. |
|
4 | # Copyright (c) IPython Development Team. | |
5 | # Distributed under the terms of the Modified BSD License. |
|
5 | # Distributed under the terms of the Modified BSD License. | |
6 |
|
6 | |||
7 | import pytest |
|
|||
8 | import types |
|
7 | import types | |
9 |
|
||||
10 | from pathlib import Path |
|
8 | from pathlib import Path | |
11 |
|
9 | |||
|
10 | import pytest | |||
|
11 | from tempfile import TemporaryDirectory | |||
|
12 | ||||
|
13 | from IPython.lib.deepreload import modules_reloading | |||
|
14 | from IPython.lib.deepreload import reload as dreload | |||
12 | from IPython.utils.syspathcontext import prepended_to_syspath |
|
15 | from IPython.utils.syspathcontext import prepended_to_syspath | |
13 | from IPython.utils.tempdir import TemporaryDirectory |
|
|||
14 | from IPython.lib.deepreload import reload as dreload, modules_reloading |
|
|||
15 |
|
16 | |||
16 |
|
17 | |||
17 | def test_deepreload(): |
|
18 | def test_deepreload(): |
@@ -31,7 +31,7 b' def no_op(*args, **kwargs):' | |||||
31 |
|
31 | |||
32 |
|
32 | |||
33 | @onlyif_cmds_exist("latex", "dvipng") |
|
33 | @onlyif_cmds_exist("latex", "dvipng") | |
34 |
@pytest.mark.parametrize("s, wrap", [( |
|
34 | @pytest.mark.parametrize("s, wrap", [("$$x^2$$", False), ("x^2", True)]) | |
35 | def test_latex_to_png_dvipng_runs(s, wrap): |
|
35 | def test_latex_to_png_dvipng_runs(s, wrap): | |
36 | """ |
|
36 | """ | |
37 | Test that latex_to_png_dvipng just runs without error. |
|
37 | Test that latex_to_png_dvipng just runs without error. |
@@ -273,7 +273,7 b' def test_unicode_repr():' | |||||
273 | p = pretty.pretty(c) |
|
273 | p = pretty.pretty(c) | |
274 | assert p == u |
|
274 | assert p == u | |
275 | p = pretty.pretty([c]) |
|
275 | p = pretty.pretty([c]) | |
276 |
assert p == |
|
276 | assert p == "[%s]" % u | |
277 |
|
277 | |||
278 |
|
278 | |||
279 | def test_basic_class(): |
|
279 | def test_basic_class(): |
@@ -220,6 +220,8 b' except Exception:' | |||||
220 | # for tokenizing blocks |
|
220 | # for tokenizing blocks | |
221 | COMMENT, INPUT, OUTPUT = range(3) |
|
221 | COMMENT, INPUT, OUTPUT = range(3) | |
222 |
|
222 | |||
|
223 | PSEUDO_DECORATORS = ["suppress", "verbatim", "savefig", "doctest"] | |||
|
224 | ||||
223 | #----------------------------------------------------------------------------- |
|
225 | #----------------------------------------------------------------------------- | |
224 | # Functions and class declarations |
|
226 | # Functions and class declarations | |
225 | #----------------------------------------------------------------------------- |
|
227 | #----------------------------------------------------------------------------- | |
@@ -263,11 +265,17 b' def block_parser(part, rgxin, rgxout, fmtin, fmtout):' | |||||
263 | block.append((COMMENT, line)) |
|
265 | block.append((COMMENT, line)) | |
264 | continue |
|
266 | continue | |
265 |
|
267 | |||
266 | if line_stripped.startswith('@'): |
|
268 | if any( | |
267 | # Here is where we assume there is, at most, one decorator. |
|
269 | line_stripped.startswith("@" + pseudo_decorator) | |
268 | # Might need to rethink this. |
|
270 | for pseudo_decorator in PSEUDO_DECORATORS | |
269 | decorator = line_stripped |
|
271 | ): | |
270 |
|
|
272 | if decorator: | |
|
273 | raise RuntimeError( | |||
|
274 | "Applying multiple pseudo-decorators on one line is not supported" | |||
|
275 | ) | |||
|
276 | else: | |||
|
277 | decorator = line_stripped | |||
|
278 | continue | |||
271 |
|
279 | |||
272 | # does this look like an input line? |
|
280 | # does this look like an input line? | |
273 | matchin = rgxin.match(line) |
|
281 | matchin = rgxin.match(line) |
@@ -68,6 +68,8 b' class TerminalPdb(Pdb):' | |||||
68 | self.debugger_history = FileHistory(os.path.expanduser(str(p))) |
|
68 | self.debugger_history = FileHistory(os.path.expanduser(str(p))) | |
69 | else: |
|
69 | else: | |
70 | self.debugger_history = InMemoryHistory() |
|
70 | self.debugger_history = InMemoryHistory() | |
|
71 | else: | |||
|
72 | self.debugger_history = self.shell.debugger_history | |||
71 |
|
73 | |||
72 | options = dict( |
|
74 | options = dict( | |
73 | message=(lambda: PygmentsTokens(get_prompt_tokens())), |
|
75 | message=(lambda: PygmentsTokens(get_prompt_tokens())), |
@@ -3,12 +3,10 b'' | |||||
3 | import asyncio |
|
3 | import asyncio | |
4 | import os |
|
4 | import os | |
5 | import sys |
|
5 | import sys | |
6 | import warnings |
|
|||
7 | from warnings import warn |
|
6 | from warnings import warn | |
8 |
|
7 | |||
9 | from IPython.core.async_helpers import get_asyncio_loop |
|
8 | from IPython.core.async_helpers import get_asyncio_loop | |
10 | from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC |
|
9 | from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC | |
11 | from IPython.utils import io |
|
|||
12 | from IPython.utils.py3compat import input |
|
10 | from IPython.utils.py3compat import input | |
13 | from IPython.utils.terminal import toggle_set_term_title, set_term_title, restore_term_title |
|
11 | from IPython.utils.terminal import toggle_set_term_title, set_term_title, restore_term_title | |
14 | from IPython.utils.process import abbrev_cwd |
|
12 | from IPython.utils.process import abbrev_cwd | |
@@ -32,7 +30,7 b' from prompt_toolkit.auto_suggest import AutoSuggestFromHistory' | |||||
32 | from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode |
|
30 | from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode | |
33 | from prompt_toolkit.filters import (HasFocus, Condition, IsDone) |
|
31 | from prompt_toolkit.filters import (HasFocus, Condition, IsDone) | |
34 | from prompt_toolkit.formatted_text import PygmentsTokens |
|
32 | from prompt_toolkit.formatted_text import PygmentsTokens | |
35 |
from prompt_toolkit.history import |
|
33 | from prompt_toolkit.history import History | |
36 | from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor |
|
34 | from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor | |
37 | from prompt_toolkit.output import ColorDepth |
|
35 | from prompt_toolkit.output import ColorDepth | |
38 | from prompt_toolkit.patch_stdout import patch_stdout |
|
36 | from prompt_toolkit.patch_stdout import patch_stdout | |
@@ -116,6 +114,59 b' def black_reformat_handler(text_before_cursor):' | |||||
116 | return formatted_text |
|
114 | return formatted_text | |
117 |
|
115 | |||
118 |
|
116 | |||
|
117 | def yapf_reformat_handler(text_before_cursor): | |||
|
118 | from yapf.yapflib import file_resources | |||
|
119 | from yapf.yapflib import yapf_api | |||
|
120 | ||||
|
121 | style_config = file_resources.GetDefaultStyleForDir(os.getcwd()) | |||
|
122 | formatted_text, was_formatted = yapf_api.FormatCode( | |||
|
123 | text_before_cursor, style_config=style_config | |||
|
124 | ) | |||
|
125 | if was_formatted: | |||
|
126 | if not text_before_cursor.endswith("\n") and formatted_text.endswith("\n"): | |||
|
127 | formatted_text = formatted_text[:-1] | |||
|
128 | return formatted_text | |||
|
129 | else: | |||
|
130 | return text_before_cursor | |||
|
131 | ||||
|
132 | ||||
|
133 | class PtkHistoryAdapter(History): | |||
|
134 | """ | |||
|
135 | Prompt toolkit has it's own way of handling history, Where it assumes it can | |||
|
136 | Push/pull from history. | |||
|
137 | ||||
|
138 | """ | |||
|
139 | ||||
|
140 | def __init__(self, shell): | |||
|
141 | super().__init__() | |||
|
142 | self.shell = shell | |||
|
143 | self._refresh() | |||
|
144 | ||||
|
145 | def append_string(self, string): | |||
|
146 | # we rely on sql for that. | |||
|
147 | self._loaded = False | |||
|
148 | self._refresh() | |||
|
149 | ||||
|
150 | def _refresh(self): | |||
|
151 | if not self._loaded: | |||
|
152 | self._loaded_strings = list(self.load_history_strings()) | |||
|
153 | ||||
|
154 | def load_history_strings(self): | |||
|
155 | last_cell = "" | |||
|
156 | res = [] | |||
|
157 | for __, ___, cell in self.shell.history_manager.get_tail( | |||
|
158 | self.shell.history_load_length, include_latest=True | |||
|
159 | ): | |||
|
160 | # Ignore blank lines and consecutive duplicates | |||
|
161 | cell = cell.rstrip() | |||
|
162 | if cell and (cell != last_cell): | |||
|
163 | res.append(cell) | |||
|
164 | last_cell = cell | |||
|
165 | yield from res[::-1] | |||
|
166 | ||||
|
167 | def store_string(self, string: str) -> None: | |||
|
168 | pass | |||
|
169 | ||||
119 | class TerminalInteractiveShell(InteractiveShell): |
|
170 | class TerminalInteractiveShell(InteractiveShell): | |
120 | mime_renderers = Dict().tag(config=True) |
|
171 | mime_renderers = Dict().tag(config=True) | |
121 |
|
172 | |||
@@ -184,8 +235,8 b' class TerminalInteractiveShell(InteractiveShell):' | |||||
184 | ).tag(config=True) |
|
235 | ).tag(config=True) | |
185 |
|
236 | |||
186 | autoformatter = Unicode( |
|
237 | autoformatter = Unicode( | |
187 |
|
|
238 | None, | |
188 | help="Autoformatter to reformat Terminal code. Can be `'black'` or `None`", |
|
239 | help="Autoformatter to reformat Terminal code. Can be `'black'`, `'yapf'` or `None`", | |
189 | allow_none=True |
|
240 | allow_none=True | |
190 | ).tag(config=True) |
|
241 | ).tag(config=True) | |
191 |
|
242 | |||
@@ -232,6 +283,8 b' class TerminalInteractiveShell(InteractiveShell):' | |||||
232 | self.reformat_handler = lambda x:x |
|
283 | self.reformat_handler = lambda x:x | |
233 | elif formatter == 'black': |
|
284 | elif formatter == 'black': | |
234 | self.reformat_handler = black_reformat_handler |
|
285 | self.reformat_handler = black_reformat_handler | |
|
286 | elif formatter == "yapf": | |||
|
287 | self.reformat_handler = yapf_reformat_handler | |||
235 | else: |
|
288 | else: | |
236 | raise ValueError |
|
289 | raise ValueError | |
237 |
|
290 | |||
@@ -379,16 +432,9 b' class TerminalInteractiveShell(InteractiveShell):' | |||||
379 | # Set up keyboard shortcuts |
|
432 | # Set up keyboard shortcuts | |
380 | key_bindings = create_ipython_shortcuts(self) |
|
433 | key_bindings = create_ipython_shortcuts(self) | |
381 |
|
434 | |||
|
435 | ||||
382 | # Pre-populate history from IPython's history database |
|
436 | # Pre-populate history from IPython's history database | |
383 |
history = |
|
437 | history = PtkHistoryAdapter(self) | |
384 | last_cell = u"" |
|
|||
385 | for __, ___, cell in self.history_manager.get_tail(self.history_load_length, |
|
|||
386 | include_latest=True): |
|
|||
387 | # Ignore blank lines and consecutive duplicates |
|
|||
388 | cell = cell.rstrip() |
|
|||
389 | if cell and (cell != last_cell): |
|
|||
390 | history.append_string(cell) |
|
|||
391 | last_cell = cell |
|
|||
392 |
|
438 | |||
393 | self._style = self._make_style_from_name_or_cls(self.highlighting_style) |
|
439 | self._style = self._make_style_from_name_or_cls(self.highlighting_style) | |
394 | self.style = DynamicStyle(lambda: self._style) |
|
440 | self.style = DynamicStyle(lambda: self._style) | |
@@ -568,7 +614,6 b' class TerminalInteractiveShell(InteractiveShell):' | |||||
568 | def enable_win_unicode_console(self): |
|
614 | def enable_win_unicode_console(self): | |
569 | # Since IPython 7.10 doesn't support python < 3.6 and PEP 528, Python uses the unicode APIs for the Windows |
|
615 | # Since IPython 7.10 doesn't support python < 3.6 and PEP 528, Python uses the unicode APIs for the Windows | |
570 | # console by default, so WUC shouldn't be needed. |
|
616 | # console by default, so WUC shouldn't be needed. | |
571 | from warnings import warn |
|
|||
572 | warn("`enable_win_unicode_console` is deprecated since IPython 7.10, does not do anything and will be removed in the future", |
|
617 | warn("`enable_win_unicode_console` is deprecated since IPython 7.10, does not do anything and will be removed in the future", | |
573 | DeprecationWarning, |
|
618 | DeprecationWarning, | |
574 | stacklevel=2) |
|
619 | stacklevel=2) |
@@ -1,7 +1,7 b'' | |||||
1 | #!/usr/bin/env python |
|
1 | #!/usr/bin/env python | |
2 | # encoding: utf-8 |
|
2 | # encoding: utf-8 | |
3 | """ |
|
3 | """ | |
4 |
The :class:`~ |
|
4 | The :class:`~traitlets.config.application.Application` object for the command | |
5 | line :command:`ipython` program. |
|
5 | line :command:`ipython` program. | |
6 | """ |
|
6 | """ | |
7 |
|
7 |
@@ -53,7 +53,7 b' class TerminalMagics(Magics):' | |||||
53 | self.shell.user_ns['pasted_block'] = b |
|
53 | self.shell.user_ns['pasted_block'] = b | |
54 | self.shell.using_paste_magics = True |
|
54 | self.shell.using_paste_magics = True | |
55 | try: |
|
55 | try: | |
56 | self.shell.run_cell(b) |
|
56 | self.shell.run_cell(b, store_history=True) | |
57 | finally: |
|
57 | finally: | |
58 | self.shell.using_paste_magics = False |
|
58 | self.shell.using_paste_magics = False | |
59 |
|
59 |
@@ -10,6 +10,7 b' import warnings' | |||||
10 | import signal |
|
10 | import signal | |
11 | import sys |
|
11 | import sys | |
12 | import re |
|
12 | import re | |
|
13 | import os | |||
13 | from typing import Callable |
|
14 | from typing import Callable | |
14 |
|
15 | |||
15 |
|
16 | |||
@@ -56,7 +57,7 b' def create_ipython_shortcuts(shell):' | |||||
56 | & insert_mode |
|
57 | & insert_mode | |
57 | ))(reformat_and_execute) |
|
58 | ))(reformat_and_execute) | |
58 |
|
59 | |||
59 |
kb.add( |
|
60 | kb.add("c-\\")(quit) | |
60 |
|
61 | |||
61 | kb.add('c-p', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER)) |
|
62 | kb.add('c-p', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER)) | |
62 | )(previous_history_or_previous_completion) |
|
63 | )(previous_history_or_previous_completion) | |
@@ -139,12 +140,24 b' def create_ipython_shortcuts(shell):' | |||||
139 | event.current_buffer.insert_text("{}") |
|
140 | event.current_buffer.insert_text("{}") | |
140 | event.current_buffer.cursor_left() |
|
141 | event.current_buffer.cursor_left() | |
141 |
|
142 | |||
142 | @kb.add('"', filter=focused_insert & auto_match & following_text(r"[,)}\]]|$")) |
|
143 | @kb.add( | |
|
144 | '"', | |||
|
145 | filter=focused_insert | |||
|
146 | & auto_match | |||
|
147 | & preceding_text(r'^([^"]+|"[^"]*")*$') | |||
|
148 | & following_text(r"[,)}\]]|$"), | |||
|
149 | ) | |||
143 | def _(event): |
|
150 | def _(event): | |
144 | event.current_buffer.insert_text('""') |
|
151 | event.current_buffer.insert_text('""') | |
145 | event.current_buffer.cursor_left() |
|
152 | event.current_buffer.cursor_left() | |
146 |
|
153 | |||
147 | @kb.add("'", filter=focused_insert & auto_match & following_text(r"[,)}\]]|$")) |
|
154 | @kb.add( | |
|
155 | "'", | |||
|
156 | filter=focused_insert | |||
|
157 | & auto_match | |||
|
158 | & preceding_text(r"^([^']+|'[^']*')*$") | |||
|
159 | & following_text(r"[,)}\]]|$"), | |||
|
160 | ) | |||
148 | def _(event): |
|
161 | def _(event): | |
149 | event.current_buffer.insert_text("''") |
|
162 | event.current_buffer.insert_text("''") | |
150 | event.current_buffer.cursor_left() |
|
163 | event.current_buffer.cursor_left() | |
@@ -186,16 +199,6 b' def create_ipython_shortcuts(shell):' | |||||
186 | event.current_buffer.insert_text("{}" + dashes) |
|
199 | event.current_buffer.insert_text("{}" + dashes) | |
187 | event.current_buffer.cursor_left(len(dashes) + 1) |
|
200 | event.current_buffer.cursor_left(len(dashes) + 1) | |
188 |
|
201 | |||
189 | @kb.add('"', filter=focused_insert & auto_match & preceding_text(r".*(r|R)$")) |
|
|||
190 | def _(event): |
|
|||
191 | event.current_buffer.insert_text('""') |
|
|||
192 | event.current_buffer.cursor_left() |
|
|||
193 |
|
||||
194 | @kb.add("'", filter=focused_insert & auto_match & preceding_text(r".*(r|R)$")) |
|
|||
195 | def _(event): |
|
|||
196 | event.current_buffer.insert_text("''") |
|
|||
197 | event.current_buffer.cursor_left() |
|
|||
198 |
|
||||
199 | # just move cursor |
|
202 | # just move cursor | |
200 | @kb.add(")", filter=focused_insert & auto_match & following_text(r"^\)")) |
|
203 | @kb.add(")", filter=focused_insert & auto_match & following_text(r"^\)")) | |
201 | @kb.add("]", filter=focused_insert & auto_match & following_text(r"^\]")) |
|
204 | @kb.add("]", filter=focused_insert & auto_match & following_text(r"^\]")) | |
@@ -265,15 +268,22 b' def create_ipython_shortcuts(shell):' | |||||
265 | focused_insert_vi = has_focus(DEFAULT_BUFFER) & vi_insert_mode |
|
268 | focused_insert_vi = has_focus(DEFAULT_BUFFER) & vi_insert_mode | |
266 |
|
269 | |||
267 | # Needed for to accept autosuggestions in vi insert mode |
|
270 | # Needed for to accept autosuggestions in vi insert mode | |
268 | @kb.add("c-e", filter=focused_insert_vi & ebivim) |
|
271 | def _apply_autosuggest(event): | |
269 | def _(event): |
|
|||
270 | b = event.current_buffer |
|
272 | b = event.current_buffer | |
271 | suggestion = b.suggestion |
|
273 | suggestion = b.suggestion | |
272 | if suggestion: |
|
274 | if suggestion is not None and suggestion.text: | |
273 | b.insert_text(suggestion.text) |
|
275 | b.insert_text(suggestion.text) | |
274 | else: |
|
276 | else: | |
275 | nc.end_of_line(event) |
|
277 | nc.end_of_line(event) | |
276 |
|
278 | |||
|
279 | @kb.add("end", filter=has_focus(DEFAULT_BUFFER) & (ebivim | ~vi_insert_mode)) | |||
|
280 | def _(event): | |||
|
281 | _apply_autosuggest(event) | |||
|
282 | ||||
|
283 | @kb.add("c-e", filter=focused_insert_vi & ebivim) | |||
|
284 | def _(event): | |||
|
285 | _apply_autosuggest(event) | |||
|
286 | ||||
277 | @kb.add("c-f", filter=focused_insert_vi) |
|
287 | @kb.add("c-f", filter=focused_insert_vi) | |
278 | def _(event): |
|
288 | def _(event): | |
279 | b = event.current_buffer |
|
289 | b = event.current_buffer | |
@@ -336,12 +346,7 b' def create_ipython_shortcuts(shell):' | |||||
336 | shape = {InputMode.NAVIGATION: 2, InputMode.REPLACE: 4}.get(mode, 6) |
|
346 | shape = {InputMode.NAVIGATION: 2, InputMode.REPLACE: 4}.get(mode, 6) | |
337 | cursor = "\x1b[{} q".format(shape) |
|
347 | cursor = "\x1b[{} q".format(shape) | |
338 |
|
348 | |||
339 | if hasattr(sys.stdout, "_cli"): |
|
349 | sys.stdout.write(cursor) | |
340 | write = sys.stdout._cli.output.write_raw |
|
|||
341 | else: |
|
|||
342 | write = sys.stdout.write |
|
|||
343 |
|
||||
344 | write(cursor) |
|
|||
345 | sys.stdout.flush() |
|
350 | sys.stdout.flush() | |
346 |
|
351 | |||
347 | self._input_mode = mode |
|
352 | self._input_mode = mode | |
@@ -454,11 +459,16 b' def reset_search_buffer(event):' | |||||
454 | def suspend_to_bg(event): |
|
459 | def suspend_to_bg(event): | |
455 | event.app.suspend_to_background() |
|
460 | event.app.suspend_to_background() | |
456 |
|
461 | |||
457 |
def |
|
462 | def quit(event): | |
458 | """ |
|
463 | """ | |
459 | Force exit (with a non-zero return value) |
|
464 | On platforms that support SIGQUIT, send SIGQUIT to the current process. | |
|
465 | On other platforms, just exit the process with a message. | |||
460 | """ |
|
466 | """ | |
461 | sys.exit("Quit") |
|
467 | sigquit = getattr(signal, "SIGQUIT", None) | |
|
468 | if sigquit is not None: | |||
|
469 | os.kill(0, signal.SIGQUIT) | |||
|
470 | else: | |||
|
471 | sys.exit("Quit") | |||
462 |
|
472 | |||
463 | def indent_buffer(event): |
|
473 | def indent_buffer(event): | |
464 | event.current_buffer.insert_text(' ' * 4) |
|
474 | event.current_buffer.insert_text(' ' * 4) |
@@ -38,7 +38,7 b' def ipfunc():' | |||||
38 | ....: print(i, end=' ') |
|
38 | ....: print(i, end=' ') | |
39 | ....: print(i+1, end=' ') |
|
39 | ....: print(i+1, end=' ') | |
40 | ....: |
|
40 | ....: | |
41 |
0 1 1 2 2 3 |
|
41 | 0 1 1 2 2 3 | |
42 |
|
42 | |||
43 |
|
43 | |||
44 | It's OK to use '_' for the last result, but do NOT try to use IPython's |
|
44 | It's OK to use '_' for the last result, but do NOT try to use IPython's | |
@@ -50,7 +50,7 b' def ipfunc():' | |||||
50 |
|
50 | |||
51 | In [8]: print(repr(_)) |
|
51 | In [8]: print(repr(_)) | |
52 | 'hi' |
|
52 | 'hi' | |
53 |
|
53 | |||
54 | In [7]: 3+4 |
|
54 | In [7]: 3+4 | |
55 | Out[7]: 7 |
|
55 | Out[7]: 7 | |
56 |
|
56 | |||
@@ -60,7 +60,7 b' def ipfunc():' | |||||
60 | In [9]: ipfunc() |
|
60 | In [9]: ipfunc() | |
61 | Out[9]: 'ipfunc' |
|
61 | Out[9]: 'ipfunc' | |
62 | """ |
|
62 | """ | |
63 |
return |
|
63 | return "ipfunc" | |
64 |
|
64 | |||
65 |
|
65 | |||
66 | def ipos(): |
|
66 | def ipos(): |
@@ -14,6 +14,7 b' import traceback' | |||||
14 | import types |
|
14 | import types | |
15 | import warnings |
|
15 | import warnings | |
16 | from contextlib import contextmanager |
|
16 | from contextlib import contextmanager | |
|
17 | from pathlib import Path | |||
17 | from typing import Any |
|
18 | from typing import Any | |
18 | from typing import Callable |
|
19 | from typing import Callable | |
19 | from typing import Dict |
|
20 | from typing import Dict | |
@@ -28,8 +29,6 b' from typing import Type' | |||||
28 | from typing import TYPE_CHECKING |
|
29 | from typing import TYPE_CHECKING | |
29 | from typing import Union |
|
30 | from typing import Union | |
30 |
|
31 | |||
31 | import py.path |
|
|||
32 |
|
||||
33 | import pytest |
|
32 | import pytest | |
34 | from _pytest import outcomes |
|
33 | from _pytest import outcomes | |
35 | from _pytest._code.code import ExceptionInfo |
|
34 | from _pytest._code.code import ExceptionInfo | |
@@ -42,6 +41,7 b' from _pytest.config.argparsing import Parser' | |||||
42 | from _pytest.fixtures import FixtureRequest |
|
41 | from _pytest.fixtures import FixtureRequest | |
43 | from _pytest.nodes import Collector |
|
42 | from _pytest.nodes import Collector | |
44 | from _pytest.outcomes import OutcomeException |
|
43 | from _pytest.outcomes import OutcomeException | |
|
44 | from _pytest.pathlib import fnmatch_ex | |||
45 | from _pytest.pathlib import import_path |
|
45 | from _pytest.pathlib import import_path | |
46 | from _pytest.python_api import approx |
|
46 | from _pytest.python_api import approx | |
47 | from _pytest.warning_types import PytestWarning |
|
47 | from _pytest.warning_types import PytestWarning | |
@@ -126,35 +126,55 b' def pytest_unconfigure() -> None:' | |||||
126 |
|
126 | |||
127 |
|
127 | |||
128 | def pytest_collect_file( |
|
128 | def pytest_collect_file( | |
129 |
path: |
|
129 | file_path: Path, | |
130 | parent: Collector, |
|
130 | parent: Collector, | |
131 | ) -> Optional[Union["IPDoctestModule", "IPDoctestTextfile"]]: |
|
131 | ) -> Optional[Union["IPDoctestModule", "IPDoctestTextfile"]]: | |
132 | config = parent.config |
|
132 | config = parent.config | |
133 |
if path. |
|
133 | if file_path.suffix == ".py": | |
134 |
if config.option.ipdoctestmodules and not |
|
134 | if config.option.ipdoctestmodules and not any( | |
135 | mod: IPDoctestModule = IPDoctestModule.from_parent(parent, fspath=path) |
|
135 | (_is_setup_py(file_path), _is_main_py(file_path)) | |
|
136 | ): | |||
|
137 | mod: IPDoctestModule = IPDoctestModule.from_parent(parent, path=file_path) | |||
136 | return mod |
|
138 | return mod | |
137 | elif _is_ipdoctest(config, path, parent): |
|
139 | elif _is_ipdoctest(config, file_path, parent): | |
138 |
txt: IPDoctestTextfile = IPDoctestTextfile.from_parent(parent, |
|
140 | txt: IPDoctestTextfile = IPDoctestTextfile.from_parent(parent, path=file_path) | |
139 | return txt |
|
141 | return txt | |
140 | return None |
|
142 | return None | |
141 |
|
143 | |||
142 |
|
144 | |||
143 | def _is_setup_py(path: py.path.local) -> bool: |
|
145 | if int(pytest.__version__.split(".")[0]) < 7: | |
144 | if path.basename != "setup.py": |
|
146 | _collect_file = pytest_collect_file | |
|
147 | ||||
|
148 | def pytest_collect_file( | |||
|
149 | path, | |||
|
150 | parent: Collector, | |||
|
151 | ) -> Optional[Union["IPDoctestModule", "IPDoctestTextfile"]]: | |||
|
152 | return _collect_file(Path(path), parent) | |||
|
153 | ||||
|
154 | _import_path = import_path | |||
|
155 | ||||
|
156 | def import_path(path, root): | |||
|
157 | import py.path | |||
|
158 | ||||
|
159 | return _import_path(py.path.local(path)) | |||
|
160 | ||||
|
161 | ||||
|
162 | def _is_setup_py(path: Path) -> bool: | |||
|
163 | if path.name != "setup.py": | |||
145 | return False |
|
164 | return False | |
146 |
contents = path.read_b |
|
165 | contents = path.read_bytes() | |
147 | return b"setuptools" in contents or b"distutils" in contents |
|
166 | return b"setuptools" in contents or b"distutils" in contents | |
148 |
|
167 | |||
149 |
|
168 | |||
150 |
def _is_ipdoctest(config: Config, path: |
|
169 | def _is_ipdoctest(config: Config, path: Path, parent: Collector) -> bool: | |
151 |
if path. |
|
170 | if path.suffix in (".txt", ".rst") and parent.session.isinitpath(path): | |
152 | return True |
|
171 | return True | |
153 | globs = config.getoption("ipdoctestglob") or ["test*.txt"] |
|
172 | globs = config.getoption("ipdoctestglob") or ["test*.txt"] | |
154 | for glob in globs: |
|
173 | return any(fnmatch_ex(glob, path) for glob in globs) | |
155 | if path.check(fnmatch=glob): |
|
174 | ||
156 | return True |
|
175 | ||
157 | return False |
|
176 | def _is_main_py(path: Path) -> bool: | |
|
177 | return path.name == "__main__.py" | |||
158 |
|
178 | |||
159 |
|
179 | |||
160 | class ReprFailDoctest(TerminalRepr): |
|
180 | class ReprFailDoctest(TerminalRepr): | |
@@ -273,7 +293,7 b' class IPDoctestItem(pytest.Item):' | |||||
273 | runner: "IPDocTestRunner", |
|
293 | runner: "IPDocTestRunner", | |
274 | dtest: "doctest.DocTest", |
|
294 | dtest: "doctest.DocTest", | |
275 | ): |
|
295 | ): | |
276 |
# incompatible signature due to |
|
296 | # incompatible signature due to imposed limits on subclass | |
277 | """The public named constructor.""" |
|
297 | """The public named constructor.""" | |
278 | return super().from_parent(name=name, parent=parent, runner=runner, dtest=dtest) |
|
298 | return super().from_parent(name=name, parent=parent, runner=runner, dtest=dtest) | |
279 |
|
299 | |||
@@ -372,61 +392,63 b' class IPDoctestItem(pytest.Item):' | |||||
372 | elif isinstance(excinfo.value, MultipleDoctestFailures): |
|
392 | elif isinstance(excinfo.value, MultipleDoctestFailures): | |
373 | failures = excinfo.value.failures |
|
393 | failures = excinfo.value.failures | |
374 |
|
394 | |||
375 |
if failures is |
|
395 | if failures is None: | |
376 | reprlocation_lines = [] |
|
|||
377 | for failure in failures: |
|
|||
378 | example = failure.example |
|
|||
379 | test = failure.test |
|
|||
380 | filename = test.filename |
|
|||
381 | if test.lineno is None: |
|
|||
382 | lineno = None |
|
|||
383 | else: |
|
|||
384 | lineno = test.lineno + example.lineno + 1 |
|
|||
385 | message = type(failure).__name__ |
|
|||
386 | # TODO: ReprFileLocation doesn't expect a None lineno. |
|
|||
387 | reprlocation = ReprFileLocation(filename, lineno, message) # type: ignore[arg-type] |
|
|||
388 | checker = _get_checker() |
|
|||
389 | report_choice = _get_report_choice( |
|
|||
390 | self.config.getoption("ipdoctestreport") |
|
|||
391 | ) |
|
|||
392 | if lineno is not None: |
|
|||
393 | assert failure.test.docstring is not None |
|
|||
394 | lines = failure.test.docstring.splitlines(False) |
|
|||
395 | # add line numbers to the left of the error message |
|
|||
396 | assert test.lineno is not None |
|
|||
397 | lines = [ |
|
|||
398 | "%03d %s" % (i + test.lineno + 1, x) |
|
|||
399 | for (i, x) in enumerate(lines) |
|
|||
400 | ] |
|
|||
401 | # trim docstring error lines to 10 |
|
|||
402 | lines = lines[max(example.lineno - 9, 0) : example.lineno + 1] |
|
|||
403 | else: |
|
|||
404 | lines = [ |
|
|||
405 | "EXAMPLE LOCATION UNKNOWN, not showing all tests of that example" |
|
|||
406 | ] |
|
|||
407 | indent = ">>>" |
|
|||
408 | for line in example.source.splitlines(): |
|
|||
409 | lines.append(f"??? {indent} {line}") |
|
|||
410 | indent = "..." |
|
|||
411 | if isinstance(failure, doctest.DocTestFailure): |
|
|||
412 | lines += checker.output_difference( |
|
|||
413 | example, failure.got, report_choice |
|
|||
414 | ).split("\n") |
|
|||
415 | else: |
|
|||
416 | inner_excinfo = ExceptionInfo(failure.exc_info) |
|
|||
417 | lines += ["UNEXPECTED EXCEPTION: %s" % repr(inner_excinfo.value)] |
|
|||
418 | lines += [ |
|
|||
419 | x.strip("\n") |
|
|||
420 | for x in traceback.format_exception(*failure.exc_info) |
|
|||
421 | ] |
|
|||
422 | reprlocation_lines.append((reprlocation, lines)) |
|
|||
423 | return ReprFailDoctest(reprlocation_lines) |
|
|||
424 | else: |
|
|||
425 | return super().repr_failure(excinfo) |
|
396 | return super().repr_failure(excinfo) | |
426 |
|
397 | |||
427 | def reportinfo(self): |
|
398 | reprlocation_lines = [] | |
|
399 | for failure in failures: | |||
|
400 | example = failure.example | |||
|
401 | test = failure.test | |||
|
402 | filename = test.filename | |||
|
403 | if test.lineno is None: | |||
|
404 | lineno = None | |||
|
405 | else: | |||
|
406 | lineno = test.lineno + example.lineno + 1 | |||
|
407 | message = type(failure).__name__ | |||
|
408 | # TODO: ReprFileLocation doesn't expect a None lineno. | |||
|
409 | reprlocation = ReprFileLocation(filename, lineno, message) # type: ignore[arg-type] | |||
|
410 | checker = _get_checker() | |||
|
411 | report_choice = _get_report_choice(self.config.getoption("ipdoctestreport")) | |||
|
412 | if lineno is not None: | |||
|
413 | assert failure.test.docstring is not None | |||
|
414 | lines = failure.test.docstring.splitlines(False) | |||
|
415 | # add line numbers to the left of the error message | |||
|
416 | assert test.lineno is not None | |||
|
417 | lines = [ | |||
|
418 | "%03d %s" % (i + test.lineno + 1, x) for (i, x) in enumerate(lines) | |||
|
419 | ] | |||
|
420 | # trim docstring error lines to 10 | |||
|
421 | lines = lines[max(example.lineno - 9, 0) : example.lineno + 1] | |||
|
422 | else: | |||
|
423 | lines = [ | |||
|
424 | "EXAMPLE LOCATION UNKNOWN, not showing all tests of that example" | |||
|
425 | ] | |||
|
426 | indent = ">>>" | |||
|
427 | for line in example.source.splitlines(): | |||
|
428 | lines.append(f"??? {indent} {line}") | |||
|
429 | indent = "..." | |||
|
430 | if isinstance(failure, doctest.DocTestFailure): | |||
|
431 | lines += checker.output_difference( | |||
|
432 | example, failure.got, report_choice | |||
|
433 | ).split("\n") | |||
|
434 | else: | |||
|
435 | inner_excinfo = ExceptionInfo.from_exc_info(failure.exc_info) | |||
|
436 | lines += ["UNEXPECTED EXCEPTION: %s" % repr(inner_excinfo.value)] | |||
|
437 | lines += [ | |||
|
438 | x.strip("\n") for x in traceback.format_exception(*failure.exc_info) | |||
|
439 | ] | |||
|
440 | reprlocation_lines.append((reprlocation, lines)) | |||
|
441 | return ReprFailDoctest(reprlocation_lines) | |||
|
442 | ||||
|
443 | def reportinfo(self) -> Tuple[Union["os.PathLike[str]", str], Optional[int], str]: | |||
428 | assert self.dtest is not None |
|
444 | assert self.dtest is not None | |
429 |
return self. |
|
445 | return self.path, self.dtest.lineno, "[ipdoctest] %s" % self.name | |
|
446 | ||||
|
447 | if int(pytest.__version__.split(".")[0]) < 7: | |||
|
448 | ||||
|
449 | @property | |||
|
450 | def path(self) -> Path: | |||
|
451 | return Path(self.fspath) | |||
430 |
|
452 | |||
431 |
|
453 | |||
432 | def _get_flag_lookup() -> Dict[str, int]: |
|
454 | def _get_flag_lookup() -> Dict[str, int]: | |
@@ -474,9 +496,9 b' class IPDoctestTextfile(pytest.Module):' | |||||
474 | # Inspired by doctest.testfile; ideally we would use it directly, |
|
496 | # Inspired by doctest.testfile; ideally we would use it directly, | |
475 | # but it doesn't support passing a custom checker. |
|
497 | # but it doesn't support passing a custom checker. | |
476 | encoding = self.config.getini("ipdoctest_encoding") |
|
498 | encoding = self.config.getini("ipdoctest_encoding") | |
477 |
text = self. |
|
499 | text = self.path.read_text(encoding) | |
478 |
filename = str(self. |
|
500 | filename = str(self.path) | |
479 |
name = self. |
|
501 | name = self.path.name | |
480 | globs = {"__name__": "__main__"} |
|
502 | globs = {"__name__": "__main__"} | |
481 |
|
503 | |||
482 | optionflags = get_optionflags(self) |
|
504 | optionflags = get_optionflags(self) | |
@@ -495,6 +517,27 b' class IPDoctestTextfile(pytest.Module):' | |||||
495 | self, name=test.name, runner=runner, dtest=test |
|
517 | self, name=test.name, runner=runner, dtest=test | |
496 | ) |
|
518 | ) | |
497 |
|
519 | |||
|
520 | if int(pytest.__version__.split(".")[0]) < 7: | |||
|
521 | ||||
|
522 | @property | |||
|
523 | def path(self) -> Path: | |||
|
524 | return Path(self.fspath) | |||
|
525 | ||||
|
526 | @classmethod | |||
|
527 | def from_parent( | |||
|
528 | cls, | |||
|
529 | parent, | |||
|
530 | *, | |||
|
531 | fspath=None, | |||
|
532 | path: Optional[Path] = None, | |||
|
533 | **kw, | |||
|
534 | ): | |||
|
535 | if path is not None: | |||
|
536 | import py.path | |||
|
537 | ||||
|
538 | fspath = py.path.local(path) | |||
|
539 | return super().from_parent(parent=parent, fspath=fspath, **kw) | |||
|
540 | ||||
498 |
|
541 | |||
499 | def _check_all_skipped(test: "doctest.DocTest") -> None: |
|
542 | def _check_all_skipped(test: "doctest.DocTest") -> None: | |
500 | """Raise pytest.skip() if all examples in the given DocTest have the SKIP |
|
543 | """Raise pytest.skip() if all examples in the given DocTest have the SKIP | |
@@ -559,15 +602,20 b' class IPDoctestModule(pytest.Module):' | |||||
559 |
|
602 | |||
560 | def _find_lineno(self, obj, source_lines): |
|
603 | def _find_lineno(self, obj, source_lines): | |
561 | """Doctest code does not take into account `@property`, this |
|
604 | """Doctest code does not take into account `@property`, this | |
562 | is a hackish way to fix it. |
|
605 | is a hackish way to fix it. https://bugs.python.org/issue17446 | |
563 |
|
606 | |||
564 | https://bugs.python.org/issue17446 |
|
607 | Wrapped Doctests will need to be unwrapped so the correct | |
|
608 | line number is returned. This will be reported upstream. #8796 | |||
565 | """ |
|
609 | """ | |
566 | if isinstance(obj, property): |
|
610 | if isinstance(obj, property): | |
567 | obj = getattr(obj, "fget", obj) |
|
611 | obj = getattr(obj, "fget", obj) | |
|
612 | ||||
|
613 | if hasattr(obj, "__wrapped__"): | |||
|
614 | # Get the main obj in case of it being wrapped | |||
|
615 | obj = inspect.unwrap(obj) | |||
|
616 | ||||
568 | # Type ignored because this is a private function. |
|
617 | # Type ignored because this is a private function. | |
569 |
return |
|
618 | return super()._find_lineno( # type:ignore[misc] | |
570 | self, |
|
|||
571 | obj, |
|
619 | obj, | |
572 | source_lines, |
|
620 | source_lines, | |
573 | ) |
|
621 | ) | |
@@ -580,20 +628,28 b' class IPDoctestModule(pytest.Module):' | |||||
580 | with _patch_unwrap_mock_aware(): |
|
628 | with _patch_unwrap_mock_aware(): | |
581 |
|
629 | |||
582 | # Type ignored because this is a private function. |
|
630 | # Type ignored because this is a private function. | |
583 |
|
|
631 | super()._find( # type:ignore[misc] | |
584 |
|
|
632 | tests, obj, name, module, source_lines, globs, seen | |
585 | ) |
|
633 | ) | |
586 |
|
634 | |||
587 |
if self. |
|
635 | if self.path.name == "conftest.py": | |
588 | module = self.config.pluginmanager._importconftest( |
|
636 | if int(pytest.__version__.split(".")[0]) < 7: | |
589 |
|
|
637 | module = self.config.pluginmanager._importconftest( | |
590 | ) |
|
638 | self.path, | |
|
639 | self.config.getoption("importmode"), | |||
|
640 | ) | |||
|
641 | else: | |||
|
642 | module = self.config.pluginmanager._importconftest( | |||
|
643 | self.path, | |||
|
644 | self.config.getoption("importmode"), | |||
|
645 | rootpath=self.config.rootpath, | |||
|
646 | ) | |||
591 | else: |
|
647 | else: | |
592 | try: |
|
648 | try: | |
593 |
module = import_path(self. |
|
649 | module = import_path(self.path, root=self.config.rootpath) | |
594 | except ImportError: |
|
650 | except ImportError: | |
595 | if self.config.getvalue("ipdoctest_ignore_import_errors"): |
|
651 | if self.config.getvalue("ipdoctest_ignore_import_errors"): | |
596 |
pytest.skip("unable to import module %r" % self. |
|
652 | pytest.skip("unable to import module %r" % self.path) | |
597 | else: |
|
653 | else: | |
598 | raise |
|
654 | raise | |
599 | # Uses internal doctest module parsing mechanism. |
|
655 | # Uses internal doctest module parsing mechanism. | |
@@ -612,6 +668,27 b' class IPDoctestModule(pytest.Module):' | |||||
612 | self, name=test.name, runner=runner, dtest=test |
|
668 | self, name=test.name, runner=runner, dtest=test | |
613 | ) |
|
669 | ) | |
614 |
|
670 | |||
|
671 | if int(pytest.__version__.split(".")[0]) < 7: | |||
|
672 | ||||
|
673 | @property | |||
|
674 | def path(self) -> Path: | |||
|
675 | return Path(self.fspath) | |||
|
676 | ||||
|
677 | @classmethod | |||
|
678 | def from_parent( | |||
|
679 | cls, | |||
|
680 | parent, | |||
|
681 | *, | |||
|
682 | fspath=None, | |||
|
683 | path: Optional[Path] = None, | |||
|
684 | **kw, | |||
|
685 | ): | |||
|
686 | if path is not None: | |||
|
687 | import py.path | |||
|
688 | ||||
|
689 | fspath = py.path.local(path) | |||
|
690 | return super().from_parent(parent=parent, fspath=fspath, **kw) | |||
|
691 | ||||
615 |
|
692 | |||
616 | def _setup_fixtures(doctest_item: IPDoctestItem) -> FixtureRequest: |
|
693 | def _setup_fixtures(doctest_item: IPDoctestItem) -> FixtureRequest: | |
617 | """Used by IPDoctestTextfile and IPDoctestItem to setup fixture information.""" |
|
694 | """Used by IPDoctestTextfile and IPDoctestItem to setup fixture information.""" | |
@@ -665,7 +742,7 b' def _init_checker_class() -> Type["IPDoctestOutputChecker"]:' | |||||
665 | ) |
|
742 | ) | |
666 |
|
743 | |||
667 | def check_output(self, want: str, got: str, optionflags: int) -> bool: |
|
744 | def check_output(self, want: str, got: str, optionflags: int) -> bool: | |
668 |
if |
|
745 | if super().check_output(want, got, optionflags): | |
669 | return True |
|
746 | return True | |
670 |
|
747 | |||
671 | allow_unicode = optionflags & _get_allow_unicode_flag() |
|
748 | allow_unicode = optionflags & _get_allow_unicode_flag() | |
@@ -689,7 +766,7 b' def _init_checker_class() -> Type["IPDoctestOutputChecker"]:' | |||||
689 | if allow_number: |
|
766 | if allow_number: | |
690 | got = self._remove_unwanted_precision(want, got) |
|
767 | got = self._remove_unwanted_precision(want, got) | |
691 |
|
768 | |||
692 |
return |
|
769 | return super().check_output(want, got, optionflags) | |
693 |
|
770 | |||
694 | def _remove_unwanted_precision(self, want: str, got: str) -> str: |
|
771 | def _remove_unwanted_precision(self, want: str, got: str) -> str: | |
695 | wants = list(self._number_re.finditer(want)) |
|
772 | wants = list(self._number_re.finditer(want)) | |
@@ -702,10 +779,7 b' def _init_checker_class() -> Type["IPDoctestOutputChecker"]:' | |||||
702 | exponent: Optional[str] = w.group("exponent1") |
|
779 | exponent: Optional[str] = w.group("exponent1") | |
703 | if exponent is None: |
|
780 | if exponent is None: | |
704 | exponent = w.group("exponent2") |
|
781 | exponent = w.group("exponent2") | |
705 |
if fraction is None |
|
782 | precision = 0 if fraction is None else len(fraction) | |
706 | precision = 0 |
|
|||
707 | else: |
|
|||
708 | precision = len(fraction) |
|
|||
709 | if exponent is not None: |
|
783 | if exponent is not None: | |
710 | precision -= int(exponent) |
|
784 | precision -= int(exponent) | |
711 | if float(w.group()) == approx(float(g.group()), abs=10 ** -precision): |
|
785 | if float(w.group()) == approx(float(g.group()), abs=10 ** -precision): |
@@ -10,8 +10,7 b' from tempfile import TemporaryDirectory' | |||||
10 |
|
10 | |||
11 |
|
11 | |||
12 | class NamedFileInTemporaryDirectory(object): |
|
12 | class NamedFileInTemporaryDirectory(object): | |
13 |
|
13 | def __init__(self, filename, mode="w+b", bufsize=-1, add_to_syspath=False, **kwds): | ||
14 | def __init__(self, filename, mode='w+b', bufsize=-1, **kwds): |
|
|||
15 | """ |
|
14 | """ | |
16 | Open a file named `filename` in a temporary directory. |
|
15 | Open a file named `filename` in a temporary directory. | |
17 |
|
16 |
@@ -10,24 +10,23 b' import sys' | |||||
10 | import tempfile |
|
10 | import tempfile | |
11 | import unittest |
|
11 | import unittest | |
12 | from contextlib import contextmanager |
|
12 | from contextlib import contextmanager | |
13 | from unittest.mock import patch |
|
|||
14 | from os.path import join, abspath |
|
|||
15 | from importlib import reload |
|
13 | from importlib import reload | |
|
14 | from os.path import abspath, join | |||
|
15 | from unittest.mock import patch | |||
16 |
|
16 | |||
17 | import pytest |
|
17 | import pytest | |
|
18 | from tempfile import TemporaryDirectory | |||
18 |
|
19 | |||
19 | import IPython |
|
20 | import IPython | |
20 | from IPython import paths |
|
21 | from IPython import paths | |
21 | from IPython.testing import decorators as dec |
|
22 | from IPython.testing import decorators as dec | |
22 | from IPython.testing.decorators import ( |
|
23 | from IPython.testing.decorators import ( | |
|
24 | onlyif_unicode_paths, | |||
23 | skip_if_not_win32, |
|
25 | skip_if_not_win32, | |
24 | skip_win32, |
|
26 | skip_win32, | |
25 | onlyif_unicode_paths, |
|
|||
26 | ) |
|
27 | ) | |
27 | from IPython.testing.tools import make_tempfile |
|
28 | from IPython.testing.tools import make_tempfile | |
28 | from IPython.utils import path |
|
29 | from IPython.utils import path | |
29 | from IPython.utils.tempdir import TemporaryDirectory |
|
|||
30 |
|
||||
31 |
|
30 | |||
32 | # Platform-dependent imports |
|
31 | # Platform-dependent imports | |
33 | try: |
|
32 | try: | |
@@ -41,6 +40,7 b' except ImportError:' | |||||
41 | import winreg as wreg |
|
40 | import winreg as wreg | |
42 | except ImportError: |
|
41 | except ImportError: | |
43 | import _winreg as wreg |
|
42 | import _winreg as wreg | |
|
43 | ||||
44 | #Add entries that needs to be stubbed by the testing code |
|
44 | #Add entries that needs to be stubbed by the testing code | |
45 | (wreg.OpenKey, wreg.QueryValueEx,) = (None, None) |
|
45 | (wreg.OpenKey, wreg.QueryValueEx,) = (None, None) | |
46 |
|
46 |
@@ -470,11 +470,11 b' def strip_ansi(source):' | |||||
470 |
|
470 | |||
471 | class EvalFormatter(Formatter): |
|
471 | class EvalFormatter(Formatter): | |
472 | """A String Formatter that allows evaluation of simple expressions. |
|
472 | """A String Formatter that allows evaluation of simple expressions. | |
473 |
|
473 | |||
474 | Note that this version interprets a : as specifying a format string (as per |
|
474 | Note that this version interprets a `:` as specifying a format string (as per | |
475 | standard string formatting), so if slicing is required, you must explicitly |
|
475 | standard string formatting), so if slicing is required, you must explicitly | |
476 | create a slice. |
|
476 | create a slice. | |
477 |
|
477 | |||
478 | This is to be used in templating cases, such as the parallel batch |
|
478 | This is to be used in templating cases, such as the parallel batch | |
479 | script templates, where simple arithmetic on arguments is useful. |
|
479 | script templates, where simple arithmetic on arguments is useful. | |
480 |
|
480 | |||
@@ -690,8 +690,8 b' def compute_item_matrix(items, row_first=False, empty=None, *args, **kwargs) :' | |||||
690 | return ([[_get_or_default(items, c * nrow + r, default=empty) for c in range(ncol)] for r in range(nrow)], info) |
|
690 | return ([[_get_or_default(items, c * nrow + r, default=empty) for c in range(ncol)] for r in range(nrow)], info) | |
691 |
|
691 | |||
692 |
|
692 | |||
693 |
def columnize(items, row_first=False, separator= |
|
693 | def columnize(items, row_first=False, separator=" ", displaywidth=80, spread=False): | |
694 |
""" |
|
694 | """Transform a list of strings into a single string with columns. | |
695 |
|
695 | |||
696 | Parameters |
|
696 | Parameters | |
697 | ---------- |
|
697 | ---------- |
@@ -3,4 +3,4 b'' | |||||
3 | ## Reporting a Vulnerability |
|
3 | ## Reporting a Vulnerability | |
4 |
|
4 | |||
5 | All IPython and Jupyter security are handled via security@ipython.org. |
|
5 | All IPython and Jupyter security are handled via security@ipython.org. | |
6 |
You can find more information |
|
6 | You can find more information on the Jupyter website. https://jupyter.org/security |
@@ -5,7 +5,7 b' matrix:' | |||||
5 | environment: |
|
5 | environment: | |
6 | global: |
|
6 | global: | |
7 | APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' |
|
7 | APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' | |
8 | COLUMNS: 120 # Appveyor web viwer window width is 130 chars |
|
8 | COLUMNS: 120 # Appveyor web viewer window width is 130 chars | |
9 |
|
9 | |||
10 | matrix: |
|
10 | matrix: | |
11 | - PYTHON: "C:\\Python38" |
|
11 | - PYTHON: "C:\\Python38" |
@@ -17,22 +17,29 b' For example::' | |||||
17 | def __init__(self, ip): |
|
17 | def __init__(self, ip): | |
18 | self.shell = ip |
|
18 | self.shell = ip | |
19 | self.last_x = None |
|
19 | self.last_x = None | |
20 |
|
20 | |||
21 | def pre_execute(self): |
|
21 | def pre_execute(self): | |
22 | self.last_x = self.shell.user_ns.get('x', None) |
|
22 | self.last_x = self.shell.user_ns.get('x', None) | |
23 |
|
23 | |||
24 | def pre_run_cell(self, info): |
|
24 | def pre_run_cell(self, info): | |
25 |
print(' |
|
25 | print('info.raw_cell =', info.raw_cell) | |
26 |
|
26 | print('info.store_history =', info.store_history) | ||
|
27 | print('info.silent =', info.silent) | |||
|
28 | print('info.shell_futures =', info.shell_futures) | |||
|
29 | print('info.cell_id =', info.cell_id) | |||
|
30 | print(dir(info)) | |||
|
31 | ||||
27 | def post_execute(self): |
|
32 | def post_execute(self): | |
28 | if self.shell.user_ns.get('x', None) != self.last_x: |
|
33 | if self.shell.user_ns.get('x', None) != self.last_x: | |
29 | print("x changed!") |
|
34 | print("x changed!") | |
30 |
|
35 | |||
31 | def post_run_cell(self, result): |
|
36 | def post_run_cell(self, result): | |
32 | print('Cell code: "%s"' % result.info.raw_cell) |
|
37 | print('result.execution_count = ', result.execution_count) | |
33 |
if result.error_before_exec |
|
38 | print('result.error_before_exec = ', result.error_before_exec) | |
34 |
|
|
39 | print('result.error_in_exec = ', result.error_in_exec) | |
35 |
|
40 | print('result.info = ', result.info) | ||
|
41 | print('result.result = ', result.result) | |||
|
42 | ||||
36 | def load_ipython_extension(ip): |
|
43 | def load_ipython_extension(ip): | |
37 | vw = VarWatcher(ip) |
|
44 | vw = VarWatcher(ip) | |
38 | ip.events.register('pre_execute', vw.pre_execute) |
|
45 | ip.events.register('pre_execute', vw.pre_execute) | |
@@ -40,6 +47,13 b' For example::' | |||||
40 | ip.events.register('post_execute', vw.post_execute) |
|
47 | ip.events.register('post_execute', vw.post_execute) | |
41 | ip.events.register('post_run_cell', vw.post_run_cell) |
|
48 | ip.events.register('post_run_cell', vw.post_run_cell) | |
42 |
|
49 | |||
|
50 | .. versionadded:: 8.3 | |||
|
51 | ||||
|
52 | Since IPython 8.3 and ipykernel 6.12.1, the ``info`` objects in the callback | |||
|
53 | now have a the ``cell_id`` that will be set to the value sent by the | |||
|
54 | frontened, when those send it. | |||
|
55 | ||||
|
56 | ||||
43 |
|
57 | |||
44 | Events |
|
58 | Events | |
45 | ====== |
|
59 | ====== |
@@ -135,7 +135,7 b' Metadata' | |||||
135 | ^^^^^^^^ |
|
135 | ^^^^^^^^ | |
136 |
|
136 | |||
137 | We often want to provide frontends with guidance on how to display the data. To |
|
137 | We often want to provide frontends with guidance on how to display the data. To | |
138 | support this, ``_repr_*_()`` methods (except `_repr_pretty_``?) can also return a ``(data, metadata)`` |
|
138 | support this, ``_repr_*_()`` methods (except ``_repr_pretty_``?) can also return a ``(data, metadata)`` | |
139 | tuple where ``metadata`` is a dictionary containing arbitrary key-value pairs for |
|
139 | tuple where ``metadata`` is a dictionary containing arbitrary key-value pairs for | |
140 | the frontend to interpret. An example use case is ``_repr_jpeg_()``, which can |
|
140 | the frontend to interpret. An example use case is ``_repr_jpeg_()``, which can | |
141 | be set to return a jpeg image and a ``{'height': 400, 'width': 600}`` dictionary |
|
141 | be set to return a jpeg image and a ``{'height': 400, 'width': 600}`` dictionary |
@@ -10,7 +10,7 b' thought to render a number of mimetypes in the shell. This can be used to either' | |||||
10 | display inline images if your terminal emulator supports it; or open some |
|
10 | display inline images if your terminal emulator supports it; or open some | |
11 | display results with external file viewers. |
|
11 | display results with external file viewers. | |
12 |
|
12 | |||
13 |
Registering new mimetype handlers can so far only be done |
|
13 | Registering new mimetype handlers can so far only be done by extensions and | |
14 | requires 4 steps: |
|
14 | requires 4 steps: | |
15 |
|
15 | |||
16 | - Define a callable that takes 2 parameters:``data`` and ``metadata``; return |
|
16 | - Define a callable that takes 2 parameters:``data`` and ``metadata``; return |
@@ -17,9 +17,9 b' interactively. Its main components are:' | |||||
17 | * A powerful interactive Python shell. |
|
17 | * A powerful interactive Python shell. | |
18 |
|
18 | |||
19 |
|
19 | |||
20 | .. image:: ./_images/ipython-6-screenshot.png |
|
20 | .. image:: ./_images/ipython-6-screenshot.png | |
21 | :alt: Screenshot of IPython 6.0 |
|
21 | :alt: Screenshot of IPython 6.0 | |
22 | :align: center |
|
22 | :align: center | |
23 |
|
23 | |||
24 |
|
24 | |||
25 | * A `Jupyter <https://jupyter.org/>`_ kernel to work with Python code in Jupyter |
|
25 | * A `Jupyter <https://jupyter.org/>`_ kernel to work with Python code in Jupyter |
@@ -59,7 +59,7 b" while some other do not. We'll come to this later." | |||||
59 | Depending on the exact command you are typing you might realize that sometimes |
|
59 | Depending on the exact command you are typing you might realize that sometimes | |
60 | :kbd:`Enter` will add a new line, and sometimes it will execute the current |
|
60 | :kbd:`Enter` will add a new line, and sometimes it will execute the current | |
61 | statement. IPython tries to guess what you are doing, so most of the time you |
|
61 | statement. IPython tries to guess what you are doing, so most of the time you | |
62 | should not have to care. Though if by any chance IPython does not the right |
|
62 | should not have to care. Though if by any chance IPython does not do the right | |
63 | thing you can force execution of the current code block by pressing in sequence |
|
63 | thing you can force execution of the current code block by pressing in sequence | |
64 | :kbd:`Esc` and :kbd:`Enter`. You can also force the insertion of a new line at |
|
64 | :kbd:`Esc` and :kbd:`Enter`. You can also force the insertion of a new line at | |
65 | the position of the cursor by using :kbd:`Ctrl-o`. |
|
65 | the position of the cursor by using :kbd:`Ctrl-o`. |
@@ -20,9 +20,6 b'' | |||||
20 | .. _ipython: https://ipython.org |
|
20 | .. _ipython: https://ipython.org | |
21 | .. _`ipython manual`: https://ipython.org/documentation.html |
|
21 | .. _`ipython manual`: https://ipython.org/documentation.html | |
22 | .. _ipython_github: http://github.com/ipython/ipython/ |
|
22 | .. _ipython_github: http://github.com/ipython/ipython/ | |
23 | .. _ipython_github_repo: http://github.com/ipython/ipython/ |
|
|||
24 | .. _ipython_downloads: https://ipython.org/download.html |
|
|||
25 | .. _ipython_pypi: http://pypi.python.org/pypi/ipython |
|
|||
26 | .. _nbviewer: http://nbviewer.ipython.org |
|
23 | .. _nbviewer: http://nbviewer.ipython.org | |
27 |
|
24 | |||
28 | .. _ZeroMQ: http://zeromq.org |
|
25 | .. _ZeroMQ: http://zeromq.org | |
@@ -35,8 +32,7 b'' | |||||
35 | .. _reST: http://docutils.sourceforge.net/rst.html |
|
32 | .. _reST: http://docutils.sourceforge.net/rst.html | |
36 | .. _docutils: http://docutils.sourceforge.net |
|
33 | .. _docutils: http://docutils.sourceforge.net | |
37 | .. _lyx: http://www.lyx.org |
|
34 | .. _lyx: http://www.lyx.org | |
38 |
.. _pep8: http |
|
35 | .. _pep8: https://peps.python.org/pep-0008/ | |
39 | .. _numpy_coding_guide: https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt |
|
|||
40 |
|
36 | |||
41 | .. Licenses |
|
37 | .. Licenses | |
42 | .. _GPL: http://www.gnu.org/licenses/gpl.html |
|
38 | .. _GPL: http://www.gnu.org/licenses/gpl.html |
@@ -204,7 +204,7 b" suppress the seed line so it doesn't show up in the rendered output" | |||||
204 | [0.22591016, 0.77731835], |
|
204 | [0.22591016, 0.77731835], | |
205 | [0.0072729 , 0.34273127]]) |
|
205 | [0.0072729 , 0.34273127]]) | |
206 |
|
206 | |||
207 | For more information on @supress and @doctest decorators, please refer to the end of this file in |
|
207 | For more information on @suppress and @doctest decorators, please refer to the end of this file in | |
208 | Pseudo-Decorators section. |
|
208 | Pseudo-Decorators section. | |
209 |
|
209 | |||
210 | Another demonstration of multi-line input and output |
|
210 | Another demonstration of multi-line input and output |
@@ -51,9 +51,9 b' Pull requests (226):' | |||||
51 | * `542 <https://github.com/ipython/ipython/issues/542>`_: issue 440 |
|
51 | * `542 <https://github.com/ipython/ipython/issues/542>`_: issue 440 | |
52 | * `533 <https://github.com/ipython/ipython/issues/533>`_: Remove unused configobj and validate libraries from externals. |
|
52 | * `533 <https://github.com/ipython/ipython/issues/533>`_: Remove unused configobj and validate libraries from externals. | |
53 | * `538 <https://github.com/ipython/ipython/issues/538>`_: fix various tests on Windows |
|
53 | * `538 <https://github.com/ipython/ipython/issues/538>`_: fix various tests on Windows | |
54 | * `540 <https://github.com/ipython/ipython/issues/540>`_: support `-pylab` flag with deprecation warning |
|
54 | * `540 <https://github.com/ipython/ipython/issues/540>`_: support ``-pylab`` flag with deprecation warning | |
55 | * `537 <https://github.com/ipython/ipython/issues/537>`_: Docs update |
|
55 | * `537 <https://github.com/ipython/ipython/issues/537>`_: Docs update | |
56 | * `536 <https://github.com/ipython/ipython/issues/536>`_: `setup.py install` depends on setuptools on Windows |
|
56 | * `536 <https://github.com/ipython/ipython/issues/536>`_: ``setup.py install`` depends on setuptools on Windows | |
57 | * `480 <https://github.com/ipython/ipython/issues/480>`_: Get help mid-command |
|
57 | * `480 <https://github.com/ipython/ipython/issues/480>`_: Get help mid-command | |
58 | * `462 <https://github.com/ipython/ipython/issues/462>`_: Str and Bytes traitlets |
|
58 | * `462 <https://github.com/ipython/ipython/issues/462>`_: Str and Bytes traitlets | |
59 | * `534 <https://github.com/ipython/ipython/issues/534>`_: Handle unicode properly in IPython.zmq.iostream |
|
59 | * `534 <https://github.com/ipython/ipython/issues/534>`_: Handle unicode properly in IPython.zmq.iostream |
@@ -916,7 +916,7 b' Pull Requests (687):' | |||||
916 | * :ghpull:`4444`: Css cleaning |
|
916 | * :ghpull:`4444`: Css cleaning | |
917 | * :ghpull:`4523`: Use username and password for MongoDB on ShiningPanda |
|
917 | * :ghpull:`4523`: Use username and password for MongoDB on ShiningPanda | |
918 | * :ghpull:`4510`: Update whatsnew from PR files |
|
918 | * :ghpull:`4510`: Update whatsnew from PR files | |
919 | * :ghpull:`4441`: add `setup.py jsversion` |
|
919 | * :ghpull:`4441`: add ``setup.py jsversion`` | |
920 | * :ghpull:`4518`: Fix for race condition in url file decoding. |
|
920 | * :ghpull:`4518`: Fix for race condition in url file decoding. | |
921 | * :ghpull:`4497`: don't automatically unpack datetime objects in the message spec |
|
921 | * :ghpull:`4497`: don't automatically unpack datetime objects in the message spec | |
922 | * :ghpull:`4506`: wait for empty queues as well as load-balanced tasks |
|
922 | * :ghpull:`4506`: wait for empty queues as well as load-balanced tasks | |
@@ -1050,7 +1050,7 b' Pull Requests (687):' | |||||
1050 | * :ghpull:`4214`: engine ID metadata should be unicode, not bytes |
|
1050 | * :ghpull:`4214`: engine ID metadata should be unicode, not bytes | |
1051 | * :ghpull:`4232`: no highlight if no language specified |
|
1051 | * :ghpull:`4232`: no highlight if no language specified | |
1052 | * :ghpull:`4218`: Fix display of SyntaxError when .py file is modified |
|
1052 | * :ghpull:`4218`: Fix display of SyntaxError when .py file is modified | |
1053 | * :ghpull:`4207`: add `setup.py css` command |
|
1053 | * :ghpull:`4207`: add ``setup.py css`` command | |
1054 | * :ghpull:`4224`: clear previous callbacks on execute |
|
1054 | * :ghpull:`4224`: clear previous callbacks on execute | |
1055 | * :ghpull:`4180`: Iptest refactoring |
|
1055 | * :ghpull:`4180`: Iptest refactoring | |
1056 | * :ghpull:`4105`: JS output area misaligned |
|
1056 | * :ghpull:`4105`: JS output area misaligned |
@@ -81,7 +81,7 b' New features' | |||||
81 | :mod:`IPython.kernel`, :mod:`IPython.kernel.core`, :mod:`traitlets.config`, |
|
81 | :mod:`IPython.kernel`, :mod:`IPython.kernel.core`, :mod:`traitlets.config`, | |
82 | :mod:`IPython.tools` and :mod:`IPython.testing`. |
|
82 | :mod:`IPython.tools` and :mod:`IPython.testing`. | |
83 |
|
83 | |||
84 | * As part of merging in the `ipython1-dev` stuff, the `setup.py` script and |
|
84 | * As part of merging in the `ipython1-dev` stuff, the ``setup.py`` script and | |
85 | friends have been completely refactored. Now we are checking for |
|
85 | friends have been completely refactored. Now we are checking for | |
86 | dependencies using the approach that matplotlib uses. |
|
86 | dependencies using the approach that matplotlib uses. | |
87 |
|
87 | |||
@@ -161,7 +161,7 b' Backwards incompatible changes' | |||||
161 | `'basic'` to `'b'`. |
|
161 | `'basic'` to `'b'`. | |
162 |
|
162 | |||
163 | * IPython has a larger set of dependencies if you want all of its capabilities. |
|
163 | * IPython has a larger set of dependencies if you want all of its capabilities. | |
164 | See the `setup.py` script for details. |
|
164 | See the ``setup.py`` script for details. | |
165 |
|
165 | |||
166 | * The constructors for :class:`IPython.kernel.client.MultiEngineClient` and |
|
166 | * The constructors for :class:`IPython.kernel.client.MultiEngineClient` and | |
167 | :class:`IPython.kernel.client.TaskClient` no longer take the (ip,port) tuple. |
|
167 | :class:`IPython.kernel.client.TaskClient` no longer take the (ip,port) tuple. |
@@ -277,7 +277,7 b' automatically upgrade to the latest version compatible with your system.' | |||||
277 |
|
277 | |||
278 | The ability to use only Python 3 on the code base of IPython brings a number |
|
278 | The ability to use only Python 3 on the code base of IPython brings a number | |
279 | of advantages. Most of the newly written code make use of `optional function type |
|
279 | of advantages. Most of the newly written code make use of `optional function type | |
280 |
annotation <https:// |
|
280 | annotation <https://peps.python.org/pep-0484/>`_ leading to clearer code | |
281 | and better documentation. |
|
281 | and better documentation. | |
282 |
|
282 | |||
283 | The total size of the repository has also decreased by about 1500 lines (for the |
|
283 | The total size of the repository has also decreased by about 1500 lines (for the |
@@ -2,6 +2,17 b'' | |||||
2 | 7.x Series |
|
2 | 7.x Series | |
3 | ============ |
|
3 | ============ | |
4 |
|
4 | |||
|
5 | .. _version 7.33: | |||
|
6 | ||||
|
7 | IPython 7.33 | |||
|
8 | ============ | |||
|
9 | ||||
|
10 | - Allow IPython hooks to receive current cell ids when frontend support it. See | |||
|
11 | :ghpull:`13600` | |||
|
12 | ||||
|
13 | - ``?`` does not trigger the insertion of a new cell anymore as most frontend | |||
|
14 | allow proper multiline edition. :ghpull:`13625` | |||
|
15 | ||||
5 |
|
16 | |||
6 | .. _version 7.32: |
|
17 | .. _version 7.32: | |
7 |
|
18 | |||
@@ -9,11 +20,15 b' IPython 7.32' | |||||
9 | ============ |
|
20 | ============ | |
10 |
|
21 | |||
11 |
|
22 | |||
|
23 | ||||
|
24 | Autoload magic lazily | |||
|
25 | --------------------- | |||
|
26 | ||||
12 | The ability to configure magics to be lazily loaded has been added to IPython. |
|
27 | The ability to configure magics to be lazily loaded has been added to IPython. | |
13 | See the ``ipython --help-all`` section on ``MagicsManager.lazy_magic``. |
|
28 | See the ``ipython --help-all`` section on ``MagicsManager.lazy_magic``. | |
14 | One can now use:: |
|
29 | One can now use:: | |
15 |
|
30 | |||
16 | c.MagicsManger.lazy_magics = { |
|
31 | c.MagicsManager.lazy_magics = { | |
17 | "my_magic": "slow.to.import", |
|
32 | "my_magic": "slow.to.import", | |
18 | "my_other_magic": "also.slow", |
|
33 | "my_other_magic": "also.slow", | |
19 | } |
|
34 | } | |
@@ -21,6 +36,21 b' One can now use::' | |||||
21 | And on first use of ``%my_magic``, or corresponding cell magic, or other line magic, |
|
36 | And on first use of ``%my_magic``, or corresponding cell magic, or other line magic, | |
22 | the corresponding ``load_ext`` will be called just before trying to invoke the magic. |
|
37 | the corresponding ``load_ext`` will be called just before trying to invoke the magic. | |
23 |
|
38 | |||
|
39 | Misc | |||
|
40 | ---- | |||
|
41 | ||||
|
42 | - Update sphinxify for Docrepr 0.2.0 :ghpull:`13503`. | |||
|
43 | - Set co_name for cells run line by line (to fix debugging with Python 3.10) | |||
|
44 | :ghpull:`13535` | |||
|
45 | ||||
|
46 | ||||
|
47 | Many thanks to all the contributors to this release. You can find all individual | |||
|
48 | contributions to this milestone `on github | |||
|
49 | <https://github.com/ipython/ipython/milestone/99>`__. | |||
|
50 | ||||
|
51 | Thanks as well to the `D. E. Shaw group <https://deshaw.com/>`__ for sponsoring | |||
|
52 | work on IPython and related libraries. | |||
|
53 | ||||
24 | .. _version 7.31: |
|
54 | .. _version 7.31: | |
25 |
|
55 | |||
26 | IPython 7.31 |
|
56 | IPython 7.31 |
@@ -3,6 +3,134 b'' | |||||
3 | ============ |
|
3 | ============ | |
4 |
|
4 | |||
5 |
|
5 | |||
|
6 | .. _version 8.3.0: | |||
|
7 | ||||
|
8 | IPython 8.3.0 | |||
|
9 | ------------- | |||
|
10 | ||||
|
11 | - :ghpull:`13625`, using ``?``, ``??``, ``*?`` will not call | |||
|
12 | ``set_next_input`` as most frontend allow proper multiline editing and it was | |||
|
13 | causing issues for many users of multi-cell frontends. This has been backported to 7.33 | |||
|
14 | ||||
|
15 | ||||
|
16 | - :ghpull:`13600`, ``pre_run_*``-hooks will now have a ``cell_id`` attribute on | |||
|
17 | the info object when frontend provide it. This has been backported to 7.33 | |||
|
18 | ||||
|
19 | - :ghpull:`13624`, fixed :kbd:`End` key being broken after accepting an | |||
|
20 | auto-suggestion. | |||
|
21 | ||||
|
22 | - :ghpull:`13657` fix issue where history from different sessions would be mixed. | |||
|
23 | ||||
|
24 | .. _version 8.2.0: | |||
|
25 | ||||
|
26 | IPython 8.2.0 | |||
|
27 | ------------- | |||
|
28 | ||||
|
29 | IPython 8.2 mostly bring bugfixes to IPython. | |||
|
30 | ||||
|
31 | - Auto-suggestion can now be elected with the ``end`` key. :ghpull:`13566` | |||
|
32 | - Some traceback issues with ``assert etb is not None`` have been fixed. :ghpull:`13588` | |||
|
33 | - History is now pulled from the sqitel database and not from in-memory. | |||
|
34 | In particular when using the ``%paste`` magic, the content of the pasted text will | |||
|
35 | be part of the history and not the verbatim text ``%paste`` anymore. :ghpull:`13592` | |||
|
36 | - Fix ``Ctrl-\\`` exit cleanup :ghpull:`13603` | |||
|
37 | - Fixes to ``ultratb`` ipdb support when used outside of IPython. :ghpull:`13498` | |||
|
38 | ||||
|
39 | ||||
|
40 | I am still trying to fix and investigate :ghissue:`13598`, which seem to be | |||
|
41 | random, and would appreciate help if you find reproducible minimal case. I've | |||
|
42 | tried to make various changes to the codebase to mitigate it, but a proper fix | |||
|
43 | will be difficult without understanding the cause. | |||
|
44 | ||||
|
45 | ||||
|
46 | All the issues on pull-requests for this release can be found in the `8.2 | |||
|
47 | milestone. <https://github.com/ipython/ipython/milestone/100>`__ . And some | |||
|
48 | documentation only PR can be found as part of the `7.33 milestone | |||
|
49 | <https://github.com/ipython/ipython/milestone/101>`__ (currently not released). | |||
|
50 | ||||
|
51 | Thanks to the `D. E. Shaw group <https://deshaw.com/>`__ for sponsoring | |||
|
52 | work on IPython and related libraries. | |||
|
53 | ||||
|
54 | .. _version 8.1.1: | |||
|
55 | ||||
|
56 | IPython 8.1.1 | |||
|
57 | ------------- | |||
|
58 | ||||
|
59 | Fix an issue with virtualenv and Python 3.8 introduced in 8.1 | |||
|
60 | ||||
|
61 | Revert :ghpull:`13537` (fix an issue with symlinks in virtualenv) that raises an | |||
|
62 | error in Python 3.8, and fixed in a different way in :ghpull:`13559`. | |||
|
63 | ||||
|
64 | .. _version 8.1: | |||
|
65 | ||||
|
66 | IPython 8.1.0 | |||
|
67 | ------------- | |||
|
68 | ||||
|
69 | IPython 8.1 is the first minor release after 8.0 and fixes a number of bugs and | |||
|
70 | Update a few behavior that were problematic with the 8.0 as with many new major | |||
|
71 | release. | |||
|
72 | ||||
|
73 | Note that beyond the changes listed here, IPython 8.1.0 also contains all the | |||
|
74 | features listed in :ref:`version 7.32`. | |||
|
75 | ||||
|
76 | - Misc and multiple fixes around quotation auto-closing. It is now disabled by | |||
|
77 | default. Run with ``TerminalInteractiveShell.auto_match=True`` to re-enabled | |||
|
78 | - Require pygments>=2.4.0 :ghpull:`13459`, this was implicit in the code, but | |||
|
79 | is now explicit in ``setup.cfg``/``setup.py`` | |||
|
80 | - Docs improvement of ``core.magic_arguments`` examples. :ghpull:`13433` | |||
|
81 | - Multi-line edit executes too early with await. :ghpull:`13424` | |||
|
82 | ||||
|
83 | - ``black`` is back as an optional dependency, and autoformatting disabled by | |||
|
84 | default until some fixes are implemented (black improperly reformat magics). | |||
|
85 | :ghpull:`13471` Additionally the ability to use ``yapf`` as a code | |||
|
86 | reformatter has been added :ghpull:`13528` . You can use | |||
|
87 | ``TerminalInteractiveShell.autoformatter="black"``, | |||
|
88 | ``TerminalInteractiveShell.autoformatter="yapf"`` to re-enable auto formating | |||
|
89 | with black, or switch to yapf. | |||
|
90 | ||||
|
91 | - Fix and issue where ``display`` was not defined. | |||
|
92 | ||||
|
93 | - Auto suggestions are now configurable. Currently only | |||
|
94 | ``AutoSuggestFromHistory`` (default) and ``None``. new provider contribution | |||
|
95 | welcomed. :ghpull:`13475` | |||
|
96 | ||||
|
97 | - multiple packaging/testing improvement to simplify downstream packaging | |||
|
98 | (xfail with reasons, try to not access network...). | |||
|
99 | ||||
|
100 | - Update deprecation. ``InteractiveShell.magic`` internal method has been | |||
|
101 | deprecated for many years but did not emit a warning until now. | |||
|
102 | ||||
|
103 | - internal ``appended_to_syspath`` context manager has been deprecated. | |||
|
104 | ||||
|
105 | - fix an issue with symlinks in virtualenv :ghpull:`13537` (Reverted in 8.1.1) | |||
|
106 | ||||
|
107 | - Fix an issue with vim mode, where cursor would not be reset on exit :ghpull:`13472` | |||
|
108 | ||||
|
109 | - ipython directive now remove only known pseudo-decorators :ghpull:`13532` | |||
|
110 | ||||
|
111 | - ``IPython/lib/security`` which used to be used for jupyter notebook has been | |||
|
112 | removed. | |||
|
113 | ||||
|
114 | - Fix an issue where ``async with`` would execute on new lines. :ghpull:`13436` | |||
|
115 | ||||
|
116 | ||||
|
117 | We want to remind users that IPython is part of the Jupyter organisations, and | |||
|
118 | thus governed by a Code of Conduct. Some of the behavior we have seen on GitHub is not acceptable. | |||
|
119 | Abuse and non-respectful comments on discussion will not be tolerated. | |||
|
120 | ||||
|
121 | Many thanks to all the contributors to this release, many of the above fixed issue and | |||
|
122 | new features where done by first time contributors, showing there is still | |||
|
123 | plenty of easy contribution possible in IPython | |||
|
124 | . You can find all individual contributions | |||
|
125 | to this milestone `on github <https://github.com/ipython/ipython/milestone/91>`__. | |||
|
126 | ||||
|
127 | Thanks as well to the `D. E. Shaw group <https://deshaw.com/>`__ for sponsoring | |||
|
128 | work on IPython and related libraries. In particular the Lazy autoloading of | |||
|
129 | magics that you will find described in the 7.32 release notes. | |||
|
130 | ||||
|
131 | ||||
|
132 | .. _version 8.0.1: | |||
|
133 | ||||
6 | IPython 8.0.1 (CVE-2022-21699) |
|
134 | IPython 8.0.1 (CVE-2022-21699) | |
7 | ------------------------------ |
|
135 | ------------------------------ | |
8 |
|
136 | |||
@@ -45,6 +173,7 b' Thus starting with this version:' | |||||
45 | Further details can be read on the `GitHub Advisory <https://github.com/ipython/ipython/security/advisories/GHSA-pq7m-3gw7-gq5x>`__ |
|
173 | Further details can be read on the `GitHub Advisory <https://github.com/ipython/ipython/security/advisories/GHSA-pq7m-3gw7-gq5x>`__ | |
46 |
|
174 | |||
47 |
|
175 | |||
|
176 | .. _version 8.0: | |||
48 |
|
177 | |||
49 | IPython 8.0 |
|
178 | IPython 8.0 | |
50 | ----------- |
|
179 | ----------- | |
@@ -344,12 +473,11 b' For more information please see the following unit test : ``extensions/tests/tes' | |||||
344 | Auto formatting with black in the CLI |
|
473 | Auto formatting with black in the CLI | |
345 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
474 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
346 |
|
475 | |||
347 | If ``black`` is installed in the same environment as IPython, terminal IPython |
|
|||
348 | will now *by default* reformat the code in the CLI when possible. You can |
|
|||
349 | disable this with ``--TerminalInteractiveShell.autoformatter=None``. |
|
|||
350 |
|
||||
351 | This feature was present in 7.x, but disabled by default. |
|
476 | This feature was present in 7.x, but disabled by default. | |
352 |
|
477 | |||
|
478 | In 8.0, input was automatically reformatted with Black when black was installed. | |||
|
479 | This feature has been reverted for the time being. | |||
|
480 | You can re-enable it by setting ``TerminalInteractiveShell.autoformatter`` to ``"black"`` | |||
353 |
|
481 | |||
354 | History Range Glob feature |
|
482 | History Range Glob feature | |
355 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
483 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
@@ -393,8 +521,8 b' Automatic Vi prompt stripping' | |||||
393 |
|
521 | |||
394 | When pasting code into IPython, it will strip the leading prompt characters if |
|
522 | When pasting code into IPython, it will strip the leading prompt characters if | |
395 | there are any. For example, you can paste the following code into the console - |
|
523 | there are any. For example, you can paste the following code into the console - | |
396 | it will still work, even though each line is prefixed with prompts (`In`, |
|
524 | it will still work, even though each line is prefixed with prompts (``In``, | |
397 | `Out`):: |
|
525 | ``Out``):: | |
398 |
|
526 | |||
399 | In [1]: 2 * 2 == 4 |
|
527 | In [1]: 2 * 2 == 4 | |
400 | Out[1]: True |
|
528 | Out[1]: True | |
@@ -489,7 +617,7 b' who did a fantastic job at updating our code base, migrating to pytest, pushing' | |||||
489 | our coverage, and fixing a large number of bugs. I highly recommend contacting |
|
617 | our coverage, and fixing a large number of bugs. I highly recommend contacting | |
490 | them if you need help with C++ and Python projects. |
|
618 | them if you need help with C++ and Python projects. | |
491 |
|
619 | |||
492 |
You can find all relevant issues and PRs with the SDG 2021 tag |
|
620 | You can find all relevant issues and PRs with `the SDG 2021 tag <https://github.com/ipython/ipython/issues?q=label%3A%22Numfocus+SDG+2021%22+>`__ | |
493 |
|
621 | |||
494 | Removing support for older Python versions |
|
622 | Removing support for older Python versions | |
495 | ------------------------------------------ |
|
623 | ------------------------------------------ |
@@ -15,7 +15,7 b'' | |||||
15 | "This is made difficult by the fact that Notebooks are not plain Python files,\n", |
|
15 | "This is made difficult by the fact that Notebooks are not plain Python files,\n", | |
16 | "and thus cannot be imported by the regular Python machinery.\n", |
|
16 | "and thus cannot be imported by the regular Python machinery.\n", | |
17 | "\n", |
|
17 | "\n", | |
18 |
"Fortunately, Python provides some fairly sophisticated [hooks](http |
|
18 | "Fortunately, Python provides some fairly sophisticated [hooks](https://peps.python.org/pep-0302/) into the import machinery,\n", | |
19 | "so we can actually make IPython notebooks importable without much difficulty,\n", |
|
19 | "so we can actually make IPython notebooks importable without much difficulty,\n", | |
20 | "and only using public APIs." |
|
20 | "and only using public APIs." | |
21 | ] |
|
21 | ] |
@@ -45,3 +45,4 b' addopts = --durations=10' | |||||
45 | --ignore=IPython/utils/version.py |
|
45 | --ignore=IPython/utils/version.py | |
46 | doctest_optionflags = NORMALIZE_WHITESPACE ELLIPSIS |
|
46 | doctest_optionflags = NORMALIZE_WHITESPACE ELLIPSIS | |
47 | ipdoctest_optionflags = NORMALIZE_WHITESPACE ELLIPSIS |
|
47 | ipdoctest_optionflags = NORMALIZE_WHITESPACE ELLIPSIS | |
|
48 | asyncio_mode = strict |
@@ -63,17 +63,16 b' qtconsole =' | |||||
63 | qtconsole |
|
63 | qtconsole | |
64 | terminal = |
|
64 | terminal = | |
65 | test = |
|
65 | test = | |
66 | pytest<7 |
|
66 | pytest<7.1 | |
67 | pytest-asyncio |
|
67 | pytest-asyncio | |
68 | testpath |
|
68 | testpath | |
69 | test_extra = |
|
69 | test_extra = | |
|
70 | %(test)s | |||
70 | curio |
|
71 | curio | |
71 | matplotlib!=3.2.0 |
|
72 | matplotlib!=3.2.0 | |
72 | nbformat |
|
73 | nbformat | |
73 | numpy>=1.19 |
|
74 | numpy>=1.19 | |
74 | pandas |
|
75 | pandas | |
75 | pytest<7 |
|
|||
76 | testpath |
|
|||
77 | trio |
|
76 | trio | |
78 | all = |
|
77 | all = | |
79 | %(black)s |
|
78 | %(black)s |
@@ -18,7 +18,6 b' requires utilities which are not available under Windows."""' | |||||
18 |
|
18 | |||
19 | import os |
|
19 | import os | |
20 | import sys |
|
20 | import sys | |
21 | from itertools import chain |
|
|||
22 |
|
21 | |||
23 | # **Python version check** |
|
22 | # **Python version check** | |
24 | # |
|
23 | # | |
@@ -53,7 +52,9 b' See IPython `README.rst` file for more information:' | |||||
53 |
|
52 | |||
54 | Python {py} detected. |
|
53 | Python {py} detected. | |
55 | {pip} |
|
54 | {pip} | |
56 | """.format(py=sys.version_info, pip=pip_message ) |
|
55 | """.format( | |
|
56 | py=sys.version_info, pip=pip_message | |||
|
57 | ) | |||
57 |
|
58 | |||
58 | print(error, file=sys.stderr) |
|
59 | print(error, file=sys.stderr) | |
59 | sys.exit(1) |
|
60 | sys.exit(1) |
@@ -111,7 +111,7 b' then' | |||||
111 | sleep 1 |
|
111 | sleep 1 | |
112 | echo $BLUE"Saving API to file $PREV_RELEASE"$NOR |
|
112 | echo $BLUE"Saving API to file $PREV_RELEASE"$NOR | |
113 | frappuccino IPython IPython.kernel IPython.lib IPython.qt IPython.lib.kernel IPython.html IPython.frontend IPython.external --save IPython-$PREV_RELEASE.json |
|
113 | frappuccino IPython IPython.kernel IPython.lib IPython.qt IPython.lib.kernel IPython.html IPython.frontend IPython.external --save IPython-$PREV_RELEASE.json | |
114 |
echo $BLUE"com |
|
114 | echo $BLUE"coming back to $BRANCH"$NOR | |
115 | git checkout $BRANCH |
|
115 | git checkout $BRANCH | |
116 | sleep 1 |
|
116 | sleep 1 | |
117 | echo $BLUE"comparing ..."$NOR |
|
117 | echo $BLUE"comparing ..."$NOR |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now