##// END OF EJS Templates
Merge branch 'master' into alt_text
blois -
r26808:a09e5776 merge
parent child Browse files
Show More
@@ -0,0 +1,26 b''
1 name: Build docs
2
3 on: [push, pull_request]
4
5 jobs:
6 build:
7 runs-on: ubuntu-latest
8
9 steps:
10 - uses: actions/checkout@v2
11 - name: Set up Python 3.8
12 uses: actions/setup-python@v2
13 with:
14 python-version: 3.8
15 - name: Install Graphviz
16 run: |
17 sudo apt-get update
18 sudo apt-get install graphviz
19 - name: Install Python dependencies
20 run: |
21 python -m pip install --upgrade pip setuptools
22 pip install -r docs/requirements.txt
23 - name: Build docs
24 run: |
25 python tools/fixup_whats_new_pr.py
26 make -C docs/ html SPHINXOPTS="-W"
@@ -0,0 +1,23 b''
1 name: Run tests on OSX
2
3 on: [push, pull_request]
4
5 jobs:
6 test:
7 runs-on: macos-latest
8
9 steps:
10 - uses: actions/checkout@v2
11 - name: Set up Python 3.7
12 uses: actions/setup-python@v2
13 with:
14 python-version: 3.7
15 - name: Install and update Python dependencies
16 run: |
17 python -m pip install --upgrade pip setuptools wheel
18 python -m pip install --upgrade -e file://$PWD#egg=ipython[test]
19 python -m pip install --upgrade --upgrade-strategy eager trio curio
20 python -m pip install --upgrade pytest pytest-trio 'matplotlib!=3.2.0'
21 python -m pip install --upgrade anyio
22 - name: pytest
23 run: pytest
@@ -0,0 +1,36 b''
1 name: Run tests
2
3 on: [push, pull_request]
4
5 jobs:
6 test:
7 runs-on: ubuntu-latest
8 strategy:
9 matrix:
10 python-version: [3.7, 3.8, 3.9]
11
12 steps:
13 - uses: actions/checkout@v2
14 - name: Set up Python ${{ matrix.python-version }}
15 uses: actions/setup-python@v2
16 with:
17 python-version: ${{ matrix.python-version }}
18 - name: Install and update Python dependencies
19 run: |
20 python -m pip install --upgrade pip setuptools wheel
21 python -m pip install --upgrade -e file://$PWD#egg=ipython[test]
22 python -m pip install --upgrade --upgrade-strategy eager trio curio
23 python -m pip install --upgrade pytest pytest-trio 'matplotlib!=3.2.0'
24 python -m pip install --upgrade check-manifest pytest-cov anyio
25 - name: Check manifest
26 run: check-manifest
27 - name: iptest
28 run: |
29 cd /tmp && iptest --coverage xml && cd -
30 cp /tmp/ipy_coverage.xml ./
31 cp /tmp/.coverage ./
32 - name: pytest
33 run: |
34 pytest
35 - name: Upload coverage to Codecov
36 uses: codecov/codecov-action@v1
@@ -0,0 +1,43 b''
1 """
2 Enable Gtk4 to be used interactively by IPython.
3 """
4 # -----------------------------------------------------------------------------
5 # Copyright (c) 2021, the IPython Development Team.
6 #
7 # Distributed under the terms of the Modified BSD License.
8 #
9 # The full license is in the file COPYING.txt, distributed with this software.
10 # -----------------------------------------------------------------------------
11
12 # -----------------------------------------------------------------------------
13 # Imports
14 # -----------------------------------------------------------------------------
15
16 import sys
17
18 from gi.repository import GLib
19
20 # -----------------------------------------------------------------------------
21 # Code
22 # -----------------------------------------------------------------------------
23
24
25 class _InputHook:
26 def __init__(self, context):
27 self._quit = False
28 GLib.io_add_watch(sys.stdin, GLib.PRIORITY_DEFAULT, GLib.IO_IN, self.quit)
29
30 def quit(self, *args, **kwargs):
31 self._quit = True
32 return False
33
34 def run(self):
35 context = GLib.MainContext.default()
36 while not self._quit:
37 context.iteration(True)
38
39
40 def inputhook_gtk4():
41 hook = _InputHook()
42 hook.run()
43 return 0
@@ -0,0 +1,27 b''
1 """
2 prompt_toolkit input hook for GTK 4.
3 """
4
5 from gi.repository import GLib
6
7
8 class _InputHook:
9 def __init__(self, context):
10 self._quit = False
11 GLib.io_add_watch(
12 context.fileno(), GLib.PRIORITY_DEFAULT, GLib.IO_IN, self.quit
13 )
14
15 def quit(self, *args, **kwargs):
16 self._quit = True
17 return False
18
19 def run(self):
20 context = GLib.MainContext.default()
21 while not self._quit:
22 context.iteration(True)
23
24
25 def inputhook(context):
26 hook = _InputHook(context)
27 hook.run()
@@ -0,0 +1,7 b''
1 {%- extends "sphinx_rtd_theme/breadcrumbs.html" %}
2
3 {% block breadcrumbs_aside %}
4 {% if not meta or meta.get('github_url') != 'hide' %}
5 {{ super() }}
6 {% endif %}
7 {% endblock %}
@@ -0,0 +1,19 b''
1 Empty History Ranges
2 ====================
3
4 A number of magics that take history ranges can now be used with an empty
5 range. These magics are:
6
7 * ``%save``
8 * ``%load``
9 * ``%pastebin``
10 * ``%pycat``
11
12 Using them this way will make them take the history of the current session up
13 to the point of the magic call (such that the magic itself will not be
14 included).
15
16 Therefore it is now possible to save the whole history to a file using simple
17 ``%save <filename>``, load and edit it using ``%load`` (makes for a nice usage
18 when followed with :kbd:`F2`), send it to dpaste.org using ``%pastebin``, or
19 view the whole thing syntax-highlighted with a single ``%pycat``.
@@ -0,0 +1,38 b''
1 ``YouTubeVideo`` autoplay and the ability to add extra attributes to ``IFrame``
2 ===============================================================================
3
4 You can add any extra attributes to the ``<iframe>`` tag using the new
5 ``extras`` argument in the ``IFrame`` class. For example::
6
7 In [1]: from IPython.display import IFrame
8
9 In [2]: IFrame(src="src", width=300, height=300, extras=['loading="eager"'])
10
11 The above cells will result in the following HTML code being displayed in a
12 notebook::
13
14 <iframe
15 width="300"
16 height="300"
17 src="src"
18 frameborder="0"
19 allowfullscreen
20 loading="eager"
21 ></iframe>
22
23 Related to the above, the ``YouTubeVideo`` class now takes an
24 ``allow_autoplay`` flag, which sets up the iframe of the embedded YouTube video
25 such that it allows autoplay.
26
27 .. note::
28 Whether this works depends on the autoplay policy of the browser rendering
29 the HTML allowing it. It also could get blocked by some browser extensions.
30
31 Try it out!
32 ::
33
34 In [1]: from IPython.display import YouTubeVideo
35
36 In [2]: YouTubeVideo("dQw4w9WgXcQ", allow_autoplay=True)
37
38 🙃
@@ -0,0 +1,4 b''
1 IPython shell for ipdb interact
2 -------------------------------
3
4 The ipdb ``interact`` starts an IPython shell instead of Python's built-in ``code.interact()``.
@@ -0,0 +1,7 b''
1 Pastebin magic expiry days option
2 =================================
3
4 The Pastebin magic now has ``-e`` option to determine
5 the number of days for paste expiration. For example
6 the paste that created with ``%pastebin -e 20 1`` magic will
7 be available for next 20 days.
@@ -0,0 +1,7 b''
1 Don't start a multiline cell with sunken parenthesis
2 ----------------------------------------------------
3
4 From now on IPython will not ask for the next line of input when given a single
5 line with more closing than opening brackets. For example, this means that if
6 you (mis)type ']]' instead of '[]', a ``SyntaxError`` will show up, instead of
7 the ``...:`` prompt continuation.
@@ -0,0 +1,39 b''
1 Traceback improvements
2 ======================
3
4 Previously, error tracebacks for errors happening in code cells were showing a hash, the one used for compiling the Python AST::
5
6 In [1]: def foo():
7 ...: return 3 / 0
8 ...:
9
10 In [2]: foo()
11 ---------------------------------------------------------------------------
12 ZeroDivisionError Traceback (most recent call last)
13 <ipython-input-2-c19b6d9633cf> in <module>
14 ----> 1 foo()
15
16 <ipython-input-1-1595a74c32d5> in foo()
17 1 def foo():
18 ----> 2 return 3 / 0
19 3
20
21 ZeroDivisionError: division by zero
22
23 The error traceback is now correctly formatted, showing the cell number in which the error happened::
24
25 In [1]: def foo():
26 ...: return 3 / 0
27 ...:
28
29 In [2]: foo()
30 ---------------------------------------------------------------------------
31 ZeroDivisionError Traceback (most recent call last)
32 In [2], in <module>
33 ----> 1 foo()
34
35 In [1], in foo()
36 1 def foo():
37 ----> 2 return 3 / 0
38
39 ZeroDivisionError: division by zero
@@ -0,0 +1,29 b''
1 Automatic Vi prompt stripping
2 =============================
3
4 When pasting code into IPython, it will strip the leading prompt characters if
5 there are any. For example, you can paste the following code into the console -
6 it will still work, even though each line is prefixed with prompts (`In`,
7 `Out`)::
8
9 In [1]: 2 * 2 == 4
10 Out[1]: True
11
12 In [2]: print("This still works as pasted")
13
14
15 Previously, this was not the case for the Vi-mode prompts::
16
17 In [1]: [ins] In [13]: 2 * 2 == 4
18 ...: Out[13]: True
19 ...:
20 File "<ipython-input-1-727bb88eaf33>", line 1
21 [ins] In [13]: 2 * 2 == 4
22 ^
23 SyntaxError: invalid syntax
24
25 This is now fixed, and Vi prompt prefixes - ``[ins]`` and ``[nav]`` - are
26 skipped just as the normal ``In`` would be.
27
28 IPython shell can be started in the Vi mode using ``ipython
29 --TerminalInteractiveShell.editing_mode=vi``
@@ -0,0 +1,37 b''
1 #!/usr/bin/env python
2 """Simple Gtk example to manually test event loop integration.
3
4 This is meant to run tests manually in ipython as:
5
6 In [1]: %gui gtk4
7
8 In [2]: %run gui-gtk4.py
9 """
10
11 import gi
12
13 gi.require_version("Gtk", "4.0")
14 from gi.repository import Gtk, GLib # noqa
15
16
17 def hello_world(wigdet, data=None):
18 print("Hello World")
19
20
21 def close_request_cb(widget, data=None):
22 global running
23 running = False
24
25
26 running = True
27 window = Gtk.Window()
28 window.connect("close-request", close_request_cb)
29 button = Gtk.Button(label="Hello World")
30 button.connect("clicked", hello_world, None)
31
32 window.set_child(button)
33 window.show()
34
35 context = GLib.MainContext.default()
36 while running:
37 context.iteration(True)
@@ -13,3 +13,9 b''
13 # <full commit hash> # initial black-format
13 # <full commit hash> # initial black-format
14 # <full commit hash> # rename something internal
14 # <full commit hash> # rename something internal
15 6e748726282d1acb9a4f9f264ee679c474c4b8f5 # Apply pygrade --36plus on IPython/core/tests/test_inputtransformer.py.
15 6e748726282d1acb9a4f9f264ee679c474c4b8f5 # Apply pygrade --36plus on IPython/core/tests/test_inputtransformer.py.
16 0233e65d8086d0ec34acb8685b7a5411633f0899 # apply pyupgrade to IPython/extensions/tests/test_autoreload.py
17 a6a7e4dd7e51b892147895006d3a2a6c34b79ae6 # apply black to IPython/extensions/tests/test_autoreload.py
18 c5ca5a8f25432dfd6b9eccbbe446a8348bf37cfa # apply pyupgrade to IPython/extensions/autoreload.py
19 50624b84ccdece781750f5eb635a9efbf2fe30d6 # apply black to IPython/extensions/autoreload.py
20 b7aaa47412b96379198705955004930c57f9d74a # apply pyupgrade to IPython/extensions/autoreload.py
21 9c7476a88af3e567426b412f1b3c778401d8f6aa # apply black to IPython/extensions/autoreload.py
@@ -13,7 +13,4 b" If it's a generic Python/Jupyter question, try other forums or discourse.jupyter"
13
13
14 If you are unsure, it's ok to post here, though, there are few maintainer so you might not get a fast response.
14 If you are unsure, it's ok to post here, though, there are few maintainer so you might not get a fast response.
15
15
16 Ability of maintainers to spend time and resources on project like IPython is heavily influenced by US politics, and the current government policies have been harmful to the IPython Maintainers and Community.
17
18 If you are on the fence on who to vote for or wether to vote, please cast your vote in for the democrat party in the US.
19 -->
16 -->
@@ -2,9 +2,9 b' name: Run MyPy'
2
2
3 on:
3 on:
4 push:
4 push:
5 branches: [ master ]
5 branches: [ master, 7.x]
6 pull_request:
6 pull_request:
7 branches: [ master ]
7 branches: [ master, 7.x]
8
8
9 jobs:
9 jobs:
10 build:
10 build:
@@ -26,8 +26,9 b' jobs:'
26 pip install mypy pyflakes flake8
26 pip install mypy pyflakes flake8
27 - name: Lint with mypy
27 - name: Lint with mypy
28 run: |
28 run: |
29 mypy IPython/terminal/ptutils.py
29 mypy -p IPython.terminal
30 mypy IPython/core/c*.py
30 mypy -p IPython.core.magics
31 - name: Lint with pyflakes
31 - name: Lint with pyflakes
32 run: |
32 run: |
33 flake8 IPython/core/magics/script.py
33 flake8 IPython/core/magics/script.py
34 flake8 IPython/core/magics/packaging.py
@@ -5,9 +5,9 b' name: Python package'
5
5
6 on:
6 on:
7 push:
7 push:
8 branches: [ master ]
8 branches: [ master, 7.x ]
9 pull_request:
9 pull_request:
10 branches: [ master ]
10 branches: [ master, 7.x ]
11
11
12 jobs:
12 jobs:
13 build:
13 build:
@@ -28,7 +28,7 b' jobs:'
28 - name: Install dependencies
28 - name: Install dependencies
29 run: |
29 run: |
30 python -m pip install --upgrade pip
30 python -m pip install --upgrade pip
31 pip install darker
31 pip install darker isort
32 - name: Lint with darker
32 - name: Lint with darker
33 run: |
33 run: |
34 darker -r 60625f241f298b5039cb2debc365db38aa7bb522 --check --diff . || (
34 darker -r 60625f241f298b5039cb2debc365db38aa7bb522 --check --diff . || (
@@ -6,6 +6,7 b' docs/man/*.gz'
6 docs/source/api/generated
6 docs/source/api/generated
7 docs/source/config/options
7 docs/source/config/options
8 docs/source/config/shortcuts/*.csv
8 docs/source/config/shortcuts/*.csv
9 docs/source/savefig
9 docs/source/interactive/magics-generated.txt
10 docs/source/interactive/magics-generated.txt
10 docs/gh-pages
11 docs/gh-pages
11 jupyter_notebook/notebook/static/mathjax
12 jupyter_notebook/notebook/static/mathjax
@@ -28,3 +29,4 b' __pycache__'
28 .python-version
29 .python-version
29 venv*/
30 venv*/
30 .idea/
31 .idea/
32 .mypy_cache/
@@ -80,8 +80,7 b' def embed_kernel(module=None, local_ns=None, **kwargs):'
80 The module to load into IPython globals (default: caller)
80 The module to load into IPython globals (default: caller)
81 local_ns : dict, optional
81 local_ns : dict, optional
82 The namespace to load into IPython user namespace (default: caller)
82 The namespace to load into IPython user namespace (default: caller)
83
83 **kwargs : various, optional
84 kwargs : various, optional
85 Further keyword args are relayed to the IPKernelApp constructor,
84 Further keyword args are relayed to the IPKernelApp constructor,
86 allowing configuration of the Kernel. Will only have an effect
85 allowing configuration of the Kernel. Will only have an effect
87 on the first embed_kernel call for a given process.
86 on the first embed_kernel call for a given process.
@@ -112,13 +111,12 b' def start_ipython(argv=None, **kwargs):'
112
111
113 Parameters
112 Parameters
114 ----------
113 ----------
115
116 argv : list or None, optional
114 argv : list or None, optional
117 If unspecified or None, IPython will parse command-line options from sys.argv.
115 If unspecified or None, IPython will parse command-line options from sys.argv.
118 To prevent any command-line parsing, pass an empty list: `argv=[]`.
116 To prevent any command-line parsing, pass an empty list: `argv=[]`.
119 user_ns : dict, optional
117 user_ns : dict, optional
120 specify this dictionary to initialize the IPython user namespace with particular values.
118 specify this dictionary to initialize the IPython user namespace with particular values.
121 kwargs : various, optional
119 **kwargs : various, optional
122 Any other kwargs will be passed to the Application constructor,
120 Any other kwargs will be passed to the Application constructor,
123 such as `config`.
121 such as `config`.
124 """
122 """
@@ -138,13 +136,12 b' def start_kernel(argv=None, **kwargs):'
138
136
139 Parameters
137 Parameters
140 ----------
138 ----------
141
142 argv : list or None, optional
139 argv : list or None, optional
143 If unspecified or None, IPython will parse command-line options from sys.argv.
140 If unspecified or None, IPython will parse command-line options from sys.argv.
144 To prevent any command-line parsing, pass an empty list: `argv=[]`.
141 To prevent any command-line parsing, pass an empty list: `argv=[]`.
145 user_ns : dict, optional
142 user_ns : dict, optional
146 specify this dictionary to initialize the IPython user namespace with particular values.
143 specify this dictionary to initialize the IPython user namespace with particular values.
147 kwargs : various, optional
144 **kwargs : various, optional
148 Any other kwargs will be passed to the Application constructor,
145 Any other kwargs will be passed to the Application constructor,
149 such as `config`.
146 such as `config`.
150 """
147 """
@@ -66,26 +66,48 b' else:'
66
66
67 # aliases and flags
67 # aliases and flags
68
68
69 base_aliases = {
69 base_aliases = {}
70 'profile-dir' : 'ProfileDir.location',
70 if isinstance(Application.aliases, dict):
71 'profile' : 'BaseIPythonApplication.profile',
71 # traitlets 5
72 'ipython-dir' : 'BaseIPythonApplication.ipython_dir',
72 base_aliases.update(Application.aliases)
73 'log-level' : 'Application.log_level',
73 base_aliases.update(
74 'config' : 'BaseIPythonApplication.extra_config_file',
74 {
75 "profile-dir": "ProfileDir.location",
76 "profile": "BaseIPythonApplication.profile",
77 "ipython-dir": "BaseIPythonApplication.ipython_dir",
78 "log-level": "Application.log_level",
79 "config": "BaseIPythonApplication.extra_config_file",
75 }
80 }
81 )
76
82
77 base_flags = dict(
83 base_flags = dict()
78 debug = ({'Application' : {'log_level' : logging.DEBUG}},
84 if isinstance(Application.flags, dict):
79 "set log level to logging.DEBUG (maximize logging output)"),
85 # traitlets 5
80 quiet = ({'Application' : {'log_level' : logging.CRITICAL}},
86 base_flags.update(Application.flags)
81 "set log level to logging.CRITICAL (minimize logging output)"),
87 base_flags.update(
82 init = ({'BaseIPythonApplication' : {
88 dict(
83 'copy_config_files' : True,
89 debug=(
84 'auto_create' : True}
90 {"Application": {"log_level": logging.DEBUG}},
85 }, """Initialize profile with default config files. This is equivalent
91 "set log level to logging.DEBUG (maximize logging output)",
92 ),
93 quiet=(
94 {"Application": {"log_level": logging.CRITICAL}},
95 "set log level to logging.CRITICAL (minimize logging output)",
96 ),
97 init=(
98 {
99 "BaseIPythonApplication": {
100 "copy_config_files": True,
101 "auto_create": True,
102 }
103 },
104 """Initialize profile with default config files. This is equivalent
86 to running `ipython profile create <profile>` prior to startup.
105 to running `ipython profile create <profile>` prior to startup.
87 """)
106 """,
107 ),
88 )
108 )
109 )
110
89
111
90 class ProfileAwareConfigLoader(PyFileConfigLoader):
112 class ProfileAwareConfigLoader(PyFileConfigLoader):
91 """A Python file config loader that is aware of IPython profiles."""
113 """A Python file config loader that is aware of IPython profiles."""
1 NO CONTENT: modified file
NO CONTENT: modified file
@@ -92,6 +92,10 b' class CachingCompiler(codeop.Compile):'
92 # (otherwise we'd lose our tracebacks).
92 # (otherwise we'd lose our tracebacks).
93 linecache.checkcache = check_linecache_ipython
93 linecache.checkcache = check_linecache_ipython
94
94
95 # Caching a dictionary { filename: execution_count } for nicely
96 # rendered tracebacks. The filename corresponds to the filename
97 # argument used for the builtins.compile function.
98 self._filename_map = {}
95
99
96 def ast_parse(self, source, filename='<unknown>', symbol='exec'):
100 def ast_parse(self, source, filename='<unknown>', symbol='exec'):
97 """Parse code to an AST with the current compiler flags active.
101 """Parse code to an AST with the current compiler flags active.
@@ -153,6 +157,10 b' class CachingCompiler(codeop.Compile):'
153 raw_code = transformed_code
157 raw_code = transformed_code
154
158
155 name = self.get_code_name(raw_code, transformed_code, number)
159 name = self.get_code_name(raw_code, transformed_code, number)
160
161 # Save the execution count
162 self._filename_map[name] = number
163
156 entry = (
164 entry = (
157 len(transformed_code),
165 len(transformed_code),
158 time.time(),
166 time.time(),
@@ -763,11 +763,11 b' def match_dict_keys(keys: List[Union[str, bytes, Tuple[Union[str, bytes]]]], pre'
763
763
764 Parameters
764 Parameters
765 ----------
765 ----------
766 keys:
766 keys
767 list of keys in dictionary currently being completed.
767 list of keys in dictionary currently being completed.
768 prefix:
768 prefix
769 Part of the text already typed by the user. E.g. `mydict[b'fo`
769 Part of the text already typed by the user. E.g. `mydict[b'fo`
770 delims:
770 delims
771 String of delimiters to consider when finding the current key.
771 String of delimiters to consider when finding the current key.
772 extra_prefix: optional
772 extra_prefix : optional
773 Part of the text already typed in multi-key index cases. E.g. for
773 Part of the text already typed in multi-key index cases. E.g. for
@@ -993,7 +993,7 b' def _formatparamchildren(parameter) -> str:'
993
993
994 Parameters
994 Parameters
995 ----------
995 ----------
996 parameter:
996 parameter
997 Jedi's function `Param`
997 Jedi's function `Param`
998
998
999 Returns
999 Returns
@@ -154,6 +154,17 b' def is_importable(module, attr, only_modules):'
154 else:
154 else:
155 return not(attr[:2] == '__' and attr[-2:] == '__')
155 return not(attr[:2] == '__' and attr[-2:] == '__')
156
156
157 def is_possible_submodule(module, attr):
158 try:
159 obj = getattr(module, attr)
160 except AttributeError:
161 # Is possilby an unimported submodule
162 return True
163 except TypeError:
164 # https://github.com/ipython/ipython/issues/9678
165 return False
166 return inspect.ismodule(obj)
167
157
168
158 def try_import(mod: str, only_modules=False) -> List[str]:
169 def try_import(mod: str, only_modules=False) -> List[str]:
159 """
170 """
@@ -172,7 +183,12 b' def try_import(mod: str, only_modules=False) -> List[str]:'
172 completions.extend( [attr for attr in dir(m) if
183 completions.extend( [attr for attr in dir(m) if
173 is_importable(m, attr, only_modules)])
184 is_importable(m, attr, only_modules)])
174
185
175 completions.extend(getattr(m, '__all__', []))
186 m_all = getattr(m, "__all__", [])
187 if only_modules:
188 completions.extend(attr for attr in m_all if is_possible_submodule(m, attr))
189 else:
190 completions.extend(m_all)
191
176 if m_is_init:
192 if m_is_init:
177 completions.extend(module_list(os.path.dirname(m.__file__)))
193 completions.extend(module_list(os.path.dirname(m.__file__)))
178 completions_set = {c for c in completions if isinstance(c, str)}
194 completions_set = {c for c in completions if isinstance(c, str)}
@@ -104,22 +104,16 b' class CrashHandler(object):'
104 app : Application
104 app : Application
105 A running :class:`Application` instance, which will be queried at
105 A running :class:`Application` instance, which will be queried at
106 crash time for internal information.
106 crash time for internal information.
107
108 contact_name : str
107 contact_name : str
109 A string with the name of the person to contact.
108 A string with the name of the person to contact.
110
111 contact_email : str
109 contact_email : str
112 A string with the email address of the contact.
110 A string with the email address of the contact.
113
114 bug_tracker : str
111 bug_tracker : str
115 A string with the URL for your project's bug tracker.
112 A string with the URL for your project's bug tracker.
116
117 show_crash_traceback : bool
113 show_crash_traceback : bool
118 If false, don't print the crash traceback on stderr, only generate
114 If false, don't print the crash traceback on stderr, only generate
119 the on-disk report
115 the on-disk report
120
116 Non-argument instance attributes
121 Non-argument instance attributes:
122
123 These instances contain some non-argument attributes which allow for
117 These instances contain some non-argument attributes which allow for
124 further customization of the crash handler's behavior. Please see the
118 further customization of the crash handler's behavior. Please see the
125 source for further details.
119 source for further details.
@@ -33,6 +33,7 b' import linecache'
33 import sys
33 import sys
34 import warnings
34 import warnings
35 import re
35 import re
36 import os
36
37
37 from IPython import get_ipython
38 from IPython import get_ipython
38 from IPython.utils import PyColorize
39 from IPython.utils import PyColorize
@@ -109,7 +110,6 b' class Tracer(object):'
109
110
110 Parameters
111 Parameters
111 ----------
112 ----------
112
113 colors : str, optional
113 colors : str, optional
114 The name of the color scheme to use, it must be one of IPython's
114 The name of the color scheme to use, it must be one of IPython's
115 valid color schemes. If not given, the function will default to
115 valid color schemes. If not given, the function will default to
@@ -199,19 +199,37 b' class Pdb(OldPdb):'
199 for a standalone version that uses prompt_toolkit, see
199 for a standalone version that uses prompt_toolkit, see
200 `IPython.terminal.debugger.TerminalPdb` and
200 `IPython.terminal.debugger.TerminalPdb` and
201 `IPython.terminal.debugger.set_trace()`
201 `IPython.terminal.debugger.set_trace()`
202
203
204 This debugger can hide and skip frames that are tagged according to some predicates.
205 See the `skip_predicates` commands.
206
202 """
207 """
203
208
209 default_predicates = {"tbhide": True, "readonly": False, "ipython_internal": True}
210
204 def __init__(self, color_scheme=None, completekey=None,
211 def __init__(self, color_scheme=None, completekey=None,
205 stdin=None, stdout=None, context=5, **kwargs):
212 stdin=None, stdout=None, context=5, **kwargs):
206 """Create a new IPython debugger.
213 """Create a new IPython debugger.
207
214
208 :param color_scheme: Deprecated, do not use.
215 Parameters
209 :param completekey: Passed to pdb.Pdb.
216 ----------
210 :param stdin: Passed to pdb.Pdb.
217 color_scheme : default None
211 :param stdout: Passed to pdb.Pdb.
218 Deprecated, do not use.
212 :param context: Number of lines of source code context to show when
219 completekey : default None
220 Passed to pdb.Pdb.
221 stdin : default None
222 Passed to pdb.Pdb.
223 stdout : default None
224 Passed to pdb.Pdb.
225 context : int
226 Number of lines of source code context to show when
213 displaying stacktrace information.
227 displaying stacktrace information.
214 :param kwargs: Passed to pdb.Pdb.
228 **kwargs
229 Passed to pdb.Pdb.
230
231 Notes
232 -----
215 The possibilities are python version dependent, see the python
233 The possibilities are python version dependent, see the python
216 docs for more info.
234 docs for more info.
217 """
235 """
@@ -281,6 +299,10 b' class Pdb(OldPdb):'
281 # Set the prompt - the default prompt is '(Pdb)'
299 # Set the prompt - the default prompt is '(Pdb)'
282 self.prompt = prompt
300 self.prompt = prompt
283 self.skip_hidden = True
301 self.skip_hidden = True
302 self.report_skipped = True
303
304 # list of predicates we use to skip frames
305 self._predicates = self.default_predicates
284
306
285 def set_colors(self, scheme):
307 def set_colors(self, scheme):
286 """Shorthand access to the color table scheme selector method."""
308 """Shorthand access to the color table scheme selector method."""
@@ -293,6 +315,26 b' class Pdb(OldPdb):'
293 self.initial_frame = frame
315 self.initial_frame = frame
294 return super().set_trace(frame)
316 return super().set_trace(frame)
295
317
318 def _hidden_predicate(self, frame):
319 """
320 Given a frame return whether it it should be hidden or not by IPython.
321 """
322
323 if self._predicates["readonly"]:
324 fname = frame.f_code.co_filename
325 # we need to check for file existence and interactively define
326 # function would otherwise appear as RO.
327 if os.path.isfile(fname) and not os.access(fname, os.W_OK):
328 return True
329
330 if self._predicates["tbhide"]:
331 if frame in (self.curframe, getattr(self, "initial_frame", None)):
332 return False
333 else:
334 return self._get_frame_locals(frame).get("__tracebackhide__", False)
335
336 return False
337
296 def hidden_frames(self, stack):
338 def hidden_frames(self, stack):
297 """
339 """
298 Given an index in the stack return whether it should be skipped.
340 Given an index in the stack return whether it should be skipped.
@@ -303,14 +345,9 b' class Pdb(OldPdb):'
303 # locals whenever the .f_locals accessor is called, so we
345 # locals whenever the .f_locals accessor is called, so we
304 # avoid calling it here to preserve self.curframe_locals.
346 # avoid calling it here to preserve self.curframe_locals.
305 # Futhermore, there is no good reason to hide the current frame.
347 # Futhermore, there is no good reason to hide the current frame.
306 ip_hide = [
348 ip_hide = [self._hidden_predicate(s[0]) for s in stack]
307 False
308 if s[0] in (self.curframe, getattr(self, "initial_frame", None))
309 else s[0].f_locals.get("__tracebackhide__", False)
310 for s in stack
311 ]
312 ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
349 ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
313 if ip_start:
350 if ip_start and self._predicates["ipython_internal"]:
314 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
351 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
315 return ip_hide
352 return ip_hide
316
353
@@ -398,6 +435,28 b' class Pdb(OldPdb):'
398 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
435 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
399 # vds: <<
436 # vds: <<
400
437
438 def _get_frame_locals(self, frame):
439 """ "
440 Acessing f_local of current frame reset the namespace, so we want to avoid
441 that or the following can happend
442
443 ipdb> foo
444 "old"
445 ipdb> foo = "new"
446 ipdb> foo
447 "new"
448 ipdb> where
449 ipdb> foo
450 "old"
451
452 So if frame is self.current_frame we instead return self.curframe_locals
453
454 """
455 if frame is self.curframe:
456 return self.curframe_locals
457 else:
458 return frame.f_locals
459
401 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
460 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
402 if context is None:
461 if context is None:
403 context = self.context
462 context = self.context
@@ -422,10 +481,11 b' class Pdb(OldPdb):'
422 frame, lineno = frame_lineno
481 frame, lineno = frame_lineno
423
482
424 return_value = ''
483 return_value = ''
425 if '__return__' in frame.f_locals:
484 loc_frame = self._get_frame_locals(frame)
426 rv = frame.f_locals['__return__']
485 if "__return__" in loc_frame:
486 rv = loc_frame["__return__"]
427 #return_value += '->'
487 # return_value += '->'
428 return_value += reprlib.repr(rv) + '\n'
488 return_value += reprlib.repr(rv) + "\n"
429 ret.append(return_value)
489 ret.append(return_value)
430
490
431 #s = filename + '(' + `lineno` + ')'
491 #s = filename + '(' + `lineno` + ')'
@@ -437,10 +497,10 b' class Pdb(OldPdb):'
437 else:
497 else:
438 func = "<lambda>"
498 func = "<lambda>"
439
499
440 call = ''
500 call = ""
441 if func != '?':
501 if func != "?":
442 if '__args__' in frame.f_locals:
502 if "__args__" in loc_frame:
443 args = reprlib.repr(frame.f_locals['__args__'])
503 args = reprlib.repr(loc_frame["__args__"])
444 else:
504 else:
445 args = '()'
505 args = '()'
446 call = tpl_call % (func, args)
506 call = tpl_call % (func, args)
@@ -533,15 +593,68 b' class Pdb(OldPdb):'
533 except KeyboardInterrupt:
593 except KeyboardInterrupt:
534 pass
594 pass
535
595
596 def do_skip_predicates(self, args):
597 """
598 Turn on/off individual predicates as to whether a frame should be hidden/skip.
599
600 The global option to skip (or not) hidden frames is set with skip_hidden
601
602 To change the value of a predicate
603
604 skip_predicates key [true|false]
605
606 Call without arguments to see the current values.
607
608 To permanently change the value of an option add the corresponding
609 command to your ``~/.pdbrc`` file. If you are programmatically using the
610 Pdb instance you can also change the ``default_predicates`` class
611 attribute.
612 """
613 if not args.strip():
614 print("current predicates:")
615 for (p, v) in self._predicates.items():
616 print(" ", p, ":", v)
617 return
618 type_value = args.strip().split(" ")
619 if len(type_value) != 2:
620 print(
621 f"Usage: skip_predicates <type> <value>, with <type> one of {set(self._predicates.keys())}"
622 )
623 return
624
625 type_, value = type_value
626 if type_ not in self._predicates:
627 print(f"{type_!r} not in {set(self._predicates.keys())}")
628 return
629 if value.lower() not in ("true", "yes", "1", "no", "false", "0"):
630 print(
631 f"{value!r} is invalid - use one of ('true', 'yes', '1', 'no', 'false', '0')"
632 )
633 return
634
635 self._predicates[type_] = value.lower() in ("true", "yes", "1")
636 if not any(self._predicates.values()):
637 print(
638 "Warning, all predicates set to False, skip_hidden may not have any effects."
639 )
640
536 def do_skip_hidden(self, arg):
641 def do_skip_hidden(self, arg):
537 """
642 """
538 Change whether or not we should skip frames with the
643 Change whether or not we should skip frames with the
539 __tracebackhide__ attribute.
644 __tracebackhide__ attribute.
540 """
645 """
541 if arg.strip().lower() in ("true", "yes"):
646 if not arg.strip():
647 print(
648 f"skip_hidden = {self.skip_hidden}, use 'yes','no', 'true', or 'false' to change."
649 )
650 elif arg.strip().lower() in ("true", "yes"):
542 self.skip_hidden = True
651 self.skip_hidden = True
543 elif arg.strip().lower() in ("false", "no"):
652 elif arg.strip().lower() in ("false", "no"):
544 self.skip_hidden = False
653 self.skip_hidden = False
654 if not any(self._predicates.values()):
655 print(
656 "Warning, all predicates set to False, skip_hidden may not have any effects."
657 )
545
658
546 def do_list(self, arg):
659 def do_list(self, arg):
547 """Print lines of code from the current stack frame
660 """Print lines of code from the current stack frame
@@ -581,7 +694,7 b' class Pdb(OldPdb):'
581
694
582 def getsourcelines(self, obj):
695 def getsourcelines(self, obj):
583 lines, lineno = inspect.findsource(obj)
696 lines, lineno = inspect.findsource(obj)
584 if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
697 if inspect.isframe(obj) and obj.f_globals is self._get_frame_locals(obj):
585 # must be a module frame: do not try to cut a block out of it
698 # must be a module frame: do not try to cut a block out of it
586 return lines, 1
699 return lines, 1
587 elif inspect.ismodule(obj):
700 elif inspect.ismodule(obj):
@@ -705,12 +818,14 b' class Pdb(OldPdb):'
705 def stop_here(self, frame):
818 def stop_here(self, frame):
706 hidden = False
819 hidden = False
707 if self.skip_hidden:
820 if self.skip_hidden:
708 hidden = frame.f_locals.get("__tracebackhide__", False)
821 hidden = self._hidden_predicate(frame)
709 if hidden:
822 if hidden:
823 if self.report_skipped:
710 Colors = self.color_scheme_table.active_colors
824 Colors = self.color_scheme_table.active_colors
711 ColorsNormal = Colors.Normal
825 ColorsNormal = Colors.Normal
712 print(f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n")
826 print(
713
827 f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n"
828 )
714 return super().stop_here(frame)
829 return super().stop_here(frame)
715
830
716 def do_up(self, arg):
831 def do_up(self, arg):
@@ -721,7 +836,7 b' class Pdb(OldPdb):'
721 Will skip hidden frames.
836 Will skip hidden frames.
722 """
837 """
723 # modified version of upstream that skips
838 # modified version of upstream that skips
724 # frames with __tracebackide__
839 # frames with __tracebackhide__
725 if self.curindex == 0:
840 if self.curindex == 0:
726 self.error("Oldest frame")
841 self.error("Oldest frame")
727 return
842 return
@@ -1178,7 +1178,12 b' class Video(DisplayObject):'
1178
1178
1179 @skip_doctest
1179 @skip_doctest
1180 def set_matplotlib_formats(*formats, **kwargs):
1180 def set_matplotlib_formats(*formats, **kwargs):
1181 """Select figure formats for the inline backend. Optionally pass quality for JPEG.
1181 """
1182 .. deprecated:: 7.23
1183
1184 use `matplotlib_inline.backend_inline.set_matplotlib_formats()`
1185
1186 Select figure formats for the inline backend. Optionally pass quality for JPEG.
1182
1187
1183 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
1188 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
1184
1189
@@ -1196,20 +1201,28 b' def set_matplotlib_formats(*formats, **kwargs):'
1196 **kwargs
1201 **kwargs
1197 Keyword args will be relayed to ``figure.canvas.print_figure``.
1202 Keyword args will be relayed to ``figure.canvas.print_figure``.
1198 """
1203 """
1199 from IPython.core.interactiveshell import InteractiveShell
1204 warnings.warn(
1200 from IPython.core.pylabtools import select_figure_formats
1205 "`set_matplotlib_formats` is deprecated since IPython 7.23, directly "
1201 # build kwargs, starting with InlineBackend config
1206 "use `matplotlib_inline.backend_inline.set_matplotlib_formats()`",
1202 kw = {}
1207 DeprecationWarning,
1203 from ipykernel.pylab.config import InlineBackend
1208 stacklevel=2,
1204 cfg = InlineBackend.instance()
1209 )
1205 kw.update(cfg.print_figure_kwargs)
1210
1206 kw.update(**kwargs)
1211 from matplotlib_inline.backend_inline import (
1207 shell = InteractiveShell.instance()
1212 set_matplotlib_formats as set_matplotlib_formats_orig,
1208 select_figure_formats(shell, formats, **kw)
1213 )
1214
1215 set_matplotlib_formats_orig(*formats, **kwargs)
1209
1216
1210 @skip_doctest
1217 @skip_doctest
1211 def set_matplotlib_close(close=True):
1218 def set_matplotlib_close(close=True):
1212 """Set whether the inline backend closes all figures automatically or not.
1219 """
1220 .. deprecated:: 7.23
1221
1222 use `matplotlib_inline.backend_inline.set_matplotlib_close()`
1223
1224
1225 Set whether the inline backend closes all figures automatically or not.
1213
1226
1214 By default, the inline backend used in the IPython Notebook will close all
1227 By default, the inline backend used in the IPython Notebook will close all
1215 matplotlib figures automatically after each cell is run. This means that
1228 matplotlib figures automatically after each cell is run. This means that
@@ -1229,6 +1242,15 b' def set_matplotlib_close(close=True):'
1229 Should all matplotlib figures be automatically closed after each cell is
1242 Should all matplotlib figures be automatically closed after each cell is
1230 run?
1243 run?
1231 """
1244 """
1232 from ipykernel.pylab.config import InlineBackend
1245 warnings.warn(
1233 cfg = InlineBackend.instance()
1246 "`set_matplotlib_close` is deprecated since IPython 7.23, directly "
1234 cfg.close_figures = close
1247 "use `matplotlib_inline.backend_inline.set_matplotlib_close()`",
1248 DeprecationWarning,
1249 stacklevel=2,
1250 )
1251
1252 from matplotlib_inline.backend_inline import (
1253 set_matplotlib_close as set_matplotlib_close_orig,
1254 )
1255
1256 set_matplotlib_close_orig(close)
@@ -85,7 +85,17 b' def _new_id():'
85 return b2a_hex(os.urandom(16)).decode('ascii')
85 return b2a_hex(os.urandom(16)).decode('ascii')
86
86
87
87
88 def display(*objs, include=None, exclude=None, metadata=None, transient=None, display_id=None, **kwargs):
88 def display(
89 *objs,
90 include=None,
91 exclude=None,
92 metadata=None,
93 transient=None,
94 display_id=None,
95 raw=False,
96 clear=False,
97 **kwargs
98 ):
89 """Display a Python object in all frontends.
99 """Display a Python object in all frontends.
90
100
91 By default all representations will be computed and sent to the frontends.
101 By default all representations will be computed and sent to the frontends.
@@ -120,12 +130,14 b' def display(*objs, include=None, exclude=None, metadata=None, transient=None, di'
120 Set an id for the display.
130 Set an id for the display.
121 This id can be used for updating this display area later via update_display.
131 This id can be used for updating this display area later via update_display.
122 If given as `True`, generate a new `display_id`
132 If given as `True`, generate a new `display_id`
123 kwargs: additional keyword-args, optional
133 clear : bool, optional
134 Should the output area be cleared before displaying anything? If True,
135 this will wait for additional output before clearing. [default: False]
136 **kwargs : additional keyword-args, optional
124 Additional keyword-arguments are passed through to the display publisher.
137 Additional keyword-arguments are passed through to the display publisher.
125
138
126 Returns
139 Returns
127 -------
140 -------
128
129 handle: DisplayHandle
141 handle: DisplayHandle
130 Returns a handle on updatable displays for use with :func:`update_display`,
142 Returns a handle on updatable displays for use with :func:`update_display`,
131 if `display_id` is given. Returns :any:`None` if no `display_id` is given
143 if `display_id` is given. Returns :any:`None` if no `display_id` is given
@@ -133,7 +145,6 b' def display(*objs, include=None, exclude=None, metadata=None, transient=None, di'
133
145
134 Examples
146 Examples
135 --------
147 --------
136
137 >>> class Json(object):
148 >>> class Json(object):
138 ... def __init__(self, json):
149 ... def __init__(self, json):
139 ... self.json = json
150 ... self.json = json
@@ -172,12 +183,10 b' def display(*objs, include=None, exclude=None, metadata=None, transient=None, di'
172
183
173 See Also
184 See Also
174 --------
185 --------
175
176 :func:`update_display`
186 :func:`update_display`
177
187
178 Notes
188 Notes
179 -----
189 -----
180
181 In Python, objects can declare their textual representation using the
190 In Python, objects can declare their textual representation using the
182 `__repr__` method. IPython expands on this idea and allows objects to declare
191 `__repr__` method. IPython expands on this idea and allows objects to declare
183 other, rich representations including:
192 other, rich representations including:
@@ -239,7 +248,6 b' def display(*objs, include=None, exclude=None, metadata=None, transient=None, di'
239 print(*objs)
248 print(*objs)
240 return
249 return
241
250
242 raw = kwargs.pop('raw', False)
243 if transient is None:
251 if transient is None:
244 transient = {}
252 transient = {}
245 if metadata is None:
253 if metadata is None:
@@ -263,6 +271,9 b' def display(*objs, include=None, exclude=None, metadata=None, transient=None, di'
263 if not raw:
271 if not raw:
264 format = InteractiveShell.instance().display_formatter.format
272 format = InteractiveShell.instance().display_formatter.format
265
273
274 if clear:
275 clear_output(wait=True)
276
266 for obj in objs:
277 for obj in objs:
267 if raw:
278 if raw:
268 publish_display_data(data=obj, metadata=metadata, **kwargs)
279 publish_display_data(data=obj, metadata=metadata, **kwargs)
@@ -285,15 +296,13 b' def update_display(obj, *, display_id, **kwargs):'
285
296
286 Parameters
297 Parameters
287 ----------
298 ----------
288
299 obj
289 obj:
290 The object with which to update the display
300 The object with which to update the display
291 display_id: keyword-only
301 display_id : keyword-only
292 The id of the display to update
302 The id of the display to update
293
303
294 See Also
304 See Also
295 --------
305 --------
296
297 :func:`display`
306 :func:`display`
298 """
307 """
299 kwargs['update'] = True
308 kwargs['update'] = True
@@ -328,10 +337,9 b' class DisplayHandle(object):'
328
337
329 Parameters
338 Parameters
330 ----------
339 ----------
331
340 obj
332 obj:
333 object to display
341 object to display
334 **kwargs:
342 **kwargs
335 additional keyword arguments passed to display
343 additional keyword arguments passed to display
336 """
344 """
337 display(obj, display_id=self.display_id, **kwargs)
345 display(obj, display_id=self.display_id, **kwargs)
@@ -341,10 +349,9 b' class DisplayHandle(object):'
341
349
342 Parameters
350 Parameters
343 ----------
351 ----------
344
352 obj
345 obj:
346 object to display
353 object to display
347 **kwargs:
354 **kwargs
348 additional keyword arguments passed to update_display
355 additional keyword arguments passed to update_display
349 """
356 """
350 update_display(obj, display_id=self.display_id, **kwargs)
357 update_display(obj, display_id=self.display_id, **kwargs)
1 NO CONTENT: modified file
NO CONTENT: modified file
1 NO CONTENT: modified file
NO CONTENT: modified file
@@ -33,7 +33,7 b' class EventManager(object):'
33 ----------
33 ----------
34 shell
34 shell
35 The :class:`~IPython.core.interactiveshell.InteractiveShell` instance
35 The :class:`~IPython.core.interactiveshell.InteractiveShell` instance
36 available_callbacks
36 available_events
37 An iterable of names for callback events.
37 An iterable of names for callback events.
38 """
38 """
39 self.shell = shell
39 self.shell = shell
1 NO CONTENT: modified file
NO CONTENT: modified file
@@ -121,7 +121,6 b' class DisplayFormatter(Configurable):'
121 Returns
121 Returns
122 -------
122 -------
123 (format_dict, metadata_dict) : tuple of two dicts
123 (format_dict, metadata_dict) : tuple of two dicts
124
125 format_dict is a dictionary of key/value pairs, one of each format that was
124 format_dict is a dictionary of key/value pairs, one of each format that was
126 generated for the object. The keys are the format types, which
125 generated for the object. The keys are the format types, which
127 will usually be MIME type strings and the values and JSON'able
126 will usually be MIME type strings and the values and JSON'able
@@ -133,7 +132,6 b' class DisplayFormatter(Configurable):'
133
132
134 Notes
133 Notes
135 -----
134 -----
136
137 If an object implement `_repr_mimebundle_` as well as various
135 If an object implement `_repr_mimebundle_` as well as various
138 `_repr_*_`, the data returned by `_repr_mimebundle_` will take
136 `_repr_*_`, the data returned by `_repr_mimebundle_` will take
139 precedence and the corresponding `_repr_*_` for this mimetype will
137 precedence and the corresponding `_repr_*_` for this mimetype will
@@ -636,7 +634,6 b' class PlainTextFormatter(BaseFormatter):'
636
634
637 This parameter can be set via the '%precision' magic.
635 This parameter can be set via the '%precision' magic.
638 """
636 """
639
640 new = change['new']
637 new = change['new']
641 if '%' in new:
638 if '%' in new:
642 # got explicit format string
639 # got explicit format string
@@ -678,6 +675,11 b' class PlainTextFormatter(BaseFormatter):'
678 def _type_printers_default(self):
675 def _type_printers_default(self):
679 d = pretty._type_pprinters.copy()
676 d = pretty._type_pprinters.copy()
680 d[float] = lambda obj,p,cycle: p.text(self.float_format%obj)
677 d[float] = lambda obj,p,cycle: p.text(self.float_format%obj)
678 # if NumPy is used, set precision for its float64 type
679 if "numpy" in sys.modules:
680 import numpy
681
682 d[numpy.float64] = lambda obj, p, cycle: p.text(self.float_format % obj)
681 return d
683 return d
682
684
683 @default('deferred_printers')
685 @default('deferred_printers')
@@ -445,8 +445,11 b' class HistoryAccessor(HistoryAccessorBase):'
445 Parameters
445 Parameters
446 ----------
446 ----------
447 rangestr : str
447 rangestr : str
448 A string specifying ranges, e.g. "5 ~2/1-4". See
448 A string specifying ranges, e.g. "5 ~2/1-4". If empty string is used,
449 :func:`magic_history` for full details.
449 this will return everything from current session's history.
450
451 See the documentation of :func:`%history` for the full details.
452
450 raw, output : bool
453 raw, output : bool
451 As :meth:`get_range`
454 As :meth:`get_range`
452
455
@@ -851,11 +854,18 b' $""", re.VERBOSE)'
851 def extract_hist_ranges(ranges_str):
854 def extract_hist_ranges(ranges_str):
852 """Turn a string of history ranges into 3-tuples of (session, start, stop).
855 """Turn a string of history ranges into 3-tuples of (session, start, stop).
853
856
857 Empty string results in a `[(0, 1, None)]`, i.e. "everything from current
858 session".
859
854 Examples
860 Examples
855 --------
861 --------
856 >>> list(extract_hist_ranges("~8/5-~7/4 2"))
862 >>> list(extract_hist_ranges("~8/5-~7/4 2"))
857 [(-8, 5, None), (-7, 1, 5), (0, 2, 3)]
863 [(-8, 5, None), (-7, 1, 5), (0, 2, 3)]
858 """
864 """
865 if ranges_str == "":
866 yield (0, 1, None) # Everything from current session
867 return
868
859 for range_str in ranges_str.split():
869 for range_str in ranges_str.split():
860 rmatch = range_re.match(range_str)
870 rmatch = range_re.match(range_str)
861 if not rmatch:
871 if not rmatch:
@@ -10,7 +10,9 b' deprecated in 7.0.'
10 # Copyright (c) IPython Development Team.
10 # Copyright (c) IPython Development Team.
11 # Distributed under the terms of the Modified BSD License.
11 # Distributed under the terms of the Modified BSD License.
12
12
13 from codeop import compile_command
13 import ast
14 import sys
15 from codeop import CommandCompiler, Compile
14 import re
16 import re
15 import tokenize
17 import tokenize
16 from typing import List, Tuple, Optional, Any
18 from typing import List, Tuple, Optional, Any
@@ -89,7 +91,30 b' classic_prompt = PromptStripper('
89 initial_re=re.compile(r'^>>>( |$)')
91 initial_re=re.compile(r'^>>>( |$)')
90 )
92 )
91
93
92 ipython_prompt = PromptStripper(re.compile(r'^(In \[\d+\]: |\s*\.{3,}: ?)'))
94 ipython_prompt = PromptStripper(
95 re.compile(
96 r"""
97 ^( # Match from the beginning of a line, either:
98
99 # 1. First-line prompt:
100 ((\[nav\]|\[ins\])?\ )? # Vi editing mode prompt, if it's there
101 In\ # The 'In' of the prompt, with a space
102 \[\d+\]: # Command index, as displayed in the prompt
103 \ # With a mandatory trailing space
104
105 | # ... or ...
106
107 # 2. The three dots of the multiline prompt
108 \s* # All leading whitespace characters
109 \.{3,}: # The three (or more) dots
110 \ ? # With an optional trailing space
111
112 )
113 """,
114 re.VERBOSE,
115 )
116 )
117
93
118
94 def cell_magic(lines):
119 def cell_magic(lines):
95 if not lines or not lines[0].startswith('%%'):
120 if not lines or not lines[0].startswith('%%'):
@@ -508,6 +533,20 b' def make_tokens_by_line(lines:List[str]):'
508
533
509 return tokens_by_line
534 return tokens_by_line
510
535
536
537 def has_sunken_brackets(tokens: List[tokenize.TokenInfo]):
538 """Check if the depth of brackets in the list of tokens drops below 0"""
539 parenlev = 0
540 for token in tokens:
541 if token.string in {"(", "[", "{"}:
542 parenlev += 1
543 elif token.string in {")", "]", "}"}:
544 parenlev -= 1
545 if parenlev < 0:
546 return True
547 return False
548
549
511 def show_linewise_tokens(s: str):
550 def show_linewise_tokens(s: str):
512 """For investigation and debugging"""
551 """For investigation and debugging"""
513 if not s.endswith('\n'):
552 if not s.endswith('\n'):
@@ -662,6 +701,15 b' class TransformerManager:'
662
701
663 tokens_by_line = make_tokens_by_line(lines)
702 tokens_by_line = make_tokens_by_line(lines)
664
703
704 # Bail if we got one line and there are more closing parentheses than
705 # the opening ones
706 if (
707 len(lines) == 1
708 and tokens_by_line
709 and has_sunken_brackets(tokens_by_line[0])
710 ):
711 return "invalid", None
712
665 if not tokens_by_line:
713 if not tokens_by_line:
666 return 'incomplete', find_last_indent(lines)
714 return 'incomplete', find_last_indent(lines)
667
715
@@ -727,3 +775,25 b' def find_last_indent(lines):'
727 if not m:
775 if not m:
728 return 0
776 return 0
729 return len(m.group(0).replace('\t', ' '*4))
777 return len(m.group(0).replace('\t', ' '*4))
778
779
780 class MaybeAsyncCompile(Compile):
781 def __init__(self, extra_flags=0):
782 super().__init__()
783 self.flags |= extra_flags
784
785 def __call__(self, *args, **kwds):
786 return compile(*args, **kwds)
787
788
789 class MaybeAsyncCommandCompiler(CommandCompiler):
790 def __init__(self, extra_flags=0):
791 self.compiler = MaybeAsyncCompile(extra_flags=extra_flags)
792
793
794 if (sys.version_info.major, sys.version_info.minor) >= (3, 8):
795 _extra_flags = ast.PyCF_ALLOW_TOP_LEVEL_AWAIT
796 else:
797 _extra_flags = ast.PyCF_ONLY_AST
798
799 compile_command = MaybeAsyncCommandCompiler(extra_flags=_extra_flags)
@@ -923,12 +923,11 b' class InteractiveShell(SingletonConfigurable):'
923 paths.append(p.resolve())
923 paths.append(p.resolve())
924
924
925 # In Cygwin paths like "c:\..." and '\cygdrive\c\...' are possible
925 # In Cygwin paths like "c:\..." and '\cygdrive\c\...' are possible
926 if str(p_venv).startswith("\\cygdrive"):
926 if p_venv.parts[1] == "cygdrive":
927 p_venv = Path(str(p_venv)[11:])
927 drive_name = p_venv.parts[2]
928 elif len(str(p_venv)) >= 2 and str(p_venv)[1] == ":":
928 p_venv = (drive_name + ":/") / Path(*p_venv.parts[3:])
929 p_venv = Path(str(p_venv)[2:])
930
929
931 if any(os.fspath(p_venv) in os.fspath(p) for p in paths):
930 if any(p_venv == p.parents[1] for p in paths):
932 # Our exe is inside or has access to the virtualenv, don't need to do anything.
931 # Our exe is inside or has access to the virtualenv, don't need to do anything.
933 return
932 return
934
933
@@ -1896,9 +1895,14 b' class InteractiveShell(SingletonConfigurable):'
1896 exception or returns an invalid result, it will be immediately
1895 exception or returns an invalid result, it will be immediately
1897 disabled.
1896 disabled.
1898
1897
1898 Notes
1899 -----
1900
1899 WARNING: by putting in your own exception handler into IPython's main
1901 WARNING: by putting in your own exception handler into IPython's main
1900 execution loop, you run a very good chance of nasty crashes. This
1902 execution loop, you run a very good chance of nasty crashes. This
1901 facility should only be used if you really know what you are doing."""
1903 facility should only be used if you really know what you are doing.
1904 """
1905
1902 if not isinstance(exc_tuple, tuple):
1906 if not isinstance(exc_tuple, tuple):
1903 raise TypeError("The custom exceptions must be given as a tuple.")
1907 raise TypeError("The custom exceptions must be given as a tuple.")
1904
1908
@@ -1966,7 +1970,7 b' class InteractiveShell(SingletonConfigurable):'
1966 sys.excepthook themselves. I guess this is a feature that
1970 sys.excepthook themselves. I guess this is a feature that
1967 enables them to keep running after exceptions that would
1971 enables them to keep running after exceptions that would
1968 otherwise kill their mainloop. This is a bother for IPython
1972 otherwise kill their mainloop. This is a bother for IPython
1969 which excepts to catch all of the program exceptions with a try:
1973 which expects to catch all of the program exceptions with a try:
1970 except: statement.
1974 except: statement.
1971
1975
1972 Normally, IPython sets sys.excepthook to a CrashHandler instance, so if
1976 Normally, IPython sets sys.excepthook to a CrashHandler instance, so if
@@ -2085,13 +2089,17 b' class InteractiveShell(SingletonConfigurable):'
2085 except KeyboardInterrupt:
2089 except KeyboardInterrupt:
2086 print('\n' + self.get_exception_only(), file=sys.stderr)
2090 print('\n' + self.get_exception_only(), file=sys.stderr)
2087
2091
2088 def _showtraceback(self, etype, evalue, stb):
2092 def _showtraceback(self, etype, evalue, stb: str):
2089 """Actually show a traceback.
2093 """Actually show a traceback.
2090
2094
2091 Subclasses may override this method to put the traceback on a different
2095 Subclasses may override this method to put the traceback on a different
2092 place, like a side channel.
2096 place, like a side channel.
2093 """
2097 """
2094 print(self.InteractiveTB.stb2text(stb))
2098 val = self.InteractiveTB.stb2text(stb)
2099 try:
2100 print(val)
2101 except UnicodeEncodeError:
2102 print(val.encode("utf-8", "backslashreplace").decode())
2095
2103
2096 def showsyntaxerror(self, filename=None, running_compiled_code=False):
2104 def showsyntaxerror(self, filename=None, running_compiled_code=False):
2097 """Display the syntax error that just occurred.
2105 """Display the syntax error that just occurred.
@@ -2218,6 +2226,9 b' class InteractiveShell(SingletonConfigurable):'
2218 matches : list
2226 matches : list
2219 A sorted list with all possible completions.
2227 A sorted list with all possible completions.
2220
2228
2229
2230 Notes
2231 -----
2221 The optional arguments allow the completion to take more context into
2232 The optional arguments allow the completion to take more context into
2222 account, and are part of the low-level completion API.
2233 account, and are part of the low-level completion API.
2223
2234
@@ -2226,7 +2237,8 b' class InteractiveShell(SingletonConfigurable):'
2226 exposing it as a method, it can be used by other non-readline
2237 exposing it as a method, it can be used by other non-readline
2227 environments (such as GUIs) for text completion.
2238 environments (such as GUIs) for text completion.
2228
2239
2229 Simple usage example:
2240 Examples
2241 --------
2230
2242
2231 In [1]: x = 'hello'
2243 In [1]: x = 'hello'
2232
2244
@@ -2508,6 +2520,23 b' class InteractiveShell(SingletonConfigurable):'
2508 Command to execute.
2520 Command to execute.
2509 """
2521 """
2510 cmd = self.var_expand(cmd, depth=1)
2522 cmd = self.var_expand(cmd, depth=1)
2523 # warn if there is an IPython magic alternative.
2524 main_cmd = cmd.split()[0]
2525 has_magic_alternatives = ("pip", "conda", "cd", "ls")
2526
2527 # had to check if the command was an alias expanded because of `ls`
2528 is_alias_expanded = self.alias_manager.is_alias(main_cmd) and (
2529 self.alias_manager.retrieve_alias(main_cmd).strip() == cmd.strip()
2530 )
2531
2532 if main_cmd in has_magic_alternatives and not is_alias_expanded:
2533 warnings.warn(
2534 (
2535 "You executed the system command !{0} which may not work "
2536 "as expected. Try the IPython magic %{0} instead."
2537 ).format(main_cmd)
2538 )
2539
2511 # protect os.system from UNC paths on Windows, which it can't handle:
2540 # protect os.system from UNC paths on Windows, which it can't handle:
2512 if sys.platform == 'win32':
2541 if sys.platform == 'win32':
2513 from IPython.utils._process_win32 import AvoidUNCPath
2542 from IPython.utils._process_win32 import AvoidUNCPath
@@ -3109,9 +3138,7 b' class InteractiveShell(SingletonConfigurable):'
3109 _run_async = False
3138 _run_async = False
3110
3139
3111 with self.builtin_trap:
3140 with self.builtin_trap:
3112 cell_name = self.compile.cache(
3141 cell_name = compiler.cache(cell, self.execution_count, raw_code=raw_cell)
3113 cell, self.execution_count, raw_code=raw_cell
3114 )
3115
3142
3116 with self.display_trap:
3143 with self.display_trap:
3117 # Compile to bytecode
3144 # Compile to bytecode
@@ -3514,6 +3541,7 b' class InteractiveShell(SingletonConfigurable):'
3514 display figures inline.
3541 display figures inline.
3515 """
3542 """
3516 from IPython.core import pylabtools as pt
3543 from IPython.core import pylabtools as pt
3544 from matplotlib_inline.backend_inline import configure_inline_support
3517 gui, backend = pt.find_gui_and_backend(gui, self.pylab_gui_select)
3545 gui, backend = pt.find_gui_and_backend(gui, self.pylab_gui_select)
3518
3546
3519 if gui != 'inline':
3547 if gui != 'inline':
@@ -3527,7 +3555,7 b' class InteractiveShell(SingletonConfigurable):'
3527 gui, backend = pt.find_gui_and_backend(self.pylab_gui_select)
3555 gui, backend = pt.find_gui_and_backend(self.pylab_gui_select)
3528
3556
3529 pt.activate_matplotlib(backend)
3557 pt.activate_matplotlib(backend)
3530 pt.configure_inline_support(self, backend)
3558 configure_inline_support(self, backend)
3531
3559
3532 # Now we must activate the gui pylab wants to use, and fix %run to take
3560 # Now we must activate the gui pylab wants to use, and fix %run to take
3533 # plot updates into account
3561 # plot updates into account
@@ -3667,12 +3695,15 b' class InteractiveShell(SingletonConfigurable):'
3667
3695
3668 Parameters
3696 Parameters
3669 ----------
3697 ----------
3670 range_str : string
3698 range_str : str
3671 The set of slices is given as a string, like "~5/6-~4/2 4:8 9",
3699 The set of slices is given as a string, like "~5/6-~4/2 4:8 9",
3672 since this function is for use by magic functions which get their
3700 since this function is for use by magic functions which get their
3673 arguments as strings. The number before the / is the session
3701 arguments as strings. The number before the / is the session
3674 number: ~n goes n back from the current session.
3702 number: ~n goes n back from the current session.
3675
3703
3704 If empty string is given, returns history of current session
3705 without the last input.
3706
3676 raw : bool, optional
3707 raw : bool, optional
3677 By default, the processed input is used. If this is true, the raw
3708 By default, the processed input is used. If this is true, the raw
3678 input history is used instead.
3709 input history is used instead.
@@ -3686,7 +3717,16 b' class InteractiveShell(SingletonConfigurable):'
3686 * ``N-M`` -> include items N..M (closed endpoint).
3717 * ``N-M`` -> include items N..M (closed endpoint).
3687 """
3718 """
3688 lines = self.history_manager.get_range_by_str(range_str, raw=raw)
3719 lines = self.history_manager.get_range_by_str(range_str, raw=raw)
3689 return "\n".join(x for _, _, x in lines)
3720 text = "\n".join(x for _, _, x in lines)
3721
3722 # Skip the last line, as it's probably the magic that called this
3723 if not range_str:
3724 if "\n" not in text:
3725 text = ""
3726 else:
3727 text = text[: text.rfind("\n")]
3728
3729 return text
3690
3730
3691 def find_user_code(self, target, raw=True, py_only=False, skip_encoding_cookie=True, search_ns=False):
3731 def find_user_code(self, target, raw=True, py_only=False, skip_encoding_cookie=True, search_ns=False):
3692 """Get a code string from history, file, url, or a string or macro.
3732 """Get a code string from history, file, url, or a string or macro.
@@ -3695,14 +3735,15 b' class InteractiveShell(SingletonConfigurable):'
3695
3735
3696 Parameters
3736 Parameters
3697 ----------
3737 ----------
3698
3699 target : str
3738 target : str
3700
3701 A string specifying code to retrieve. This will be tried respectively
3739 A string specifying code to retrieve. This will be tried respectively
3702 as: ranges of input history (see %history for syntax), url,
3740 as: ranges of input history (see %history for syntax), url,
3703 corresponding .py file, filename, or an expression evaluating to a
3741 corresponding .py file, filename, or an expression evaluating to a
3704 string or Macro in the user namespace.
3742 string or Macro in the user namespace.
3705
3743
3744 If empty string is given, returns complete history of current
3745 session, without the last line.
3746
3706 raw : bool
3747 raw : bool
3707 If true (default), retrieve raw history. Has no effect on the other
3748 If true (default), retrieve raw history. Has no effect on the other
3708 retrieval mechanisms.
3749 retrieval mechanisms.
@@ -3771,6 +3812,22 b' class InteractiveShell(SingletonConfigurable):'
3771 raise TypeError("%s is neither a string nor a macro." % target,
3812 raise TypeError("%s is neither a string nor a macro." % target,
3772 codeobj)
3813 codeobj)
3773
3814
3815 def _atexit_once(self):
3816 """
3817 At exist operation that need to be called at most once.
3818 Second call to this function per instance will do nothing.
3819 """
3820
3821 if not getattr(self, "_atexit_once_called", False):
3822 self._atexit_once_called = True
3823 # Clear all user namespaces to release all references cleanly.
3824 self.reset(new_session=False)
3825 # Close the history session (this stores the end time and line count)
3826 # this must be *before* the tempfile cleanup, in case of temporary
3827 # history db
3828 self.history_manager.end_session()
3829 self.history_manager = None
3830
3774 #-------------------------------------------------------------------------
3831 #-------------------------------------------------------------------------
3775 # Things related to IPython exiting
3832 # Things related to IPython exiting
3776 #-------------------------------------------------------------------------
3833 #-------------------------------------------------------------------------
@@ -3785,26 +3842,24 b' class InteractiveShell(SingletonConfigurable):'
3785 code that has the appropriate information, rather than trying to
3842 code that has the appropriate information, rather than trying to
3786 clutter
3843 clutter
3787 """
3844 """
3788 # Close the history session (this stores the end time and line count)
3845 self._atexit_once()
3789 # this must be *before* the tempfile cleanup, in case of temporary
3790 # history db
3791 self.history_manager.end_session()
3792
3846
3793 # Cleanup all tempfiles and folders left around
3847 # Cleanup all tempfiles and folders left around
3794 for tfile in self.tempfiles:
3848 for tfile in self.tempfiles:
3795 try:
3849 try:
3796 tfile.unlink()
3850 tfile.unlink()
3851 self.tempfiles.remove(tfile)
3797 except FileNotFoundError:
3852 except FileNotFoundError:
3798 pass
3853 pass
3799
3854 del self.tempfiles
3800 for tdir in self.tempdirs:
3855 for tdir in self.tempdirs:
3801 try:
3856 try:
3802 tdir.rmdir()
3857 tdir.rmdir()
3858 self.tempdirs.remove(tdir)
3803 except FileNotFoundError:
3859 except FileNotFoundError:
3804 pass
3860 pass
3861 del self.tempdirs
3805
3862
3806 # Clear all user namespaces to release all references cleanly.
3807 self.reset(new_session=False)
3808
3863
3809 # Run user hooks
3864 # Run user hooks
3810 self.hooks.shutdown_hook()
3865 self.hooks.shutdown_hook()
@@ -618,6 +618,9 b' class Magics(Configurable):'
618 posix = kw.get('posix', os.name == 'posix')
618 posix = kw.get('posix', os.name == 'posix')
619 strict = kw.get('strict', True)
619 strict = kw.get('strict', True)
620
620
621 preserve_non_opts = kw.get("preserve_non_opts", False)
622 remainder_arg_str = arg_str
623
621 # Check if we have more than one argument to warrant extra processing:
624 # Check if we have more than one argument to warrant extra processing:
622 odict = {} # Dictionary with options
625 odict = {} # Dictionary with options
623 args = arg_str.split()
626 args = arg_str.split()
@@ -629,10 +632,18 b' class Magics(Configurable):'
629 try:
632 try:
630 opts,args = getopt(argv, opt_str, long_opts)
633 opts,args = getopt(argv, opt_str, long_opts)
631 except GetoptError as e:
634 except GetoptError as e:
632 raise UsageError('%s ( allowed: "%s" %s)' % (e.msg,opt_str,
635 raise UsageError(
633 " ".join(long_opts))) from e
636 '%s ( allowed: "%s" %s)' % (e.msg, opt_str, " ".join(long_opts))
637 ) from e
634 for o,a in opts:
638 for o, a in opts:
635 if o.startswith('--'):
639 if mode == "string" and preserve_non_opts:
640 # remove option-parts from the original args-string and preserve remaining-part.
641 # This relies on the arg_split(...) and getopt(...)'s impl spec, that the parsed options are
642 # returned in the original order.
643 remainder_arg_str = remainder_arg_str.replace(o, "", 1).replace(
644 a, "", 1
645 )
646 if o.startswith("--"):
636 o = o[2:]
647 o = o[2:]
637 else:
648 else:
638 o = o[1:]
649 o = o[1:]
@@ -649,7 +660,10 b' class Magics(Configurable):'
649 # Prepare opts,args for return
660 # Prepare opts,args for return
650 opts = Struct(odict)
661 opts = Struct(odict)
651 if mode == 'string':
662 if mode == 'string':
652 args = ' '.join(args)
663 if preserve_non_opts:
664 args = remainder_arg_str.lstrip()
665 else:
666 args = " ".join(args)
653
667
654 return opts,args
668 return opts,args
655
669
@@ -104,16 +104,32 b' class AutoMagics(Magics):'
104 # all-random (note for auto-testing)
104 # all-random (note for auto-testing)
105 """
105 """
106
106
107 valid_modes = {
108 0: "Off",
109 1: "Smart",
110 2: "Full",
111 }
112
113 def errorMessage() -> str:
114 error = "Valid modes: "
115 for k, v in valid_modes.items():
116 error += str(k) + "->" + v + ", "
117 error = error[:-2] # remove tailing `, ` after last element
118 return error
119
107 if parameter_s:
120 if parameter_s:
121 if not parameter_s in map(str, valid_modes.keys()):
122 error(errorMessage())
123 return
108 arg = int(parameter_s)
124 arg = int(parameter_s)
109 else:
125 else:
110 arg = 'toggle'
126 arg = 'toggle'
111
127
112 if not arg in (0, 1, 2, 'toggle'):
128 if not arg in (*list(valid_modes.keys()), "toggle"):
113 error('Valid modes: (0->Off, 1->Smart, 2->Full')
129 error(errorMessage())
114 return
130 return
115
131
116 if arg in (0, 1, 2):
132 if arg in (valid_modes.keys()):
117 self.shell.autocall = arg
133 self.shell.autocall = arg
118 else: # toggle
134 else: # toggle
119 if self.shell.autocall:
135 if self.shell.autocall:
@@ -125,4 +141,4 b' class AutoMagics(Magics):'
125 except AttributeError:
141 except AttributeError:
126 self.shell.autocall = self._magic_state.autocall_save = 1
142 self.shell.autocall = self._magic_state.autocall_save = 1
127
143
128 print("Automatic calling is:",['OFF','Smart','Full'][self.shell.autocall])
144 print("Automatic calling is:", list(valid_modes.values())[self.shell.autocall])
@@ -493,6 +493,7 b' Currently the magic system has the following functions:""",'
493 %gui qt5 # enable PyQt5 event loop integration
493 %gui qt5 # enable PyQt5 event loop integration
494 %gui gtk # enable PyGTK event loop integration
494 %gui gtk # enable PyGTK event loop integration
495 %gui gtk3 # enable Gtk3 event loop integration
495 %gui gtk3 # enable Gtk3 event loop integration
496 %gui gtk4 # enable Gtk4 event loop integration
496 %gui tk # enable Tk event loop integration
497 %gui tk # enable Tk event loop integration
497 %gui osx # enable Cocoa event loop integration
498 %gui osx # enable Cocoa event loop integration
498 # (requires %matplotlib 1.1)
499 # (requires %matplotlib 1.1)
@@ -20,7 +20,7 b' import re'
20 import sys
20 import sys
21 import ast
21 import ast
22 from itertools import chain
22 from itertools import chain
23 from urllib.request import urlopen
23 from urllib.request import Request, urlopen
24 from urllib.parse import urlencode
24 from urllib.parse import urlencode
25 from pathlib import Path
25 from pathlib import Path
26
26
@@ -29,6 +29,7 b' from IPython.core.error import TryNext, StdinNotImplementedError, UsageError'
29 from IPython.core.macro import Macro
29 from IPython.core.macro import Macro
30 from IPython.core.magic import Magics, magics_class, line_magic
30 from IPython.core.magic import Magics, magics_class, line_magic
31 from IPython.core.oinspect import find_file, find_source_lines
31 from IPython.core.oinspect import find_file, find_source_lines
32 from IPython.core.release import version
32 from IPython.testing.skipdoctest import skip_doctest
33 from IPython.testing.skipdoctest import skip_doctest
33 from IPython.utils.contexts import preserve_keys
34 from IPython.utils.contexts import preserve_keys
34 from IPython.utils.path import get_py_filename
35 from IPython.utils.path import get_py_filename
@@ -201,6 +202,9 b' class CodeMagics(Magics):'
201 This function uses the same syntax as %history for input ranges,
202 This function uses the same syntax as %history for input ranges,
202 then saves the lines to the filename you specify.
203 then saves the lines to the filename you specify.
203
204
205 If no ranges are specified, saves history of the current session up to
206 this point.
207
204 It adds a '.py' extension to the file if you don't do so yourself, and
208 It adds a '.py' extension to the file if you don't do so yourself, and
205 it asks for confirmation before overwriting existing files.
209 it asks for confirmation before overwriting existing files.
206
210
@@ -245,20 +249,25 b' class CodeMagics(Magics):'
245
249
246 @line_magic
250 @line_magic
247 def pastebin(self, parameter_s=''):
251 def pastebin(self, parameter_s=''):
248 """Upload code to dpaste's paste bin, returning the URL.
252 """Upload code to dpaste.com, returning the URL.
249
253
250 Usage:\\
254 Usage:\\
251 %pastebin [-d "Custom description"] 1-7
255 %pastebin [-d "Custom description"][-e 24] 1-7
252
256
253 The argument can be an input history range, a filename, or the name of a
257 The argument can be an input history range, a filename, or the name of a
254 string or macro.
258 string or macro.
255
259
260 If no arguments are given, uploads the history of this session up to
261 this point.
262
256 Options:
263 Options:
257
264
258 -d: Pass a custom description for the gist. The default will say
265 -d: Pass a custom description. The default will say
259 "Pasted from IPython".
266 "Pasted from IPython".
267 -e: Pass number of days for the link to be expired.
268 The default will be 7 days.
260 """
269 """
261 opts, args = self.parse_options(parameter_s, 'd:')
270 opts, args = self.parse_options(parameter_s, "d:e:")
262
271
263 try:
272 try:
264 code = self.shell.find_user_code(args)
273 code = self.shell.find_user_code(args)
@@ -266,13 +275,30 b' class CodeMagics(Magics):'
266 print(e.args[0])
275 print(e.args[0])
267 return
276 return
268
277
269 post_data = urlencode({
278 expiry_days = 7
270 "title": opts.get('d', "Pasted from IPython"),
279 try:
271 "syntax": "python3",
280 expiry_days = int(opts.get("e", 7))
272 "content": code
281 except ValueError as e:
273 }).encode('utf-8')
282 print(e.args[0].capitalize())
283 return
284 if expiry_days < 1 or expiry_days > 365:
285 print("Expiry days should be in range of 1 to 365")
286 return
274
287
275 response = urlopen("http://dpaste.com/api/v2/", post_data)
288 post_data = urlencode(
289 {
290 "title": opts.get("d", "Pasted from IPython"),
291 "syntax": "python",
292 "content": code,
293 "expiry_days": expiry_days,
294 }
295 ).encode("utf-8")
296
297 request = Request(
298 "https://dpaste.com/api/v2/",
299 headers={"User-Agent": "IPython v{}".format(version)},
300 )
301 response = urlopen(request, post_data)
276 return response.headers.get('Location')
302 return response.headers.get('Location')
277
303
278 @line_magic
304 @line_magic
@@ -295,6 +321,9 b' class CodeMagics(Magics):'
295 where source can be a filename, URL, input history range, macro, or
321 where source can be a filename, URL, input history range, macro, or
296 element in the user namespace
322 element in the user namespace
297
323
324 If no arguments are given, loads the history of this session up to this
325 point.
326
298 Options:
327 Options:
299
328
300 -r <lines>: Specify lines or ranges of lines to load from the source.
329 -r <lines>: Specify lines or ranges of lines to load from the source.
@@ -313,6 +342,7 b' class CodeMagics(Magics):'
313 confirmation before loading source with more than 200 000 characters, unless
342 confirmation before loading source with more than 200 000 characters, unless
314 -y flag is passed or if the frontend does not support raw_input::
343 -y flag is passed or if the frontend does not support raw_input::
315
344
345 %load
316 %load myscript.py
346 %load myscript.py
317 %load 7-27
347 %load 7-27
318 %load myMacro
348 %load myMacro
@@ -324,13 +354,7 b' class CodeMagics(Magics):'
324 %load -n my_module.wonder_function
354 %load -n my_module.wonder_function
325 """
355 """
326 opts,args = self.parse_options(arg_s,'yns:r:')
356 opts,args = self.parse_options(arg_s,'yns:r:')
327
328 if not args:
329 raise UsageError('Missing filename, URL, input history range, '
330 'macro, or element in the user namespace.')
331
332 search_ns = 'n' in opts
357 search_ns = 'n' in opts
333
334 contents = self.shell.find_user_code(args, search_ns=search_ns)
358 contents = self.shell.find_user_code(args, search_ns=search_ns)
335
359
336 if 's' in opts:
360 if 's' in opts:
@@ -1073,8 +1073,9 b' class ExecutionMagics(Magics):'
1073 does not matter as long as results from timeit.py are not mixed with
1073 does not matter as long as results from timeit.py are not mixed with
1074 those from %timeit."""
1074 those from %timeit."""
1075
1075
1076 opts, stmt = self.parse_options(line,'n:r:tcp:qo',
1076 opts, stmt = self.parse_options(
1077 posix=False, strict=False)
1077 line, "n:r:tcp:qo", posix=False, strict=False, preserve_non_opts=True
1078 )
1078 if stmt == "" and cell is None:
1079 if stmt == "" and cell is None:
1079 return
1080 return
1080
1081
@@ -1234,11 +1235,15 b' class ExecutionMagics(Magics):'
1234 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
1235 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
1235 Wall time: 0.00
1236 Wall time: 0.00
1236
1237
1237 Note that the time needed by Python to compile the given expression
1238
1238 will be reported if it is more than 0.1s. In this example, the
1239 .. note::
1239 actual exponentiation is done by Python at compilation time, so while
1240 The time needed by Python to compile the given expression will be
1240 the expression can take a noticeable amount of time to compute, that
1241 reported if it is more than 0.1s.
1241 time is purely due to the compilation:
1242
1243 In the example below, the actual exponentiation is done by Python
1244 at compilation time, so while the expression can take a noticeable
1245 amount of time to compute, that time is purely due to the
1246 compilation::
1242
1247
1243 In [5]: %time 3**9999;
1248 In [5]: %time 3**9999;
1244 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
1249 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
@@ -1249,7 +1254,6 b' class ExecutionMagics(Magics):'
1249 Wall time: 0.00 s
1254 Wall time: 0.00 s
1250 Compiler : 0.78 s
1255 Compiler : 0.78 s
1251 """
1256 """
1252
1253 # fail immediately if the given expression can't be compiled
1257 # fail immediately if the given expression can't be compiled
1254
1258
1255 if line and cell:
1259 if line and cell:
@@ -184,14 +184,12 b' class HistoryMagics(Magics):'
184 n = 10 if limit is None else limit
184 n = 10 if limit is None else limit
185 hist = history_manager.get_tail(n, raw=raw, output=get_output)
185 hist = history_manager.get_tail(n, raw=raw, output=get_output)
186 else:
186 else:
187 if args.range: # Get history by ranges
188 if args.pattern:
187 if args.pattern:
189 range_pattern = "*" + " ".join(args.pattern) + "*"
188 range_pattern = "*" + " ".join(args.pattern) + "*"
190 print_nums = True
189 print_nums = True
191 hist = history_manager.get_range_by_str(" ".join(args.range),
190 hist = history_manager.get_range_by_str(
192 raw, get_output)
191 " ".join(args.range), raw, get_output
193 else: # Just get history for the current session
192 )
194 hist = history_manager.get_range(raw=raw, output=get_output)
195
193
196 # We could be displaying the entire history, so let's not try to pull
194 # We could be displaying the entire history, so let's not try to pull
197 # it into a list in memory. Anything that needs more space will just
195 # it into a list in memory. Anything that needs more space will just
@@ -282,6 +280,7 b' class HistoryMagics(Magics):'
282 return
280 return
283 else:
281 else:
284 self.shell.set_next_input(cmd.rstrip())
282 self.shell.set_next_input(cmd.rstrip())
283 return
285 print("Couldn't evaluate or find in history:", arg)
284 print("Couldn't evaluate or find in history:", arg)
286
285
287 @line_magic
286 @line_magic
@@ -301,6 +300,14 b' class HistoryMagics(Magics):'
301 opts, args = self.parse_options(parameter_s, 'l:g:', mode='string')
300 opts, args = self.parse_options(parameter_s, 'l:g:', mode='string')
302 if "l" in opts: # Last n lines
301 if "l" in opts: # Last n lines
303 n = int(opts['l'])
302 n = int(opts['l'])
303
304 if n == 0:
305 print("Requested 0 last lines - nothing to run")
306 return
307 elif n < 0:
308 print("Number of lines to rerun cannot be negative")
309 return
310
304 hist = self.shell.history_manager.get_tail(n)
311 hist = self.shell.history_manager.get_tail(n)
305 elif "g" in opts: # Search
312 elif "g" in opts: # Search
306 p = "*"+opts['g']+"*"
313 p = "*"+opts['g']+"*"
@@ -302,33 +302,36 b' class OSMagics(Magics):'
302 """Change the current working directory.
302 """Change the current working directory.
303
303
304 This command automatically maintains an internal list of directories
304 This command automatically maintains an internal list of directories
305 you visit during your IPython session, in the variable _dh. The
305 you visit during your IPython session, in the variable ``_dh``. The
306 command %dhist shows this history nicely formatted. You can also
306 command :magic:`%dhist` shows this history nicely formatted. You can
307 do 'cd -<tab>' to see directory history conveniently.
307 also do ``cd -<tab>`` to see directory history conveniently.
308
309 Usage:
308 Usage:
310
309
311 cd 'dir': changes to directory 'dir'.
310 - ``cd 'dir'``: changes to directory 'dir'.
312
311 - ``cd -``: changes to the last visited directory.
313 cd -: changes to the last visited directory.
312 - ``cd -<n>``: changes to the n-th directory in the directory history.
314
313 - ``cd --foo``: change to directory that matches 'foo' in history
315 cd -<n>: changes to the n-th directory in the directory history.
314 - ``cd -b <bookmark_name>``: jump to a bookmark set by %bookmark
315 - Hitting a tab key after ``cd -b`` allows you to tab-complete
316 bookmark names.
316
317
317 cd --foo: change to directory that matches 'foo' in history
318 .. note::
319 ``cd <bookmark_name>`` is enough if there is no directory
320 ``<bookmark_name>``, but a bookmark with the name exists.
318
321
319 cd -b <bookmark_name>: jump to a bookmark set by %bookmark
320 (note: cd <bookmark_name> is enough if there is no
321 directory <bookmark_name>, but a bookmark with the name exists.)
322 'cd -b <tab>' allows you to tab-complete bookmark names.
323
322
324 Options:
323 Options:
325
324
326 -q: quiet. Do not print the working directory after the cd command is
325 -q Be quiet. Do not print the working directory after the
327 executed. By default IPython's cd command does print this directory,
326 cd command is executed. By default IPython's cd
328 since the default prompts do not display path information.
327 command does print this directory, since the default
328 prompts do not display path information.
329
330 .. note::
331 Note that ``!cd`` doesn't work for this purpose because the shell
332 where ``!command`` runs is immediately discarded after executing
333 'command'.
329
334
330 Note that !cd doesn't work for this purpose because the shell where
331 !command runs is immediately discarded after executing 'command'.
332
335
333 Examples
336 Examples
334 --------
337 --------
@@ -436,11 +439,11 b' class OSMagics(Magics):'
436
439
437 Usage:\\
440 Usage:\\
438
441
439 %env: lists all environment variables/values
442 :``%env``: lists all environment variables/values
440 %env var: get value for var
443 :``%env var``: get value for var
441 %env var val: set value for var
444 :``%env var val``: set value for var
442 %env var=val: set value for var
445 :``%env var=val``: set value for var
443 %env var=$val: set value for var, using python expansion if possible
446 :``%env var=$val``: set value for var, using python expansion if possible
444 """
447 """
445 if parameter_s.strip():
448 if parameter_s.strip():
446 split = '=' if '=' in parameter_s else ' '
449 split = '=' if '=' in parameter_s else ' '
@@ -803,17 +806,16 b' class OSMagics(Magics):'
803 to be Python source and will show it with syntax highlighting.
806 to be Python source and will show it with syntax highlighting.
804
807
805 This magic command can either take a local filename, an url,
808 This magic command can either take a local filename, an url,
806 an history range (see %history) or a macro as argument ::
809 an history range (see %history) or a macro as argument.
810
811 If no parameter is given, prints out history of current session up to
812 this point. ::
807
813
808 %pycat myscript.py
814 %pycat myscript.py
809 %pycat 7-27
815 %pycat 7-27
810 %pycat myMacro
816 %pycat myMacro
811 %pycat http://www.example.com/myscript.py
817 %pycat http://www.example.com/myscript.py
812 """
818 """
813 if not parameter_s:
814 raise UsageError('Missing filename, URL, input history range, '
815 'or macro.')
816
817 try :
819 try:
818 cont = self.shell.find_user_code(parameter_s, skip_encoding_cookie=False)
820 cont = self.shell.find_user_code(parameter_s, skip_encoding_cookie=False)
819 except (ValueError, IOError):
821 except (ValueError, IOError):
@@ -66,7 +66,14 b' class PackagingMagics(Magics):'
66 Usage:
66 Usage:
67 %pip install [pkgs]
67 %pip install [pkgs]
68 """
68 """
69 self.shell.system(' '.join([sys.executable, '-m', 'pip', line]))
69 python = sys.executable
70 if sys.platform == "win32":
71 python = '"' + python + '"'
72 else:
73 python = shlex.quote(python)
74
75 self.shell.system(" ".join([python, "-m", "pip", line]))
76
70 print("Note: you may need to restart the kernel to use updated packages.")
77 print("Note: you may need to restart the kernel to use updated packages.")
71
78
72 @line_magic
79 @line_magic
@@ -88,7 +88,7 b' class PylabMagics(Magics):'
88 You can list the available backends using the -l/--list option::
88 You can list the available backends using the -l/--list option::
89
89
90 In [4]: %matplotlib --list
90 In [4]: %matplotlib --list
91 Available matplotlib backends: ['osx', 'qt4', 'qt5', 'gtk3', 'notebook', 'wx', 'qt', 'nbagg',
91 Available matplotlib backends: ['osx', 'qt4', 'qt5', 'gtk3', 'gtk4', 'notebook', 'wx', 'qt', 'nbagg',
92 'gtk', 'tk', 'inline']
92 'gtk', 'tk', 'inline']
93 """
93 """
94 args = magic_arguments.parse_argstring(self.matplotlib, line)
94 args = magic_arguments.parse_argstring(self.matplotlib, line)
@@ -199,6 +199,7 b' class ScriptMagics(Magics):'
199 _handle_stream(process.stderr, args.err, sys.stderr)
199 _handle_stream(process.stderr, args.err, sys.stderr)
200 )
200 )
201 await asyncio.wait([stdout_task, stderr_task])
201 await asyncio.wait([stdout_task, stderr_task])
202 await process.wait()
202
203
203 if sys.platform.startswith("win"):
204 if sys.platform.startswith("win"):
204 asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
205 asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
@@ -212,7 +213,7 b' class ScriptMagics(Magics):'
212 *cmd,
213 *cmd,
213 stdout=asyncio.subprocess.PIPE,
214 stdout=asyncio.subprocess.PIPE,
214 stderr=asyncio.subprocess.PIPE,
215 stderr=asyncio.subprocess.PIPE,
215 stdin=asyncio.subprocess.PIPE
216 stdin=asyncio.subprocess.PIPE,
216 )
217 )
217 )
218 )
218 except OSError as e:
219 except OSError as e:
@@ -264,7 +265,11 b' class ScriptMagics(Magics):'
264 print("Error while terminating subprocess (pid=%i): %s" % (p.pid, e))
265 print("Error while terminating subprocess (pid=%i): %s" % (p.pid, e))
265 return
266 return
266 if args.raise_error and p.returncode!=0:
267 if args.raise_error and p.returncode!=0:
267 raise CalledProcessError(p.returncode, cell)
268 # If we get here and p.returncode is still None, we must have
269 # killed it but not yet seen its return code. We don't wait for it,
270 # in case it's stuck in uninterruptible sleep. -9 = SIGKILL
271 rc = p.returncode or -9
272 raise CalledProcessError(rc, cell)
268
273
269 def _run_script(self, p, cell, to_close):
274 def _run_script(self, p, cell, to_close):
270 """callback for running the script in the background"""
275 """callback for running the script in the background"""
@@ -5,29 +5,33 b''
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 from io import BytesIO
7 from io import BytesIO
8 import warnings
8
9
9 from IPython.core.display import _pngxy
10 from IPython.core.display import _pngxy
10 from IPython.utils.decorators import flag_calls
11 from IPython.utils.decorators import flag_calls
11
12
12 # If user specifies a GUI, that dictates the backend, otherwise we read the
13 # If user specifies a GUI, that dictates the backend, otherwise we read the
13 # user's mpl default from the mpl rc structure
14 # user's mpl default from the mpl rc structure
14 backends = {'tk': 'TkAgg',
15 backends = {
15 'gtk': 'GTKAgg',
16 "tk": "TkAgg",
16 'gtk3': 'GTK3Agg',
17 "gtk": "GTKAgg",
17 'wx': 'WXAgg',
18 "gtk3": "GTK3Agg",
18 'qt4': 'Qt4Agg',
19 "gtk4": "GTK4Agg",
19 'qt5': 'Qt5Agg',
20 "wx": "WXAgg",
20 'qt': 'Qt5Agg',
21 "qt4": "Qt4Agg",
21 'osx': 'MacOSX',
22 "qt5": "Qt5Agg",
22 'nbagg': 'nbAgg',
23 "qt6": "QtAgg",
23 'notebook': 'nbAgg',
24 "qt": "Qt5Agg",
24 'agg': 'agg',
25 "osx": "MacOSX",
25 'svg': 'svg',
26 "nbagg": "nbAgg",
26 'pdf': 'pdf',
27 "notebook": "nbAgg",
27 'ps': 'ps',
28 "agg": "agg",
28 'inline': 'module://ipykernel.pylab.backend_inline',
29 "svg": "svg",
29 'ipympl': 'module://ipympl.backend_nbagg',
30 "pdf": "pdf",
30 'widget': 'module://ipympl.backend_nbagg',
31 "ps": "ps",
32 "inline": "module://matplotlib_inline.backend_inline",
33 "ipympl": "module://ipympl.backend_nbagg",
34 "widget": "module://ipympl.backend_nbagg",
31 }
35 }
32
36
33 # We also need a reverse backends2guis mapping that will properly choose which
37 # We also need a reverse backends2guis mapping that will properly choose which
@@ -39,17 +43,18 b' backend2gui = dict(zip(backends.values(), backends.keys()))'
39 backend2gui['Qt4Agg'] = 'qt'
43 backend2gui['Qt4Agg'] = 'qt'
40 # In the reverse mapping, there are a few extra valid matplotlib backends that
44 # In the reverse mapping, there are a few extra valid matplotlib backends that
41 # map to the same GUI support
45 # map to the same GUI support
42 backend2gui['GTK'] = backend2gui['GTKCairo'] = 'gtk'
46 backend2gui["GTK"] = backend2gui["GTKCairo"] = "gtk"
43 backend2gui['GTK3Cairo'] = 'gtk3'
47 backend2gui["GTK3Cairo"] = "gtk3"
44 backend2gui['WX'] = 'wx'
48 backend2gui["GTK4Cairo"] = "gtk4"
45 backend2gui['CocoaAgg'] = 'osx'
49 backend2gui["WX"] = "wx"
50 backend2gui["CocoaAgg"] = "osx"
46 # And some backends that don't need GUI integration
51 # And some backends that don't need GUI integration
47 del backend2gui['nbAgg']
52 del backend2gui["nbAgg"]
48 del backend2gui['agg']
53 del backend2gui["agg"]
49 del backend2gui['svg']
54 del backend2gui["svg"]
50 del backend2gui['pdf']
55 del backend2gui["pdf"]
51 del backend2gui['ps']
56 del backend2gui["ps"]
52 del backend2gui['module://ipykernel.pylab.backend_inline']
57 del backend2gui["module://matplotlib_inline.backend_inline"]
53
58
54 #-----------------------------------------------------------------------------
59 #-----------------------------------------------------------------------------
55 # Matplotlib utilities
60 # Matplotlib utilities
@@ -274,7 +279,7 b' def find_gui_and_backend(gui=None, gui_select=None):'
274 Returns
279 Returns
275 -------
280 -------
276 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
281 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
277 'WXAgg','Qt4Agg','module://ipykernel.pylab.backend_inline','agg').
282 'WXAgg','Qt4Agg','module://matplotlib_inline.backend_inline','agg').
278 """
283 """
279
284
280 import matplotlib
285 import matplotlib
@@ -361,7 +366,12 b' def import_pylab(user_ns, import_all=True):'
361
366
362
367
363 def configure_inline_support(shell, backend):
368 def configure_inline_support(shell, backend):
364 """Configure an IPython shell object for matplotlib use.
369 """
370 .. deprecated: 7.23
371
372 use `matplotlib_inline.backend_inline.configure_inline_support()`
373
374 Configure an IPython shell object for matplotlib use.
365
375
366 Parameters
376 Parameters
367 ----------
377 ----------
@@ -369,51 +379,15 b' def configure_inline_support(shell, backend):'
369
379
370 backend : matplotlib backend
380 backend : matplotlib backend
371 """
381 """
372 # If using our svg payload backend, register the post-execution
382 warnings.warn(
373 # function that will pick up the results for display. This can only be
383 "`configure_inline_support` is deprecated since IPython 7.23, directly "
374 # done with access to the real shell object.
384 "use `matplotlib_inline.backend_inline.configure_inline_support()`",
385 DeprecationWarning,
386 stacklevel=2,
387 )
375
388
376 # Note: if we can't load the inline backend, then there's no point
389 from matplotlib_inline.backend_inline import (
377 # continuing (such as in terminal-only shells in environments without
390 configure_inline_support as configure_inline_support_orig,
378 # zeromq available).
391 )
379 try:
380 from ipykernel.pylab.backend_inline import InlineBackend
381 except ImportError:
382 return
383 import matplotlib
384
392
385 cfg = InlineBackend.instance(parent=shell)
393 configure_inline_support_orig(shell, backend)
386 cfg.shell = shell
387 if cfg not in shell.configurables:
388 shell.configurables.append(cfg)
389
390 if backend == backends['inline']:
391 from ipykernel.pylab.backend_inline import flush_figures
392 shell.events.register('post_execute', flush_figures)
393
394 # Save rcParams that will be overwrittern
395 shell._saved_rcParams = {}
396 for k in cfg.rc:
397 shell._saved_rcParams[k] = matplotlib.rcParams[k]
398 # load inline_rc
399 matplotlib.rcParams.update(cfg.rc)
400 new_backend_name = "inline"
401 else:
402 from ipykernel.pylab.backend_inline import flush_figures
403 try:
404 shell.events.unregister('post_execute', flush_figures)
405 except ValueError:
406 pass
407 if hasattr(shell, '_saved_rcParams'):
408 matplotlib.rcParams.update(shell._saved_rcParams)
409 del shell._saved_rcParams
410 new_backend_name = "other"
411
412 # only enable the formats once -> don't change the enabled formats (which the user may
413 # has changed) when getting another "%matplotlib inline" call.
414 # See https://github.com/ipython/ipykernel/issues/29
415 cur_backend = getattr(configure_inline_support, "current_backend", "unset")
416 if new_backend_name != cur_backend:
417 # Setup the default figure format
418 select_figure_formats(shell, cfg.figure_formats, **cfg.print_figure_kwargs)
419 configure_inline_support.current_backend = new_backend_name
@@ -1,6 +1,6 b''
1 from IPython.utils.capture import capture_output
1 from IPython.utils.capture import capture_output
2
2
3 import nose.tools as nt
3 import pytest
4
4
5 def test_alias_lifecycle():
5 def test_alias_lifecycle():
6 name = 'test_alias1'
6 name = 'test_alias1'
@@ -9,8 +9,8 b' def test_alias_lifecycle():'
9 am.clear_aliases()
9 am.clear_aliases()
10 am.define_alias(name, cmd)
10 am.define_alias(name, cmd)
11 assert am.is_alias(name)
11 assert am.is_alias(name)
12 nt.assert_equal(am.retrieve_alias(name), cmd)
12 assert am.retrieve_alias(name) == cmd
13 nt.assert_in((name, cmd), am.aliases)
13 assert (name, cmd) in am.aliases
14
14
15 # Test running the alias
15 # Test running the alias
16 orig_system = _ip.system
16 orig_system = _ip.system
@@ -19,16 +19,16 b' def test_alias_lifecycle():'
19 try:
19 try:
20 _ip.run_cell('%{}'.format(name))
20 _ip.run_cell('%{}'.format(name))
21 result = [c.strip() for c in result]
21 result = [c.strip() for c in result]
22 nt.assert_equal(result, [cmd])
22 assert result == [cmd]
23 finally:
23 finally:
24 _ip.system = orig_system
24 _ip.system = orig_system
25
25
26 # Test removing the alias
26 # Test removing the alias
27 am.undefine_alias(name)
27 am.undefine_alias(name)
28 assert not am.is_alias(name)
28 assert not am.is_alias(name)
29 with nt.assert_raises(ValueError):
29 with pytest.raises(ValueError):
30 am.retrieve_alias(name)
30 am.retrieve_alias(name)
31 nt.assert_not_in((name, cmd), am.aliases)
31 assert (name, cmd) not in am.aliases
32
32
33
33
34 def test_alias_args_error():
34 def test_alias_args_error():
@@ -38,7 +38,8 b' def test_alias_args_error():'
38 with capture_output() as cap:
38 with capture_output() as cap:
39 _ip.run_cell('parts 1')
39 _ip.run_cell('parts 1')
40
40
41 nt.assert_equal(cap.stderr.split(':')[0], 'UsageError')
41 assert cap.stderr.split(":")[0] == "UsageError"
42
42
43
43 def test_alias_args_commented():
44 def test_alias_args_commented():
44 """Check that alias correctly ignores 'commented out' args"""
45 """Check that alias correctly ignores 'commented out' args"""
@@ -62,4 +63,4 b' def test_alias_args_commented_nargs():'
62 assert am.is_alias(alias_name)
63 assert am.is_alias(alias_name)
63
64
64 thealias = am.get_alias(alias_name)
65 thealias = am.get_alias(alias_name)
65 nt.assert_equal(thealias.nargs, 1)
66 assert thealias.nargs == 1
@@ -18,7 +18,7 b' import linecache'
18 import sys
18 import sys
19
19
20 # Third-party imports
20 # Third-party imports
21 import nose.tools as nt
21 import pytest
22
22
23 # Our own imports
23 # Our own imports
24 from IPython.core import compilerop
24 from IPython.core import compilerop
@@ -30,13 +30,13 b' from IPython.core import compilerop'
30 def test_code_name():
30 def test_code_name():
31 code = 'x=1'
31 code = 'x=1'
32 name = compilerop.code_name(code)
32 name = compilerop.code_name(code)
33 nt.assert_true(name.startswith('<ipython-input-0'))
33 assert name.startswith("<ipython-input-0")
34
34
35
35
36 def test_code_name2():
36 def test_code_name2():
37 code = 'x=1'
37 code = 'x=1'
38 name = compilerop.code_name(code, 9)
38 name = compilerop.code_name(code, 9)
39 nt.assert_true(name.startswith('<ipython-input-9'))
39 assert name.startswith("<ipython-input-9")
40
40
41
41
42 def test_cache():
42 def test_cache():
@@ -45,18 +45,18 b' def test_cache():'
45 cp = compilerop.CachingCompiler()
45 cp = compilerop.CachingCompiler()
46 ncache = len(linecache.cache)
46 ncache = len(linecache.cache)
47 cp.cache('x=1')
47 cp.cache('x=1')
48 nt.assert_true(len(linecache.cache) > ncache)
48 assert len(linecache.cache) > ncache
49
49
50 def test_proper_default_encoding():
50 def test_proper_default_encoding():
51 # Check we're in a proper Python 2 environment (some imports, such
51 # Check we're in a proper Python 2 environment (some imports, such
52 # as GTK, can change the default encoding, which can hide bugs.)
52 # as GTK, can change the default encoding, which can hide bugs.)
53 nt.assert_equal(sys.getdefaultencoding(), "utf-8")
53 assert sys.getdefaultencoding() == "utf-8"
54
54
55 def test_cache_unicode():
55 def test_cache_unicode():
56 cp = compilerop.CachingCompiler()
56 cp = compilerop.CachingCompiler()
57 ncache = len(linecache.cache)
57 ncache = len(linecache.cache)
58 cp.cache(u"t = 'žćčšđ'")
58 cp.cache(u"t = 'žćčšđ'")
59 nt.assert_true(len(linecache.cache) > ncache)
59 assert len(linecache.cache) > ncache
60
60
61 def test_compiler_check_cache():
61 def test_compiler_check_cache():
62 """Test the compiler properly manages the cache.
62 """Test the compiler properly manages the cache.
@@ -157,6 +157,11 b' def test_bad_module_all():'
157 nt.assert_in('puppies', results)
157 nt.assert_in('puppies', results)
158 for r in results:
158 for r in results:
159 nt.assert_is_instance(r, str)
159 nt.assert_is_instance(r, str)
160
161 # bad_all doesn't contain submodules, but this completion
162 # should finish without raising an exception:
163 results = module_completion("import bad_all.")
164 nt.assert_equal(results, [])
160 finally:
165 finally:
161 sys.path.remove(testsdir)
166 sys.path.remove(testsdir)
162
167
@@ -176,3 +181,14 b' def test_module_without_init():'
176 assert s == []
181 assert s == []
177 finally:
182 finally:
178 sys.path.remove(tmpdir)
183 sys.path.remove(tmpdir)
184
185
186 def test_valid_exported_submodules():
187 """
188 Test checking exported (__all__) objects are submodules
189 """
190 results = module_completion("import os.pa")
191 # ensure we get a valid submodule:
192 nt.assert_in("os.path", results)
193 # ensure we don't get objects that aren't submodules:
194 nt.assert_not_in("os.pathconf", results)
@@ -321,3 +321,70 b' f()'
321 child.expect("ipdb>")
321 child.expect("ipdb>")
322
322
323 child.close()
323 child.close()
324
325
326 @skip_win32
327 def test_where_erase_value():
328 """Test that `where` does not access f_locals and erase values."""
329 import pexpect
330
331 env = os.environ.copy()
332 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
333
334 child = pexpect.spawn(
335 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
336 )
337 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
338
339 child.expect("IPython")
340 child.expect("\n")
341 child.expect_exact("In [1]")
342
343 block = dedent(
344 """
345 def simple_f():
346 myvar = 1
347 print(myvar)
348 1/0
349 print(myvar)
350 simple_f() """
351 )
352
353 for line in block.splitlines():
354 child.sendline(line)
355 child.expect_exact(line)
356 child.expect_exact("ZeroDivisionError")
357 child.expect_exact("In [2]:")
358
359 child.sendline("%debug")
360
361 ##
362 child.expect("ipdb>")
363
364 child.sendline("myvar")
365 child.expect("1")
366
367 ##
368 child.expect("ipdb>")
369
370 child.sendline("myvar = 2")
371
372 ##
373 child.expect_exact("ipdb>")
374
375 child.sendline("myvar")
376
377 child.expect_exact("2")
378
379 ##
380 child.expect("ipdb>")
381 child.sendline("where")
382
383 ##
384 child.expect("ipdb>")
385 child.sendline("myvar")
386
387 child.expect_exact("2")
388 child.expect("ipdb>")
389
390 child.close()
@@ -135,7 +135,7 b' def test_image_filename_defaults():'
135 nt.assert_is_none(img._repr_jpeg_())
135 nt.assert_is_none(img._repr_jpeg_())
136
136
137 def _get_inline_config():
137 def _get_inline_config():
138 from ipykernel.pylab.config import InlineBackend
138 from matplotlib_inline.config import InlineBackend
139 return InlineBackend.instance()
139 return InlineBackend.instance()
140
140
141
141
@@ -183,7 +183,7 b' def test_set_matplotlib_formats_kwargs():'
183 ip = get_ipython()
183 ip = get_ipython()
184 cfg = _get_inline_config()
184 cfg = _get_inline_config()
185 cfg.print_figure_kwargs.update(dict(foo='bar'))
185 cfg.print_figure_kwargs.update(dict(foo='bar'))
186 kwargs = dict(quality=10)
186 kwargs = dict(dpi=150)
187 display.set_matplotlib_formats('png', **kwargs)
187 display.set_matplotlib_formats('png', **kwargs)
188 formatter = ip.display_formatter.formatters['image/png']
188 formatter = ip.display_formatter.formatters['image/png']
189 f = formatter.lookup_by_type(Figure)
189 f = formatter.lookup_by_type(Figure)
@@ -457,6 +457,7 b' def test_display_handle():'
457 'update': True,
457 'update': True,
458 })
458 })
459
459
460
460 def test_image_alt_tag():
461 def test_image_alt_tag():
461 """Simple test for display.Image(args, alt=x,)"""
462 """Simple test for display.Image(args, alt=x,)"""
462 thisurl = "http://example.com/image.png"
463 thisurl = "http://example.com/image.png"
@@ -480,3 +481,8 b' def test_image_alt_tag():'
480 nt.assert_equal(img.alt, "an image")
481 nt.assert_equal(img.alt, "an image")
481 _, md = img._repr_png_()
482 _, md = img._repr_png_()
482 nt.assert_equal(md["alt"], "an image")
483 nt.assert_equal(md["alt"], "an image")
484
485
486 @nt.raises(FileNotFoundError)
487 def test_image_bad_filename_raises_proper_exception():
488 display.Image("/this/file/does/not/exist/")._repr_png_()
@@ -48,16 +48,16 b' def foo_printer(obj, pp, cycle):'
48 def test_pretty():
48 def test_pretty():
49 f = PlainTextFormatter()
49 f = PlainTextFormatter()
50 f.for_type(A, foo_printer)
50 f.for_type(A, foo_printer)
51 nt.assert_equal(f(A()), 'foo')
51 assert f(A()) == "foo"
52 nt.assert_equal(f(B()), 'B()')
52 assert f(B()) == "B()"
53 nt.assert_equal(f(GoodPretty()), 'foo')
53 assert f(GoodPretty()) == "foo"
54 # Just don't raise an exception for the following:
54 # Just don't raise an exception for the following:
55 f(BadPretty())
55 f(BadPretty())
56
56
57 f.pprint = False
57 f.pprint = False
58 nt.assert_equal(f(A()), 'A()')
58 assert f(A()) == "A()"
59 nt.assert_equal(f(B()), 'B()')
59 assert f(B()) == "B()"
60 nt.assert_equal(f(GoodPretty()), 'GoodPretty()')
60 assert f(GoodPretty()) == "GoodPretty()"
61
61
62
62
63 def test_deferred():
63 def test_deferred():
@@ -66,29 +66,30 b' def test_deferred():'
66 def test_precision():
66 def test_precision():
67 """test various values for float_precision."""
67 """test various values for float_precision."""
68 f = PlainTextFormatter()
68 f = PlainTextFormatter()
69 nt.assert_equal(f(pi), repr(pi))
69 assert f(pi) == repr(pi)
70 f.float_precision = 0
70 f.float_precision = 0
71 if numpy:
71 if numpy:
72 po = numpy.get_printoptions()
72 po = numpy.get_printoptions()
73 nt.assert_equal(po['precision'], 0)
73 assert po["precision"] == 0
74 nt.assert_equal(f(pi), '3')
74 assert f(pi) == "3"
75 f.float_precision = 2
75 f.float_precision = 2
76 if numpy:
76 if numpy:
77 po = numpy.get_printoptions()
77 po = numpy.get_printoptions()
78 nt.assert_equal(po['precision'], 2)
78 assert po["precision"] == 2
79 nt.assert_equal(f(pi), '3.14')
79 assert f(pi) == "3.14"
80 f.float_precision = '%g'
80 f.float_precision = "%g"
81 if numpy:
81 if numpy:
82 po = numpy.get_printoptions()
82 po = numpy.get_printoptions()
83 nt.assert_equal(po['precision'], 2)
83 assert po["precision"] == 2
84 nt.assert_equal(f(pi), '3.14159')
84 assert f(pi) == "3.14159"
85 f.float_precision = '%e'
85 f.float_precision = "%e"
86 nt.assert_equal(f(pi), '3.141593e+00')
86 assert f(pi) == "3.141593e+00"
87 f.float_precision = ''
87 f.float_precision = ""
88 if numpy:
88 if numpy:
89 po = numpy.get_printoptions()
89 po = numpy.get_printoptions()
90 nt.assert_equal(po['precision'], 8)
90 assert po["precision"] == 8
91 nt.assert_equal(f(pi), repr(pi))
91 assert f(pi) == repr(pi)
92
92
93
93 def test_bad_precision():
94 def test_bad_precision():
94 """test various invalid values for float_precision."""
95 """test various invalid values for float_precision."""
@@ -260,8 +261,9 b' def test_nowarn_notimplemented():'
260 with capture_output() as captured:
261 with capture_output() as captured:
261 result = f(h)
262 result = f(h)
262 nt.assert_is(result, None)
263 nt.assert_is(result, None)
263 nt.assert_equal("", captured.stderr)
264 assert "" == captured.stderr
264 nt.assert_equal("", captured.stdout)
265 assert "" == captured.stdout
266
265
267
266 def test_warn_error_for_type():
268 def test_warn_error_for_type():
267 f = HTMLFormatter()
269 f = HTMLFormatter()
@@ -307,7 +309,8 b' class MakePDF(object):'
307 def test_pdf_formatter():
309 def test_pdf_formatter():
308 pdf = MakePDF()
310 pdf = MakePDF()
309 f = PDFFormatter()
311 f = PDFFormatter()
310 nt.assert_equal(f(pdf), 'PDF')
312 assert f(pdf) == "PDF"
313
311
314
312 def test_print_method_bound():
315 def test_print_method_bound():
313 f = HTMLFormatter()
316 f = HTMLFormatter()
@@ -321,8 +324,9 b' def test_print_method_bound():'
321
324
322 with capture_output() as captured:
325 with capture_output() as captured:
323 result = f(MyHTML())
326 result = f(MyHTML())
324 nt.assert_equal(result, "hello")
327 assert result == "hello"
325 nt.assert_equal(captured.stderr, "")
328 assert captured.stderr == ""
329
326
330
327 def test_print_method_weird():
331 def test_print_method_weird():
328
332
@@ -333,7 +337,7 b' def test_print_method_weird():'
333 f = HTMLFormatter()
337 f = HTMLFormatter()
334
338
335 text_hat = TextMagicHat()
339 text_hat = TextMagicHat()
336 nt.assert_equal(text_hat._repr_html_, '_repr_html_')
340 assert text_hat._repr_html_ == "_repr_html_"
337 with capture_output() as captured:
341 with capture_output() as captured:
338 result = f(text_hat)
342 result = f(text_hat)
339
343
@@ -348,7 +352,7 b' def test_print_method_weird():'
348 with capture_output() as captured:
352 with capture_output() as captured:
349 result = f(call_hat)
353 result = f(call_hat)
350
354
351 nt.assert_equal(result, None)
355 assert result is None
352
356
353 class BadReprArgs(object):
357 class BadReprArgs(object):
354 def _repr_html_(self, extra, args):
358 def _repr_html_(self, extra, args):
@@ -369,24 +373,25 b' def test_format_config():'
369 with capture_output() as captured:
373 with capture_output() as captured:
370 result = f(cfg)
374 result = f(cfg)
371 nt.assert_is(result, None)
375 nt.assert_is(result, None)
372 nt.assert_equal(captured.stderr, "")
376 assert captured.stderr == ""
373
377
374 with capture_output() as captured:
378 with capture_output() as captured:
375 result = f(Config)
379 result = f(Config)
376 nt.assert_is(result, None)
380 nt.assert_is(result, None)
377 nt.assert_equal(captured.stderr, "")
381 assert captured.stderr == ""
382
378
383
379 def test_pretty_max_seq_length():
384 def test_pretty_max_seq_length():
380 f = PlainTextFormatter(max_seq_length=1)
385 f = PlainTextFormatter(max_seq_length=1)
381 lis = list(range(3))
386 lis = list(range(3))
382 text = f(lis)
387 text = f(lis)
383 nt.assert_equal(text, '[0, ...]')
388 assert text == "[0, ...]"
384 f.max_seq_length = 0
389 f.max_seq_length = 0
385 text = f(lis)
390 text = f(lis)
386 nt.assert_equal(text, '[0, 1, 2]')
391 assert text == "[0, 1, 2]"
387 text = f(list(range(1024)))
392 text = f(list(range(1024)))
388 lines = text.splitlines()
393 lines = text.splitlines()
389 nt.assert_equal(len(lines), 1024)
394 assert len(lines) == 1024
390
395
391
396
392 def test_ipython_display_formatter():
397 def test_ipython_display_formatter():
@@ -411,14 +416,14 b' def test_ipython_display_formatter():'
411 no = NotSelfDisplaying()
416 no = NotSelfDisplaying()
412
417
413 d, md = f.format(no)
418 d, md = f.format(no)
414 nt.assert_equal(d, {'text/plain': repr(no)})
419 assert d == {"text/plain": repr(no)}
415 nt.assert_equal(md, {})
420 assert md == {}
416 nt.assert_equal(catcher, [])
421 assert catcher == []
417
422
418 d, md = f.format(yes)
423 d, md = f.format(yes)
419 nt.assert_equal(d, {})
424 assert d == {}
420 nt.assert_equal(md, {})
425 assert md == {}
421 nt.assert_equal(catcher, [yes])
426 assert catcher == [yes]
422
427
423 f.ipython_display_formatter.enabled = save_enabled
428 f.ipython_display_formatter.enabled = save_enabled
424
429
@@ -431,8 +436,8 b' def test_json_as_string_deprecated():'
431 f = JSONFormatter()
436 f = JSONFormatter()
432 with warnings.catch_warnings(record=True) as w:
437 with warnings.catch_warnings(record=True) as w:
433 d = f(JSONString())
438 d = f(JSONString())
434 nt.assert_equal(d, {})
439 assert d == {}
435 nt.assert_equal(len(w), 1)
440 assert len(w) == 1
436
441
437
442
438 def test_repr_mime():
443 def test_repr_mime():
@@ -459,18 +464,21 b' def test_repr_mime():'
459 d, md = f.format(obj)
464 d, md = f.format(obj)
460 html_f.enabled = save_enabled
465 html_f.enabled = save_enabled
461
466
462 nt.assert_equal(sorted(d), ['application/json+test.v2',
467 assert sorted(d) == [
463 'image/png',
468 "application/json+test.v2",
464 'plain/text',
469 "image/png",
465 'text/html',
470 "plain/text",
466 'text/plain'])
471 "text/html",
467 nt.assert_equal(md, {})
472 "text/plain",
473 ]
474 assert md == {}
468
475
469 d, md = f.format(obj, include={'image/png'})
476 d, md = f.format(obj, include={"image/png"})
470 nt.assert_equal(list(d.keys()), ['image/png'],
477 assert list(d.keys()) == [
471 'Include should filter out even things from repr_mimebundle')
478 "image/png"
472 nt.assert_equal(d['image/png'], 'i-overwrite', '_repr_mimebundle_ take precedence')
479 ], "Include should filter out even things from repr_mimebundle"
473
480
481 assert d["image/png"] == "i-overwrite", "_repr_mimebundle_ take precedence"
474
482
475
483
476 def test_pass_correct_include_exclude():
484 def test_pass_correct_include_exclude():
@@ -514,13 +522,14 b' def test_repr_mime_meta():'
514 f = get_ipython().display_formatter
522 f = get_ipython().display_formatter
515 obj = HasReprMimeMeta()
523 obj = HasReprMimeMeta()
516 d, md = f.format(obj)
524 d, md = f.format(obj)
517 nt.assert_equal(sorted(d), ['image/png', 'text/plain'])
525 assert sorted(d) == ["image/png", "text/plain"]
518 nt.assert_equal(md, {
526 assert md == {
519 'image/png': {
527 "image/png": {
520 'width': 5,
528 "width": 5,
521 'height': 10,
529 "height": 10,
522 }
530 }
523 })
531 }
532
524
533
525 def test_repr_mime_failure():
534 def test_repr_mime_failure():
526 class BadReprMime(object):
535 class BadReprMime(object):
@@ -530,4 +539,4 b' def test_repr_mime_failure():'
530 f = get_ipython().display_formatter
539 f = get_ipython().display_formatter
531 obj = BadReprMime()
540 obj = BadReprMime()
532 d, md = f.format(obj)
541 d, md = f.format(obj)
533 nt.assert_in('text/plain', d)
542 assert "text/plain" in d
@@ -160,6 +160,14 b' def test_extract_hist_ranges():'
160 actual = list(extract_hist_ranges(instr))
160 actual = list(extract_hist_ranges(instr))
161 nt.assert_equal(actual, expected)
161 nt.assert_equal(actual, expected)
162
162
163
164 def test_extract_hist_ranges_empty_str():
165 instr = ""
166 expected = [(0, 1, None)] # 0 == current session, None == to end
167 actual = list(extract_hist_ranges(instr))
168 nt.assert_equal(actual, expected)
169
170
163 def test_magic_rerun():
171 def test_magic_rerun():
164 """Simple test for %rerun (no args -> rerun last line)"""
172 """Simple test for %rerun (no args -> rerun last line)"""
165 ip = get_ipython()
173 ip = get_ipython()
@@ -255,18 +255,18 b' def test_find_assign_op_dedent():'
255
255
256 def test_check_complete():
256 def test_check_complete():
257 cc = ipt2.TransformerManager().check_complete
257 cc = ipt2.TransformerManager().check_complete
258 nt.assert_equal(cc("a = 1"), ('complete', None))
258 nt.assert_equal(cc("a = 1"), ("complete", None))
259 nt.assert_equal(cc("for a in range(5):"), ('incomplete', 4))
259 nt.assert_equal(cc("for a in range(5):"), ("incomplete", 4))
260 nt.assert_equal(cc("for a in range(5):\n if a > 0:"), ('incomplete', 8))
260 nt.assert_equal(cc("for a in range(5):\n if a > 0:"), ("incomplete", 8))
261 nt.assert_equal(cc("raise = 2"), ('invalid', None))
261 nt.assert_equal(cc("raise = 2"), ("invalid", None))
262 nt.assert_equal(cc("a = [1,\n2,"), ('incomplete', 0))
262 nt.assert_equal(cc("a = [1,\n2,"), ("incomplete", 0))
263 nt.assert_equal(cc(")"), ('incomplete', 0))
263 nt.assert_equal(cc("(\n))"), ("incomplete", 0))
264 nt.assert_equal(cc("\\\r\n"), ('incomplete', 0))
264 nt.assert_equal(cc("\\\r\n"), ("incomplete", 0))
265 nt.assert_equal(cc("a = '''\n hi"), ('incomplete', 3))
265 nt.assert_equal(cc("a = '''\n hi"), ("incomplete", 3))
266 nt.assert_equal(cc("def a():\n x=1\n global x"), ('invalid', None))
266 nt.assert_equal(cc("def a():\n x=1\n global x"), ("invalid", None))
267 nt.assert_equal(cc("a \\ "), ('invalid', None)) # Nothing allowed after backslash
267 nt.assert_equal(cc("a \\ "), ("invalid", None)) # Nothing allowed after backslash
268 nt.assert_equal(cc("1\\\n+2"), ('complete', None))
268 nt.assert_equal(cc("1\\\n+2"), ("complete", None))
269 nt.assert_equal(cc("exit"), ('complete', None))
269 nt.assert_equal(cc("exit"), ("complete", None))
270
270
271 example = dedent("""
271 example = dedent("""
272 if True:
272 if True:
@@ -297,6 +297,24 b' def test_check_complete_II():'
297 nt.assert_equal(cc('''def foo():\n """'''), ('incomplete', 4))
297 nt.assert_equal(cc('''def foo():\n """'''), ('incomplete', 4))
298
298
299
299
300 def test_check_complete_invalidates_sunken_brackets():
301 """
302 Test that a single line with more closing brackets than the opening ones is
303 interpretted as invalid
304 """
305 cc = ipt2.TransformerManager().check_complete
306 nt.assert_equal(cc(")"), ("invalid", None))
307 nt.assert_equal(cc("]"), ("invalid", None))
308 nt.assert_equal(cc("}"), ("invalid", None))
309 nt.assert_equal(cc(")("), ("invalid", None))
310 nt.assert_equal(cc("]["), ("invalid", None))
311 nt.assert_equal(cc("}{"), ("invalid", None))
312 nt.assert_equal(cc("]()("), ("invalid", None))
313 nt.assert_equal(cc("())("), ("invalid", None))
314 nt.assert_equal(cc(")[]("), ("invalid", None))
315 nt.assert_equal(cc("()]("), ("invalid", None))
316
317
300 def test_null_cleanup_transformer():
318 def test_null_cleanup_transformer():
301 manager = ipt2.TransformerManager()
319 manager = ipt2.TransformerManager()
302 manager.cleanup_transforms.insert(0, null_cleanup_transformer)
320 manager.cleanup_transforms.insert(0, null_cleanup_transformer)
@@ -3,7 +3,7 b''
3 Line-based transformers are the simpler ones; token-based transformers are
3 Line-based transformers are the simpler ones; token-based transformers are
4 more complex. See test_inputtransformer2 for tests for token-based transformers.
4 more complex. See test_inputtransformer2 for tests for token-based transformers.
5 """
5 """
6 import nose.tools as nt
6 import pytest
7
7
8 from IPython.core import inputtransformer2 as ipt2
8 from IPython.core import inputtransformer2 as ipt2
9
9
@@ -17,8 +17,9 b" get_ipython().run_cell_magic('foo', 'arg', 'body 1\\\\nbody 2\\\\n')"
17
17
18 def test_cell_magic():
18 def test_cell_magic():
19 for sample, expected in [CELL_MAGIC]:
19 for sample, expected in [CELL_MAGIC]:
20 nt.assert_equal(ipt2.cell_magic(sample.splitlines(keepends=True)),
20 assert ipt2.cell_magic(sample.splitlines(keepends=True)) == expected.splitlines(
21 expected.splitlines(keepends=True))
21 keepends=True
22 )
22
23
23 CLASSIC_PROMPT = ("""\
24 CLASSIC_PROMPT = ("""\
24 >>> for a in range(5):
25 >>> for a in range(5):
@@ -40,8 +41,9 b' for a in range(5):'
40
41
41 def test_classic_prompt():
42 def test_classic_prompt():
42 for sample, expected in [CLASSIC_PROMPT, CLASSIC_PROMPT_L2]:
43 for sample, expected in [CLASSIC_PROMPT, CLASSIC_PROMPT_L2]:
43 nt.assert_equal(ipt2.classic_prompt(sample.splitlines(keepends=True)),
44 assert ipt2.classic_prompt(
44 expected.splitlines(keepends=True))
45 sample.splitlines(keepends=True)
46 ) == expected.splitlines(keepends=True)
45
47
46 IPYTHON_PROMPT = ("""\
48 IPYTHON_PROMPT = ("""\
47 In [1]: for a in range(5):
49 In [1]: for a in range(5):
@@ -61,10 +63,49 b' for a in range(5):'
61 print(a ** 2)
63 print(a ** 2)
62 """)
64 """)
63
65
66
67 IPYTHON_PROMPT_VI_INS = (
68 """\
69 [ins] In [11]: def a():
70 ...: 123
71 ...:
72 ...: 123
73 """,
74 """\
75 def a():
76 123
77
78 123
79 """,
80 )
81
82 IPYTHON_PROMPT_VI_NAV = (
83 """\
84 [nav] In [11]: def a():
85 ...: 123
86 ...:
87 ...: 123
88 """,
89 """\
90 def a():
91 123
92
93 123
94 """,
95 )
96
97
64 def test_ipython_prompt():
98 def test_ipython_prompt():
65 for sample, expected in [IPYTHON_PROMPT, IPYTHON_PROMPT_L2]:
99 for sample, expected in [
66 nt.assert_equal(ipt2.ipython_prompt(sample.splitlines(keepends=True)),
100 IPYTHON_PROMPT,
67 expected.splitlines(keepends=True))
101 IPYTHON_PROMPT_L2,
102 IPYTHON_PROMPT_VI_INS,
103 IPYTHON_PROMPT_VI_NAV,
104 ]:
105 assert ipt2.ipython_prompt(
106 sample.splitlines(keepends=True)
107 ) == expected.splitlines(keepends=True)
108
68
109
69 INDENT_SPACES = ("""\
110 INDENT_SPACES = ("""\
70 if True:
111 if True:
@@ -84,8 +125,9 b' if True:'
84
125
85 def test_leading_indent():
126 def test_leading_indent():
86 for sample, expected in [INDENT_SPACES, INDENT_TABS]:
127 for sample, expected in [INDENT_SPACES, INDENT_TABS]:
87 nt.assert_equal(ipt2.leading_indent(sample.splitlines(keepends=True)),
128 assert ipt2.leading_indent(
88 expected.splitlines(keepends=True))
129 sample.splitlines(keepends=True)
130 ) == expected.splitlines(keepends=True)
89
131
90 LEADING_EMPTY_LINES = ("""\
132 LEADING_EMPTY_LINES = ("""\
91 \t
133 \t
@@ -111,9 +153,9 b' ONLY_EMPTY_LINES = ("""\\'
111
153
112 def test_leading_empty_lines():
154 def test_leading_empty_lines():
113 for sample, expected in [LEADING_EMPTY_LINES, ONLY_EMPTY_LINES]:
155 for sample, expected in [LEADING_EMPTY_LINES, ONLY_EMPTY_LINES]:
114 nt.assert_equal(
156 assert ipt2.leading_empty_lines(
115 ipt2.leading_empty_lines(sample.splitlines(keepends=True)),
157 sample.splitlines(keepends=True)
116 expected.splitlines(keepends=True))
158 ) == expected.splitlines(keepends=True)
117
159
118 CRLF_MAGIC = ([
160 CRLF_MAGIC = ([
119 "%%ls\r\n"
161 "%%ls\r\n"
@@ -123,4 +165,4 b' CRLF_MAGIC = (['
123
165
124 def test_crlf_magic():
166 def test_crlf_magic():
125 for sample, expected in [CRLF_MAGIC]:
167 for sample, expected in [CRLF_MAGIC]:
126 nt.assert_equal(ipt2.cell_magic(sample), expected) No newline at end of file
168 assert ipt2.cell_magic(sample) == expected
@@ -449,6 +449,25 b' class InteractiveShellTestCase(unittest.TestCase):'
449 # Reset the custom exception hook
449 # Reset the custom exception hook
450 ip.set_custom_exc((), None)
450 ip.set_custom_exc((), None)
451
451
452 @mock.patch("builtins.print")
453 def test_showtraceback_with_surrogates(self, mocked_print):
454 values = []
455
456 def mock_print_func(value, sep=" ", end="\n", file=sys.stdout, flush=False):
457 values.append(value)
458 if value == chr(0xD8FF):
459 raise UnicodeEncodeError("utf-8", chr(0xD8FF), 0, 1, "")
460
461 # mock builtins.print
462 mocked_print.side_effect = mock_print_func
463
464 # ip._showtraceback() is replaced in globalipapp.py.
465 # Call original method to test.
466 interactiveshell.InteractiveShell._showtraceback(ip, None, None, chr(0xD8FF))
467
468 self.assertEqual(mocked_print.call_count, 2)
469 self.assertEqual(values, [chr(0xD8FF), "\\ud8ff"])
470
452 def test_mktempfile(self):
471 def test_mktempfile(self):
453 filename = ip.mktempfile()
472 filename = ip.mktempfile()
454 # Check that we can open the file again on Windows
473 # Check that we can open the file again on Windows
@@ -581,9 +600,16 b' class TestSystemRaw(ExitCodeChecks):'
581 try:
600 try:
582 self.system("sleep 1 # wont happen")
601 self.system("sleep 1 # wont happen")
583 except KeyboardInterrupt:
602 except KeyboardInterrupt:
584 self.fail("system call should intercept "
603 self.fail(
585 "keyboard interrupt from subprocess.call")
604 "system call should intercept "
586 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGINT)
605 "keyboard interrupt from subprocess.call"
606 )
607 self.assertEqual(ip.user_ns["_exit_code"], -signal.SIGINT)
608
609 def test_magic_warnings(self):
610 for magic_cmd in ("ls", "pip", "conda", "cd"):
611 with self.assertWarnsRegex(Warning, "You executed the system command"):
612 ip.system_raw(magic_cmd)
587
613
588 # TODO: Exit codes are currently ignored on Windows.
614 # TODO: Exit codes are currently ignored on Windows.
589 class TestSystemPipedExitCode(ExitCodeChecks):
615 class TestSystemPipedExitCode(ExitCodeChecks):
@@ -5,7 +5,7 b''
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6
6
7 # third party
7 # third party
8 import nose.tools as nt
8 import pytest
9
9
10 # our own packages
10 # our own packages
11
11
@@ -31,8 +31,8 b' def test_reset():'
31
31
32 # Finally, check that all namespaces have only as many variables as we
32 # Finally, check that all namespaces have only as many variables as we
33 # expect to find in them:
33 # expect to find in them:
34 nt.assert_equal(len(ip.user_ns), nvars_user_ns)
34 assert len(ip.user_ns) == nvars_user_ns
35 nt.assert_equal(len(ip.user_ns_hidden), nvars_hidden)
35 assert len(ip.user_ns_hidden) == nvars_hidden
36
36
37
37
38 # Tests for reporting of exceptions in various modes, handling of SystemExit,
38 # Tests for reporting of exceptions in various modes, handling of SystemExit,
@@ -117,93 +117,103 b' ZeroDivisionError Traceback (most recent call last)'
117 ZeroDivisionError: ...
117 ZeroDivisionError: ...
118 """
118 """
119
119
120 def doctest_tb_sysexit():
121 """
122 In [17]: %xmode plain
123 Exception reporting mode: Plain
124
125 In [18]: %run simpleerr.py exit
126 An exception has occurred, use %tb to see the full traceback.
127 SystemExit: (1, 'Mode = exit')
128
129 In [19]: %run simpleerr.py exit 2
130 An exception has occurred, use %tb to see the full traceback.
131 SystemExit: (2, 'Mode = exit')
132
133 In [20]: %tb
134 Traceback (most recent call last):
135 File ... in <module>
136 bar(mode)
137 File ... line 22, in bar
138 sysexit(stat, mode)
139 File ... line 11, in sysexit
140 raise SystemExit(stat, 'Mode = %s' % mode)
141 SystemExit: (2, 'Mode = exit')
142
143 In [21]: %xmode context
144 Exception reporting mode: Context
145
146 In [22]: %tb
147 ---------------------------------------------------------------------------
148 SystemExit Traceback (most recent call last)
149 <BLANKLINE>
150 ...<module>
151 29 except IndexError:
152 30 mode = 'div'
153 ---> 32 bar(mode)
154 <BLANKLINE>
155 ...bar(mode)
156 20 except:
157 21 stat = 1
158 ---> 22 sysexit(stat, mode)
159 23 else:
160 24 raise ValueError('Unknown mode')
161 <BLANKLINE>
162 ...sysexit(stat, mode)
163 10 def sysexit(stat, mode):
164 ---> 11 raise SystemExit(stat, 'Mode = %s' % mode)
165 <BLANKLINE>
166 SystemExit: (2, 'Mode = exit')
167
168 In [23]: %xmode verbose
169 Exception reporting mode: Verbose
170
120
171 In [24]: %tb
121 # TODO : Marc 2021 – this seem to fail due
172 ---------------------------------------------------------------------------
122 # to upstream changes in CI for whatever reason.
173 SystemExit Traceback (most recent call last)
123 # Commenting for now, to revive someday (maybe?)
174 <BLANKLINE>
124 # nose won't work in 3.10 anyway and we'll have to disable iptest.
175 ... in <module>
125 # thus this likely need to bemigrated to pytest.
176 29 except IndexError:
126
177 30 mode = 'div'
127
178 ---> 32 bar(mode)
128 # def doctest_tb_sysexit():
179 mode = 'exit'
129 # """
180 <BLANKLINE>
130 # In [17]: %xmode plain
181 ... in bar(mode='exit')
131 # Exception reporting mode: Plain
182 20 except:
132 #
183 21 stat = 1
133 # In [18]: %run simpleerr.py exit
184 ---> 22 sysexit(stat, mode)
134 # An exception has occurred, use %tb to see the full traceback.
185 mode = 'exit'
135 # SystemExit: (1, 'Mode = exit')
186 stat = 2
136 #
187 23 else:
137 # In [19]: %run simpleerr.py exit 2
188 24 raise ValueError('Unknown mode')
138 # An exception has occurred, use %tb to see the full traceback.
189 <BLANKLINE>
139 # SystemExit: (2, 'Mode = exit')
190 ... in sysexit(stat=2, mode='exit')
140 #
191 10 def sysexit(stat, mode):
141 # In [20]: %tb
192 ---> 11 raise SystemExit(stat, 'Mode = %s' % mode)
142 # Traceback (most recent call last):
193 stat = 2
143 # File ... in <module>
194 mode = 'exit'
144 # bar(mode)
195 <BLANKLINE>
145 # File ... line 22, in bar
196 SystemExit: (2, 'Mode = exit')
146 # sysexit(stat, mode)
197 """
147 # File ... line 11, in sysexit
148 # raise SystemExit(stat, 'Mode = %s' % mode)
149 # SystemExit: (2, 'Mode = exit')
150 #
151 # In [21]: %xmode context
152 # Exception reporting mode: Context
153 #
154 # In [22]: %tb
155 # ---------------------------------------------------------------------------
156 # SystemExit Traceback (most recent call last)
157 # <BLANKLINE>
158 # ...<module>
159 # 29 except IndexError:
160 # 30 mode = 'div'
161 # ---> 32 bar(mode)
162 # <BLANKLINE>
163 # ...bar(mode)
164 # 20 except:
165 # 21 stat = 1
166 # ---> 22 sysexit(stat, mode)
167 # 23 else:
168 # 24 raise ValueError('Unknown mode')
169 # <BLANKLINE>
170 # ...sysexit(stat, mode)
171 # 10 def sysexit(stat, mode):
172 # ---> 11 raise SystemExit(stat, 'Mode = %s' % mode)
173 # <BLANKLINE>
174 # SystemExit: (2, 'Mode = exit')
175 #
176 # In [23]: %xmode verbose
177 # Exception reporting mode: Verbose
178 #
179 # In [24]: %tb
180 # ---------------------------------------------------------------------------
181 # SystemExit Traceback (most recent call last)
182 # <BLANKLINE>
183 # ... in <module>
184 # 29 except IndexError:
185 # 30 mode = 'div'
186 # ---> 32 bar(mode)
187 # mode = 'exit'
188 # <BLANKLINE>
189 # ... in bar(mode='exit')
190 # 20 except:
191 # 21 stat = 1
192 # ---> 22 sysexit(stat, mode)
193 # mode = 'exit'
194 # stat = 2
195 # 23 else:
196 # 24 raise ValueError('Unknown mode')
197 # <BLANKLINE>
198 # ... in sysexit(stat=2, mode='exit')
199 # 10 def sysexit(stat, mode):
200 # ---> 11 raise SystemExit(stat, 'Mode = %s' % mode)
201 # stat = 2
202 # mode = 'exit'
203 # <BLANKLINE>
204 # SystemExit: (2, 'Mode = exit')
205 # """
198
206
199
207
200 def test_run_cell():
208 def test_run_cell():
201 import textwrap
209 import textwrap
202 ip.run_cell('a = 10\na+=1')
203 ip.run_cell('assert a == 11\nassert 1')
204
210
205 nt.assert_equal(ip.user_ns['a'], 11)
211 ip.run_cell("a = 10\na+=1")
206 complex = textwrap.dedent("""
212 ip.run_cell("assert a == 11\nassert 1")
213
214 assert ip.user_ns["a"] == 11
215 complex = textwrap.dedent(
216 """
207 if 1:
217 if 1:
208 print "hello"
218 print "hello"
209 if 1:
219 if 1:
@@ -225,7 +235,7 b' def test_run_cell():'
225
235
226 def test_db():
236 def test_db():
227 """Test the internal database used for variable persistence."""
237 """Test the internal database used for variable persistence."""
228 ip.db['__unittest_'] = 12
238 ip.db["__unittest_"] = 12
229 nt.assert_equal(ip.db['__unittest_'], 12)
239 assert ip.db["__unittest_"] == 12
230 del ip.db['__unittest_']
240 del ip.db["__unittest_"]
231 assert '__unittest_' not in ip.db
241 assert "__unittest_" not in ip.db
@@ -2,8 +2,8 b''
2 """Test IPython.core.logger"""
2 """Test IPython.core.logger"""
3
3
4 import os.path
4 import os.path
5 import pytest
5
6
6 import nose.tools as nt
7 from IPython.utils.tempdir import TemporaryDirectory
7 from IPython.utils.tempdir import TemporaryDirectory
8
8
9 def test_logstart_inaccessible_file():
9 def test_logstart_inaccessible_file():
@@ -12,7 +12,7 b' def test_logstart_inaccessible_file():'
12 except IOError:
12 except IOError:
13 pass
13 pass
14 else:
14 else:
15 nt.assert_true(False) # The try block should never pass.
15 assert False # The try block should never pass.
16
16
17 try:
17 try:
18 _ip.run_cell("a=1") # Check it doesn't try to log this
18 _ip.run_cell("a=1") # Check it doesn't try to log this
@@ -472,6 +472,23 b' def test_parse_options():'
472 nt.assert_equal(m.parse_options(u'foo', '')[1], u'foo')
472 nt.assert_equal(m.parse_options(u'foo', '')[1], u'foo')
473
473
474
474
475 def test_parse_options_preserve_non_option_string():
476 """Test to assert preservation of non-option part of magic-block, while parsing magic options."""
477 m = DummyMagics(_ip)
478 opts, stmt = m.parse_options(
479 " -n1 -r 13 _ = 314 + foo", "n:r:", preserve_non_opts=True
480 )
481 nt.assert_equal(opts, {"n": "1", "r": "13"})
482 nt.assert_equal(stmt, "_ = 314 + foo")
483
484
485 def test_run_magic_preserve_code_block():
486 """Test to assert preservation of non-option part of magic-block, while running magic."""
487 _ip.user_ns["spaces"] = []
488 _ip.magic("timeit -n1 -r1 spaces.append([s.count(' ') for s in ['document']])")
489 assert _ip.user_ns["spaces"] == [[0]]
490
491
475 def test_dirops():
492 def test_dirops():
476 """Test various directory handling operations."""
493 """Test various directory handling operations."""
477 # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/')
494 # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/')
@@ -1072,6 +1089,29 b' def test_save():'
1072 nt.assert_in("coding: utf-8", content)
1089 nt.assert_in("coding: utf-8", content)
1073
1090
1074
1091
1092 def test_save_with_no_args():
1093 ip = get_ipython()
1094 ip.history_manager.reset() # Clear any existing history.
1095 cmds = [u"a=1", u"def b():\n return a**2", u"print(a, b())", "%save"]
1096 for i, cmd in enumerate(cmds, start=1):
1097 ip.history_manager.store_inputs(i, cmd)
1098
1099 with TemporaryDirectory() as tmpdir:
1100 path = os.path.join(tmpdir, "testsave.py")
1101 ip.run_line_magic("save", path)
1102 content = Path(path).read_text()
1103 expected_content = dedent(
1104 """\
1105 # coding: utf-8
1106 a=1
1107 def b():
1108 return a**2
1109 print(a, b())
1110 """
1111 )
1112 nt.assert_equal(content, expected_content)
1113
1114
1075 def test_store():
1115 def test_store():
1076 """Test %store."""
1116 """Test %store."""
1077 ip = get_ipython()
1117 ip = get_ipython()
@@ -7,7 +7,7 b''
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8
8
9 import argparse
9 import argparse
10 from nose.tools import assert_equal
10 import pytest
11
11
12 from IPython.core.magic_arguments import (argument, argument_group, kwds,
12 from IPython.core.magic_arguments import (argument, argument_group, kwds,
13 magic_arguments, parse_argstring, real_name)
13 magic_arguments, parse_argstring, real_name)
@@ -74,45 +74,62 b' def foo(self, args):'
74
74
75
75
76 def test_magic_arguments():
76 def test_magic_arguments():
77 assert_equal(magic_foo1.__doc__, '::\n\n %foo1 [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
77 assert (
78 assert_equal(getattr(magic_foo1, 'argcmd_name', None), None)
78 magic_foo1.__doc__
79 assert_equal(real_name(magic_foo1), 'foo1')
79 == "::\n\n %foo1 [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n"
80 assert_equal(magic_foo1(None, ''), argparse.Namespace(foo=None))
80 )
81 assert hasattr(magic_foo1, 'has_arguments')
81 assert getattr(magic_foo1, "argcmd_name", None) == None
82
82 assert real_name(magic_foo1) == "foo1"
83 assert_equal(magic_foo2.__doc__, '::\n\n %foo2\n\n A docstring.\n')
83 assert magic_foo1(None, "") == argparse.Namespace(foo=None)
84 assert_equal(getattr(magic_foo2, 'argcmd_name', None), None)
84 assert hasattr(magic_foo1, "has_arguments")
85 assert_equal(real_name(magic_foo2), 'foo2')
85
86 assert_equal(magic_foo2(None, ''), argparse.Namespace())
86 assert magic_foo2.__doc__ == "::\n\n %foo2\n\n A docstring.\n"
87 assert hasattr(magic_foo2, 'has_arguments')
87 assert getattr(magic_foo2, "argcmd_name", None) == None
88
88 assert real_name(magic_foo2) == "foo2"
89 assert_equal(magic_foo3.__doc__, '::\n\n %foo3 [-f FOO] [-b BAR] [-z BAZ]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n\nGroup:\n -b BAR, --bar BAR a grouped argument\n\nSecond Group:\n -z BAZ, --baz BAZ another grouped argument\n')
89 assert magic_foo2(None, "") == argparse.Namespace()
90 assert_equal(getattr(magic_foo3, 'argcmd_name', None), None)
90 assert hasattr(magic_foo2, "has_arguments")
91 assert_equal(real_name(magic_foo3), 'foo3')
91
92 assert_equal(magic_foo3(None, ''),
92 assert (
93 argparse.Namespace(bar=None, baz=None, foo=None))
93 magic_foo3.__doc__
94 assert hasattr(magic_foo3, 'has_arguments')
94 == "::\n\n %foo3 [-f FOO] [-b BAR] [-z BAZ]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n\nGroup:\n -b BAR, --bar BAR a grouped argument\n\nSecond Group:\n -z BAZ, --baz BAZ another grouped argument\n"
95
95 )
96 assert_equal(magic_foo4.__doc__, '::\n\n %foo4 [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
96 assert getattr(magic_foo3, "argcmd_name", None) == None
97 assert_equal(getattr(magic_foo4, 'argcmd_name', None), None)
97 assert real_name(magic_foo3) == "foo3"
98 assert_equal(real_name(magic_foo4), 'foo4')
98 assert magic_foo3(None, "") == argparse.Namespace(bar=None, baz=None, foo=None)
99 assert_equal(magic_foo4(None, ''), argparse.Namespace())
99 assert hasattr(magic_foo3, "has_arguments")
100 assert hasattr(magic_foo4, 'has_arguments')
100
101
101 assert (
102 assert_equal(magic_foo5.__doc__, '::\n\n %frobnicate [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
102 magic_foo4.__doc__
103 assert_equal(getattr(magic_foo5, 'argcmd_name', None), 'frobnicate')
103 == "::\n\n %foo4 [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n"
104 assert_equal(real_name(magic_foo5), 'frobnicate')
104 )
105 assert_equal(magic_foo5(None, ''), argparse.Namespace(foo=None))
105 assert getattr(magic_foo4, "argcmd_name", None) == None
106 assert hasattr(magic_foo5, 'has_arguments')
106 assert real_name(magic_foo4) == "foo4"
107
107 assert magic_foo4(None, "") == argparse.Namespace()
108 assert_equal(magic_magic_foo.__doc__, '::\n\n %magic_foo [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
108 assert hasattr(magic_foo4, "has_arguments")
109 assert_equal(getattr(magic_magic_foo, 'argcmd_name', None), None)
109
110 assert_equal(real_name(magic_magic_foo), 'magic_foo')
110 assert (
111 assert_equal(magic_magic_foo(None, ''), argparse.Namespace(foo=None))
111 magic_foo5.__doc__
112 assert hasattr(magic_magic_foo, 'has_arguments')
112 == "::\n\n %frobnicate [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n"
113
113 )
114 assert_equal(foo.__doc__, '::\n\n %foo [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
114 assert getattr(magic_foo5, "argcmd_name", None) == "frobnicate"
115 assert_equal(getattr(foo, 'argcmd_name', None), None)
115 assert real_name(magic_foo5) == "frobnicate"
116 assert_equal(real_name(foo), 'foo')
116 assert magic_foo5(None, "") == argparse.Namespace(foo=None)
117 assert_equal(foo(None, ''), argparse.Namespace(foo=None))
117 assert hasattr(magic_foo5, "has_arguments")
118 assert hasattr(foo, 'has_arguments')
118
119 assert (
120 magic_magic_foo.__doc__
121 == "::\n\n %magic_foo [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n"
122 )
123 assert getattr(magic_magic_foo, "argcmd_name", None) == None
124 assert real_name(magic_magic_foo) == "magic_foo"
125 assert magic_magic_foo(None, "") == argparse.Namespace(foo=None)
126 assert hasattr(magic_magic_foo, "has_arguments")
127
128 assert (
129 foo.__doc__
130 == "::\n\n %foo [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n"
131 )
132 assert getattr(foo, "argcmd_name", None) == None
133 assert real_name(foo) == "foo"
134 assert foo(None, "") == argparse.Namespace(foo=None)
135 assert hasattr(foo, "has_arguments")
@@ -56,7 +56,7 b' def pyfile(fname):'
56
56
57
57
58 def match_pyfiles(f1, f2):
58 def match_pyfiles(f1, f2):
59 nt.assert_equal(pyfile(f1), pyfile(f2))
59 assert pyfile(f1) == pyfile(f2)
60
60
61
61
62 def test_find_file():
62 def test_find_file():
@@ -76,7 +76,7 b' def test_find_file_decorated1():'
76 "My docstring"
76 "My docstring"
77
77
78 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
78 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
79 nt.assert_equal(f.__doc__, "My docstring")
79 assert f.__doc__ == "My docstring"
80
80
81
81
82 def test_find_file_decorated2():
82 def test_find_file_decorated2():
@@ -92,7 +92,7 b' def test_find_file_decorated2():'
92 "My docstring 2"
92 "My docstring 2"
93
93
94 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
94 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
95 nt.assert_equal(f.__doc__, "My docstring 2")
95 assert f.__doc__ == "My docstring 2"
96
96
97
97
98 def test_find_file_magic():
98 def test_find_file_magic():
@@ -167,41 +167,46 b' class SerialLiar(object):'
167
167
168 def test_info():
168 def test_info():
169 "Check that Inspector.info fills out various fields as expected."
169 "Check that Inspector.info fills out various fields as expected."
170 i = inspector.info(Call, oname='Call')
170 i = inspector.info(Call, oname="Call")
171 nt.assert_equal(i['type_name'], 'type')
171 assert i["type_name"] == "type"
172 expted_class = str(type(type)) # <class 'type'> (Python 3) or <type 'type'>
172 expted_class = str(type(type)) # <class 'type'> (Python 3) or <type 'type'>
173 nt.assert_equal(i['base_class'], expted_class)
173 assert i["base_class"] == expted_class
174 nt.assert_regex(i['string_form'], "<class 'IPython.core.tests.test_oinspect.Call'( at 0x[0-9a-f]{1,9})?>")
174 nt.assert_regex(
175 i["string_form"],
176 "<class 'IPython.core.tests.test_oinspect.Call'( at 0x[0-9a-f]{1,9})?>",
177 )
175 fname = __file__
178 fname = __file__
176 if fname.endswith(".pyc"):
179 if fname.endswith(".pyc"):
177 fname = fname[:-1]
180 fname = fname[:-1]
178 # case-insensitive comparison needed on some filesystems
181 # case-insensitive comparison needed on some filesystems
179 # e.g. Windows:
182 # e.g. Windows:
180 nt.assert_equal(i['file'].lower(), compress_user(fname).lower())
183 assert i["file"].lower() == compress_user(fname).lower()
181 nt.assert_equal(i['definition'], None)
184 assert i["definition"] == None
182 nt.assert_equal(i['docstring'], Call.__doc__)
185 assert i["docstring"] == Call.__doc__
183 nt.assert_equal(i['source'], None)
186 assert i["source"] == None
184 nt.assert_true(i['isclass'])
187 nt.assert_true(i["isclass"])
185 nt.assert_equal(i['init_definition'], "Call(x, y=1)")
188 assert i["init_definition"] == "Call(x, y=1)"
186 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
189 assert i["init_docstring"] == Call.__init__.__doc__
187
190
188 i = inspector.info(Call, detail_level=1)
191 i = inspector.info(Call, detail_level=1)
189 nt.assert_not_equal(i['source'], None)
192 nt.assert_not_equal(i["source"], None)
190 nt.assert_equal(i['docstring'], None)
193 assert i["docstring"] == None
191
194
192 c = Call(1)
195 c = Call(1)
193 c.__doc__ = "Modified instance docstring"
196 c.__doc__ = "Modified instance docstring"
194 i = inspector.info(c)
197 i = inspector.info(c)
195 nt.assert_equal(i['type_name'], 'Call')
198 assert i["type_name"] == "Call"
196 nt.assert_equal(i['docstring'], "Modified instance docstring")
199 assert i["docstring"] == "Modified instance docstring"
197 nt.assert_equal(i['class_docstring'], Call.__doc__)
200 assert i["class_docstring"] == Call.__doc__
198 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
201 assert i["init_docstring"] == Call.__init__.__doc__
199 nt.assert_equal(i['call_docstring'], Call.__call__.__doc__)
202 assert i["call_docstring"] == Call.__call__.__doc__
203
200
204
201 def test_class_signature():
205 def test_class_signature():
202 info = inspector.info(HasSignature, 'HasSignature')
206 info = inspector.info(HasSignature, "HasSignature")
203 nt.assert_equal(info['init_definition'], "HasSignature(test)")
207 assert info["init_definition"] == "HasSignature(test)"
204 nt.assert_equal(info['init_docstring'], HasSignature.__init__.__doc__)
208 assert info["init_docstring"] == HasSignature.__init__.__doc__
209
205
210
206 def test_info_awkward():
211 def test_info_awkward():
207 # Just test that this doesn't throw an error.
212 # Just test that this doesn't throw an error.
@@ -231,8 +236,9 b' def f_kwarg(pos, *, kwonly):'
231 pass
236 pass
232
237
233 def test_definition_kwonlyargs():
238 def test_definition_kwonlyargs():
234 i = inspector.info(f_kwarg, oname='f_kwarg') # analysis:ignore
239 i = inspector.info(f_kwarg, oname="f_kwarg") # analysis:ignore
235 nt.assert_equal(i['definition'], "f_kwarg(pos, *, kwonly)")
240 assert i["definition"] == "f_kwarg(pos, *, kwonly)"
241
236
242
237 def test_getdoc():
243 def test_getdoc():
238 class A(object):
244 class A(object):
@@ -253,9 +259,9 b' def test_getdoc():'
253 b = B()
259 b = B()
254 c = C()
260 c = C()
255
261
256 nt.assert_equal(oinspect.getdoc(a), "standard docstring")
262 assert oinspect.getdoc(a) == "standard docstring"
257 nt.assert_equal(oinspect.getdoc(b), "custom docstring")
263 assert oinspect.getdoc(b) == "custom docstring"
258 nt.assert_equal(oinspect.getdoc(c), "standard docstring")
264 assert oinspect.getdoc(c) == "standard docstring"
259
265
260
266
261 def test_empty_property_has_no_source():
267 def test_empty_property_has_no_source():
@@ -299,15 +305,17 b' def test_property_docstring_is_in_info_for_detail_level_0():'
299 """This is `foobar` property."""
305 """This is `foobar` property."""
300 pass
306 pass
301
307
302 ip.user_ns['a_obj'] = A()
308 ip.user_ns["a_obj"] = A()
303 nt.assert_equal(
309 assert (
304 'This is `foobar` property.',
310 "This is `foobar` property."
305 ip.object_inspect('a_obj.foobar', detail_level=0)['docstring'])
311 == ip.object_inspect("a_obj.foobar", detail_level=0)["docstring"]
312 )
306
313
307 ip.user_ns['a_cls'] = A
314 ip.user_ns["a_cls"] = A
308 nt.assert_equal(
315 assert (
309 'This is `foobar` property.',
316 "This is `foobar` property."
310 ip.object_inspect('a_cls.foobar', detail_level=0)['docstring'])
317 == ip.object_inspect("a_cls.foobar", detail_level=0)["docstring"]
318 )
311
319
312
320
313 def test_pdef():
321 def test_pdef():
@@ -404,7 +412,7 b' def test_render_signature_short():'
404 signature(short_fun),
412 signature(short_fun),
405 short_fun.__name__,
413 short_fun.__name__,
406 )
414 )
407 nt.assert_equal(sig, 'short_fun(a=1)')
415 assert sig == "short_fun(a=1)"
408
416
409
417
410 def test_render_signature_long():
418 def test_render_signature_long():
@@ -3,7 +3,7 b''
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Imports
4 # Imports
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 import nose.tools as nt
6 import pytest
7
7
8 from IPython.core.prefilter import AutocallChecker
8 from IPython.core.prefilter import AutocallChecker
9
9
@@ -19,7 +19,7 b' def test_prefilter():'
19 ]
19 ]
20
20
21 for raw, correct in pairs:
21 for raw, correct in pairs:
22 nt.assert_equal(ip.prefilter(raw), correct)
22 assert ip.prefilter(raw) == correct
23
23
24 def test_prefilter_shadowed():
24 def test_prefilter_shadowed():
25 def dummy_magic(line): pass
25 def dummy_magic(line): pass
@@ -32,16 +32,16 b' def test_prefilter_shadowed():'
32 # These should not be transformed - they are shadowed by other names
32 # These should not be transformed - they are shadowed by other names
33 for name in ['if', 'zip', 'get_ipython']: # keyword, builtin, global
33 for name in ['if', 'zip', 'get_ipython']: # keyword, builtin, global
34 ip.register_magic_function(dummy_magic, magic_name=name)
34 ip.register_magic_function(dummy_magic, magic_name=name)
35 res = ip.prefilter(name+' foo')
35 res = ip.prefilter(name + " foo")
36 nt.assert_equal(res, name+' foo')
36 assert res == name + " foo"
37 del ip.magics_manager.magics['line'][name]
37 del ip.magics_manager.magics["line"][name]
38
38
39 # These should be transformed
39 # These should be transformed
40 for name in ['fi', 'piz', 'nohtypi_teg']:
40 for name in ['fi', 'piz', 'nohtypi_teg']:
41 ip.register_magic_function(dummy_magic, magic_name=name)
41 ip.register_magic_function(dummy_magic, magic_name=name)
42 res = ip.prefilter(name+' foo')
42 res = ip.prefilter(name + " foo")
43 nt.assert_not_equal(res, name+' foo')
43 assert res != name + " foo"
44 del ip.magics_manager.magics['line'][name]
44 del ip.magics_manager.magics["line"][name]
45
45
46 finally:
46 finally:
47 ip.automagic = prev_automagic_state
47 ip.automagic = prev_automagic_state
@@ -52,9 +52,9 b' def test_autocall_binops():'
52 f = lambda x: x
52 f = lambda x: x
53 ip.user_ns['f'] = f
53 ip.user_ns['f'] = f
54 try:
54 try:
55 nt.assert_equal(ip.prefilter('f 1'),'f(1)')
55 assert ip.prefilter("f 1") == "f(1)"
56 for t in ['f +1', 'f -1']:
56 for t in ["f +1", "f -1"]:
57 nt.assert_equal(ip.prefilter(t), t)
57 assert ip.prefilter(t) == t
58
58
59 # Run tests again with a more permissive exclude_regexp, which will
59 # Run tests again with a more permissive exclude_regexp, which will
60 # allow transformation of binary operations ('f -1' -> 'f(-1)').
60 # allow transformation of binary operations ('f -1' -> 'f(-1)').
@@ -66,8 +66,8 b' def test_autocall_binops():'
66 ac.exclude_regexp = r'^[,&^\|\*/]|^is |^not |^in |^and |^or '
66 ac.exclude_regexp = r'^[,&^\|\*/]|^is |^not |^in |^and |^or '
67 pm.sort_checkers()
67 pm.sort_checkers()
68
68
69 nt.assert_equal(ip.prefilter('f -1'), 'f(-1)')
69 assert ip.prefilter("f -1") == "f(-1)"
70 nt.assert_equal(ip.prefilter('f +1'), 'f(+1)')
70 assert ip.prefilter("f +1") == "f(+1)"
71 finally:
71 finally:
72 pm.unregister_checker(ac)
72 pm.unregister_checker(ac)
73 finally:
73 finally:
@@ -88,7 +88,7 b' def test_issue_114():'
88 try:
88 try:
89 for mgk in ip.magics_manager.lsmagic()['line']:
89 for mgk in ip.magics_manager.lsmagic()['line']:
90 raw = template % mgk
90 raw = template % mgk
91 nt.assert_equal(ip.prefilter(raw), raw)
91 assert ip.prefilter(raw) == raw
92 finally:
92 finally:
93 ip.prefilter_manager.multi_line_specials = msp
93 ip.prefilter_manager.multi_line_specials = msp
94
94
@@ -121,7 +121,7 b' def test_autocall_should_support_unicode():'
121 ip.magic('autocall 2')
121 ip.magic('autocall 2')
122 ip.user_ns['π'] = lambda x: x
122 ip.user_ns['π'] = lambda x: x
123 try:
123 try:
124 nt.assert_equal(ip.prefilter('π 3'),'π(3)')
124 assert ip.prefilter("π 3") == "π(3)"
125 finally:
125 finally:
126 ip.magic('autocall 0')
126 ip.magic('autocall 0')
127 del ip.user_ns['π']
127 del ip.user_ns['π']
@@ -15,6 +15,7 b' from nose import SkipTest'
15 import nose.tools as nt
15 import nose.tools as nt
16
16
17 from matplotlib import pyplot as plt
17 from matplotlib import pyplot as plt
18 import matplotlib_inline
18 import numpy as np
19 import numpy as np
19
20
20 from IPython.core.getipython import get_ipython
21 from IPython.core.getipython import get_ipython
@@ -146,8 +147,16 b' def test_import_pylab():'
146 nt.assert_true('plt' in ns)
147 nt.assert_true('plt' in ns)
147 nt.assert_equal(ns['np'], np)
148 nt.assert_equal(ns['np'], np)
148
149
150 from traitlets.config import Config
151
152
149 class TestPylabSwitch(object):
153 class TestPylabSwitch(object):
150 class Shell(InteractiveShell):
154 class Shell(InteractiveShell):
155 def init_history(self):
156 """Sets up the command history, and starts regular autosaves."""
157 self.config.HistoryManager.hist_file = ":memory:"
158 super().init_history()
159
151 def enable_gui(self, gui):
160 def enable_gui(self, gui):
152 pass
161 pass
153
162
@@ -167,18 +176,21 b' class TestPylabSwitch(object):'
167 pt.activate_matplotlib = act_mpl
176 pt.activate_matplotlib = act_mpl
168 self._save_ip = pt.import_pylab
177 self._save_ip = pt.import_pylab
169 pt.import_pylab = lambda *a,**kw:None
178 pt.import_pylab = lambda *a,**kw:None
170 self._save_cis = pt.configure_inline_support
179 self._save_cis = matplotlib_inline.backend_inline.configure_inline_support
171 pt.configure_inline_support = lambda *a,**kw:None
180 matplotlib_inline.backend_inline.configure_inline_support = (
181 lambda *a, **kw: None
182 )
172
183
173 def teardown(self):
184 def teardown(self):
174 pt.activate_matplotlib = self._save_am
185 pt.activate_matplotlib = self._save_am
175 pt.import_pylab = self._save_ip
186 pt.import_pylab = self._save_ip
176 pt.configure_inline_support = self._save_cis
187 matplotlib_inline.backend_inline.configure_inline_support = self._save_cis
177 import matplotlib
188 import matplotlib
178 matplotlib.rcParams = self._saved_rcParams
189 matplotlib.rcParams = self._saved_rcParams
179 matplotlib.rcParamsOrig = self._saved_rcParamsOrig
190 matplotlib.rcParamsOrig = self._saved_rcParamsOrig
180
191
181 def test_qt(self):
192 def test_qt(self):
193
182 s = self.Shell()
194 s = self.Shell()
183 gui, backend = s.enable_matplotlib(None)
195 gui, backend = s.enable_matplotlib(None)
184 nt.assert_equal(gui, 'qt')
196 nt.assert_equal(gui, 'qt')
@@ -169,6 +169,31 b' def _format_traceback_lines(lines, Colors, has_colors, lvals):'
169 return res
169 return res
170
170
171
171
172 def _format_filename(file, ColorFilename, ColorNormal):
173 """
174 Format filename lines with `In [n]` if it's the nth code cell or `File *.py` if it's a module.
175
176 Parameters
177 ----------
178 file : str
179 ColorFilename
180 ColorScheme's filename coloring to be used.
181 ColorNormal
182 ColorScheme's normal coloring to be used.
183 """
184 ipinst = get_ipython()
185
186 if ipinst is not None and file in ipinst.compile._filename_map:
187 file = "[%s]" % ipinst.compile._filename_map[file]
188 tpl_link = "In %s%%s%s" % (ColorFilename, ColorNormal)
189 else:
190 file = util_path.compress_user(
191 py3compat.cast_unicode(file, util_path.fs_encoding)
192 )
193 tpl_link = "File %s%%s%s" % (ColorFilename, ColorNormal)
194
195 return tpl_link % file
196
172 #---------------------------------------------------------------------------
197 #---------------------------------------------------------------------------
173 # Module classes
198 # Module classes
174 class TBTools(colorable.Colorable):
199 class TBTools(colorable.Colorable):
@@ -414,21 +439,31 b' class ListTB(TBTools):'
414 Colors = self.Colors
439 Colors = self.Colors
415 list = []
440 list = []
416 for filename, lineno, name, line in extracted_list[:-1]:
441 for filename, lineno, name, line in extracted_list[:-1]:
417 item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \
442 item = " %s, line %s%d%s, in %s%s%s\n" % (
418 (Colors.filename, filename, Colors.Normal,
443 _format_filename(filename, Colors.filename, Colors.Normal),
419 Colors.lineno, lineno, Colors.Normal,
444 Colors.lineno,
420 Colors.name, name, Colors.Normal)
445 lineno,
446 Colors.Normal,
447 Colors.name,
448 name,
449 Colors.Normal,
450 )
421 if line:
451 if line:
422 item += ' %s\n' % line.strip()
452 item += ' %s\n' % line.strip()
423 list.append(item)
453 list.append(item)
424 # Emphasize the last entry
454 # Emphasize the last entry
425 filename, lineno, name, line = extracted_list[-1]
455 filename, lineno, name, line = extracted_list[-1]
426 item = '%s File %s"%s"%s, line %s%d%s, in %s%s%s%s\n' % \
456 item = "%s %s, line %s%d%s, in %s%s%s%s\n" % (
427 (Colors.normalEm,
457 Colors.normalEm,
428 Colors.filenameEm, filename, Colors.normalEm,
458 _format_filename(filename, Colors.filenameEm, Colors.normalEm),
429 Colors.linenoEm, lineno, Colors.normalEm,
459 Colors.linenoEm,
430 Colors.nameEm, name, Colors.normalEm,
460 lineno,
431 Colors.Normal)
461 Colors.normalEm,
462 Colors.nameEm,
463 name,
464 Colors.normalEm,
465 Colors.Normal,
466 )
432 if line:
467 if line:
433 item += '%s %s%s\n' % (Colors.line, line.strip(),
468 item += '%s %s%s\n' % (Colors.line, line.strip(),
434 Colors.Normal)
469 Colors.Normal)
@@ -463,13 +498,21 b' class ListTB(TBTools):'
463 lineno = value.lineno
498 lineno = value.lineno
464 textline = linecache.getline(value.filename, value.lineno)
499 textline = linecache.getline(value.filename, value.lineno)
465 else:
500 else:
466 lineno = 'unknown'
501 lineno = "unknown"
467 textline = ''
502 textline = ""
468 list.append('%s File %s"%s"%s, line %s%s%s\n' % \
503 list.append(
469 (Colors.normalEm,
504 "%s %s, line %s%s%s\n"
470 Colors.filenameEm, py3compat.cast_unicode(value.filename), Colors.normalEm,
505 % (
471 Colors.linenoEm, lineno, Colors.Normal ))
506 Colors.normalEm,
472 if textline == '':
507 _format_filename(
508 value.filename, Colors.filenameEm, Colors.normalEm
509 ),
510 Colors.linenoEm,
511 lineno,
512 Colors.Normal,
513 )
514 )
515 if textline == "":
473 textline = py3compat.cast_unicode(value.text, "utf-8")
516 textline = py3compat.cast_unicode(value.text, "utf-8")
474
517
475 if textline is not None:
518 if textline is not None:
@@ -581,25 +624,19 b' class VerboseTB(TBTools):'
581 Colors = self.Colors # just a shorthand + quicker name lookup
624 Colors = self.Colors # just a shorthand + quicker name lookup
582 ColorsNormal = Colors.Normal # used a lot
625 ColorsNormal = Colors.Normal # used a lot
583
626
584
585
586 if isinstance(frame_info, stack_data.RepeatedFrames):
627 if isinstance(frame_info, stack_data.RepeatedFrames):
587 return ' %s[... skipping similar frames: %s]%s\n' % (
628 return ' %s[... skipping similar frames: %s]%s\n' % (
588 Colors.excName, frame_info.description, ColorsNormal)
629 Colors.excName, frame_info.description, ColorsNormal)
589
630
590 indent = ' ' * INDENT_SIZE
631 indent = ' ' * INDENT_SIZE
591 em_normal = '%s\n%s%s' % (Colors.valEm, indent, ColorsNormal)
632 em_normal = '%s\n%s%s' % (Colors.valEm, indent, ColorsNormal)
592 tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
593 tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm,
633 tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm,
594 ColorsNormal)
634 ColorsNormal)
595 tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \
635 tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \
596 (Colors.vName, Colors.valEm, ColorsNormal)
636 (Colors.vName, Colors.valEm, ColorsNormal)
597 tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal)
598 tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal)
637 tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal)
599
638
600 file = frame_info.filename
639 link = _format_filename(frame_info.filename, Colors.filenameEm, ColorsNormal)
601 file = py3compat.cast_unicode(file, util_path.fs_encoding)
602 link = tpl_link % util_path.compress_user(file)
603 args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame)
640 args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame)
604
641
605 func = frame_info.executing.code_qualname()
642 func = frame_info.executing.code_qualname()
@@ -634,12 +671,20 b' class VerboseTB(TBTools):'
634 lvals = ''
671 lvals = ''
635 lvals_list = []
672 lvals_list = []
636 if self.include_vars:
673 if self.include_vars:
637 for var in frame_info.variables_in_executing_piece:
674 try:
675 # we likely want to fix stackdata at some point, but
676 # still need a workaround.
677 fibp = frame_info.variables_in_executing_piece
678 for var in fibp:
638 lvals_list.append(tpl_name_val % (var.name, repr(var.value)))
679 lvals_list.append(tpl_name_val % (var.name, repr(var.value)))
680 except Exception:
681 lvals_list.append(
682 "Exception trying to inspect frame. No more locals available."
683 )
639 if lvals_list:
684 if lvals_list:
640 lvals = '%s%s' % (indent, em_normal.join(lvals_list))
685 lvals = '%s%s' % (indent, em_normal.join(lvals_list))
641
686
642 result = '%s %s\n' % (link, call)
687 result = "%s, %s\n" % (link, call)
643
688
644 result += ''.join(_format_traceback_lines(frame_info.lines, Colors, self.has_colors, lvals))
689 result += ''.join(_format_traceback_lines(frame_info.lines, Colors, self.has_colors, lvals))
645 return result
690 return result
@@ -48,6 +48,11 b' The following magic commands are provided:'
48 Reload all modules (except those excluded by ``%aimport``) every
48 Reload all modules (except those excluded by ``%aimport``) every
49 time before executing the Python code typed.
49 time before executing the Python code typed.
50
50
51 ``%autoreload 3``
52
53 Reload all modules AND autoload newly added objects
54 every time before executing the Python code typed.
55
51 ``%aimport``
56 ``%aimport``
52
57
53 List modules which are to be automatically imported or not to be imported.
58 List modules which are to be automatically imported or not to be imported.
@@ -124,14 +129,18 b' from imp import reload'
124 # Autoreload functionality
129 # Autoreload functionality
125 #------------------------------------------------------------------------------
130 # ------------------------------------------------------------------------------
126
131
127 class ModuleReloader(object):
132
133 class ModuleReloader:
128 enabled = False
134 enabled = False
129 """Whether this reloader is enabled"""
135 """Whether this reloader is enabled"""
130
136
131 check_all = True
137 check_all = True
132 """Autoreload all modules, not just those listed in 'modules'"""
138 """Autoreload all modules, not just those listed in 'modules'"""
133
139
134 def __init__(self):
140 autoload_obj = False
141 """Autoreload all modules AND autoload all new objects"""
142
143 def __init__(self, shell=None):
135 # Modules that failed to reload: {module: mtime-on-failed-reload, ...}
144 # Modules that failed to reload: {module: mtime-on-failed-reload, ...}
136 self.failed = {}
145 self.failed = {}
137 # Modules specially marked as autoreloadable.
146 # Modules specially marked as autoreloadable.
@@ -142,6 +151,7 b' class ModuleReloader(object):'
142 self.old_objects = {}
151 self.old_objects = {}
143 # Module modification timestamps
152 # Module modification timestamps
144 self.modules_mtimes = {}
153 self.modules_mtimes = {}
154 self.shell = shell
145
155
146 # Cache module modification times
156 # Cache module modification times
147 self.check(check_all=True, do_reload=False)
157 self.check(check_all=True, do_reload=False)
@@ -176,22 +186,22 b' class ModuleReloader(object):'
176 self.mark_module_reloadable(module_name)
186 self.mark_module_reloadable(module_name)
177
187
178 import_module(module_name)
188 import_module(module_name)
179 top_name = module_name.split('.')[0]
189 top_name = module_name.split(".")[0]
180 top_module = sys.modules[top_name]
190 top_module = sys.modules[top_name]
181 return top_module, top_name
191 return top_module, top_name
182
192
183 def filename_and_mtime(self, module):
193 def filename_and_mtime(self, module):
184 if not hasattr(module, '__file__') or module.__file__ is None:
194 if not hasattr(module, "__file__") or module.__file__ is None:
185 return None, None
195 return None, None
186
196
187 if getattr(module, '__name__', None) in [None, '__mp_main__', '__main__']:
197 if getattr(module, "__name__", None) in [None, "__mp_main__", "__main__"]:
188 # we cannot reload(__main__) or reload(__mp_main__)
198 # we cannot reload(__main__) or reload(__mp_main__)
189 return None, None
199 return None, None
190
200
191 filename = module.__file__
201 filename = module.__file__
192 path, ext = os.path.splitext(filename)
202 path, ext = os.path.splitext(filename)
193
203
194 if ext.lower() == '.py':
204 if ext.lower() == ".py":
195 py_filename = filename
205 py_filename = filename
196 else:
206 else:
197 try:
207 try:
@@ -242,21 +252,35 b' class ModuleReloader(object):'
242 # If we've reached this point, we should try to reload the module
252 # If we've reached this point, we should try to reload the module
243 if do_reload:
253 if do_reload:
244 try:
254 try:
255 if self.autoload_obj:
256 superreload(m, reload, self.old_objects, self.shell)
257 else:
245 superreload(m, reload, self.old_objects)
258 superreload(m, reload, self.old_objects)
246 if py_filename in self.failed:
259 if py_filename in self.failed:
247 del self.failed[py_filename]
260 del self.failed[py_filename]
248 except:
261 except:
249 print("[autoreload of %s failed: %s]" % (
262 print(
250 modname, traceback.format_exc(10)), file=sys.stderr)
263 "[autoreload of {} failed: {}]".format(
264 modname, traceback.format_exc(10)
265 ),
266 file=sys.stderr,
267 )
251 self.failed[py_filename] = pymtime
268 self.failed[py_filename] = pymtime
252
269
270
253 #------------------------------------------------------------------------------
271 # ------------------------------------------------------------------------------
254 # superreload
272 # superreload
255 #------------------------------------------------------------------------------
273 # ------------------------------------------------------------------------------
256
274
257
275
258 func_attrs = ['__code__', '__defaults__', '__doc__',
276 func_attrs = [
259 '__closure__', '__globals__', '__dict__']
277 "__code__",
278 "__defaults__",
279 "__doc__",
280 "__closure__",
281 "__globals__",
282 "__dict__",
283 ]
260
284
261
285
262 def update_function(old, new):
286 def update_function(old, new):
@@ -299,7 +323,8 b' def update_class(old, new):'
299 pass
323 pass
300 continue
324 continue
301
325
302 if update_generic(old_obj, new_obj): continue
326 if update_generic(old_obj, new_obj):
327 continue
303
328
304 try:
329 try:
305 setattr(old, key, getattr(new, key))
330 setattr(old, key, getattr(new, key))
@@ -329,16 +354,18 b' def isinstance2(a, b, typ):'
329
354
330
355
331 UPDATE_RULES = [
356 UPDATE_RULES = [
332 (lambda a, b: isinstance2(a, b, type),
357 (lambda a, b: isinstance2(a, b, type), update_class),
333 update_class),
358 (lambda a, b: isinstance2(a, b, types.FunctionType), update_function),
334 (lambda a, b: isinstance2(a, b, types.FunctionType),
359 (lambda a, b: isinstance2(a, b, property), update_property),
335 update_function),
336 (lambda a, b: isinstance2(a, b, property),
337 update_property),
338 ]
360 ]
339 UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.MethodType),
361 UPDATE_RULES.extend(
340 lambda a, b: update_function(a.__func__, b.__func__)),
362 [
341 ])
363 (
364 lambda a, b: isinstance2(a, b, types.MethodType),
365 lambda a, b: update_function(a.__func__, b.__func__),
366 ),
367 ]
368 )
342
369
343
370
344 def update_generic(a, b):
371 def update_generic(a, b):
@@ -349,14 +376,45 b' def update_generic(a, b):'
349 return False
376 return False
350
377
351
378
352 class StrongRef(object):
379 class StrongRef:
353 def __init__(self, obj):
380 def __init__(self, obj):
354 self.obj = obj
381 self.obj = obj
382
355 def __call__(self):
383 def __call__(self):
356 return self.obj
384 return self.obj
357
385
358
386
359 def superreload(module, reload=reload, old_objects=None):
387 mod_attrs = [
388 "__name__",
389 "__doc__",
390 "__package__",
391 "__loader__",
392 "__spec__",
393 "__file__",
394 "__cached__",
395 "__builtins__",
396 ]
397
398
399 def append_obj(module, d, name, obj, autoload=False):
400 in_module = hasattr(obj, "__module__") and obj.__module__ == module.__name__
401 if autoload:
402 # check needed for module global built-ins
403 if not in_module and name in mod_attrs:
404 return False
405 else:
406 if not in_module:
407 return False
408
409 key = (module.__name__, name)
410 try:
411 d.setdefault(key, []).append(weakref.ref(obj))
412 except TypeError:
413 pass
414 return True
415
416
417 def superreload(module, reload=reload, old_objects=None, shell=None):
360 """Enhanced version of the builtin reload function.
418 """Enhanced version of the builtin reload function.
361
419
362 superreload remembers objects previously in the module, and
420 superreload remembers objects previously in the module, and
@@ -371,7 +429,7 b' def superreload(module, reload=reload, old_objects=None):'
371
429
372 # collect old objects in the module
430 # collect old objects in the module
373 for name, obj in list(module.__dict__.items()):
431 for name, obj in list(module.__dict__.items()):
374 if not hasattr(obj, '__module__') or obj.__module__ != module.__name__:
432 if not append_obj(module, old_objects, name, obj):
375 continue
433 continue
376 key = (module.__name__, name)
434 key = (module.__name__, name)
377 try:
435 try:
@@ -385,8 +443,8 b' def superreload(module, reload=reload, old_objects=None):'
385 old_dict = module.__dict__.copy()
443 old_dict = module.__dict__.copy()
386 old_name = module.__name__
444 old_name = module.__name__
387 module.__dict__.clear()
445 module.__dict__.clear()
388 module.__dict__['__name__'] = old_name
446 module.__dict__["__name__"] = old_name
389 module.__dict__['__loader__'] = old_dict['__loader__']
447 module.__dict__["__loader__"] = old_dict["__loader__"]
390 except (TypeError, AttributeError, KeyError):
448 except (TypeError, AttributeError, KeyError):
391 pass
449 pass
392
450
@@ -400,12 +458,21 b' def superreload(module, reload=reload, old_objects=None):'
400 # iterate over all objects and update functions & classes
458 # iterate over all objects and update functions & classes
401 for name, new_obj in list(module.__dict__.items()):
459 for name, new_obj in list(module.__dict__.items()):
402 key = (module.__name__, name)
460 key = (module.__name__, name)
403 if key not in old_objects: continue
461 if key not in old_objects:
462 # here 'shell' acts both as a flag and as an output var
463 if (
464 shell is None
465 or name == "Enum"
466 or not append_obj(module, old_objects, name, new_obj, True)
467 ):
468 continue
469 shell.user_ns[name] = new_obj
404
470
405 new_refs = []
471 new_refs = []
406 for old_ref in old_objects[key]:
472 for old_ref in old_objects[key]:
407 old_obj = old_ref()
473 old_obj = old_ref()
408 if old_obj is None: continue
474 if old_obj is None:
475 continue
409 new_refs.append(old_ref)
476 new_refs.append(old_ref)
410 update_generic(old_obj, new_obj)
477 update_generic(old_obj, new_obj)
411
478
@@ -416,22 +483,25 b' def superreload(module, reload=reload, old_objects=None):'
416
483
417 return module
484 return module
418
485
486
419 #------------------------------------------------------------------------------
487 # ------------------------------------------------------------------------------
420 # IPython connectivity
488 # IPython connectivity
421 #------------------------------------------------------------------------------
489 # ------------------------------------------------------------------------------
422
490
423 from IPython.core.magic import Magics, magics_class, line_magic
491 from IPython.core.magic import Magics, magics_class, line_magic
424
492
493
425 @magics_class
494 @magics_class
426 class AutoreloadMagics(Magics):
495 class AutoreloadMagics(Magics):
427 def __init__(self, *a, **kw):
496 def __init__(self, *a, **kw):
428 super(AutoreloadMagics, self).__init__(*a, **kw)
497 super().__init__(*a, **kw)
429 self._reloader = ModuleReloader()
498 self._reloader = ModuleReloader(self.shell)
430 self._reloader.check_all = False
499 self._reloader.check_all = False
500 self._reloader.autoload_obj = False
431 self.loaded_modules = set(sys.modules)
501 self.loaded_modules = set(sys.modules)
432
502
433 @line_magic
503 @line_magic
434 def autoreload(self, parameter_s=''):
504 def autoreload(self, parameter_s=""):
435 r"""%autoreload => Reload modules automatically
505 r"""%autoreload => Reload modules automatically
436
506
437 %autoreload
507 %autoreload
@@ -475,19 +545,24 b' class AutoreloadMagics(Magics):'
475 autoreloaded.
545 autoreloaded.
476
546
477 """
547 """
478 if parameter_s == '':
548 if parameter_s == "":
479 self._reloader.check(True)
549 self._reloader.check(True)
480 elif parameter_s == '0':
550 elif parameter_s == "0":
481 self._reloader.enabled = False
551 self._reloader.enabled = False
482 elif parameter_s == '1':
552 elif parameter_s == "1":
483 self._reloader.check_all = False
553 self._reloader.check_all = False
484 self._reloader.enabled = True
554 self._reloader.enabled = True
485 elif parameter_s == '2':
555 elif parameter_s == "2":
486 self._reloader.check_all = True
556 self._reloader.check_all = True
487 self._reloader.enabled = True
557 self._reloader.enabled = True
558 self._reloader.enabled = True
559 elif parameter_s == "3":
560 self._reloader.check_all = True
561 self._reloader.enabled = True
562 self._reloader.autoload_obj = True
488
563
489 @line_magic
564 @line_magic
490 def aimport(self, parameter_s='', stream=None):
565 def aimport(self, parameter_s="", stream=None):
491 """%aimport => Import modules for automatic reloading.
566 """%aimport => Import modules for automatic reloading.
492
567
493 %aimport
568 %aimport
@@ -511,13 +586,13 b' class AutoreloadMagics(Magics):'
511 if self._reloader.check_all:
586 if self._reloader.check_all:
512 stream.write("Modules to reload:\nall-except-skipped\n")
587 stream.write("Modules to reload:\nall-except-skipped\n")
513 else:
588 else:
514 stream.write("Modules to reload:\n%s\n" % ' '.join(to_reload))
589 stream.write("Modules to reload:\n%s\n" % " ".join(to_reload))
515 stream.write("\nModules to skip:\n%s\n" % ' '.join(to_skip))
590 stream.write("\nModules to skip:\n%s\n" % " ".join(to_skip))
516 elif modname.startswith('-'):
591 elif modname.startswith("-"):
517 modname = modname[1:]
592 modname = modname[1:]
518 self._reloader.mark_module_skipped(modname)
593 self._reloader.mark_module_skipped(modname)
519 else:
594 else:
520 for _module in ([_.strip() for _ in modname.split(',')]):
595 for _module in [_.strip() for _ in modname.split(",")]:
521 top_module, top_name = self._reloader.aimport_module(_module)
596 top_module, top_name = self._reloader.aimport_module(_module)
522
597
523 # Inject module to user namespace
598 # Inject module to user namespace
@@ -531,8 +606,7 b' class AutoreloadMagics(Magics):'
531 pass
606 pass
532
607
533 def post_execute_hook(self):
608 def post_execute_hook(self):
534 """Cache the modification times of any modules imported in this execution
609 """Cache the modification times of any modules imported in this execution"""
535 """
536 newly_loaded_modules = set(sys.modules) - self.loaded_modules
610 newly_loaded_modules = set(sys.modules) - self.loaded_modules
537 for modname in newly_loaded_modules:
611 for modname in newly_loaded_modules:
538 _, pymtime = self._reloader.filename_and_mtime(sys.modules[modname])
612 _, pymtime = self._reloader.filename_and_mtime(sys.modules[modname])
@@ -546,5 +620,5 b' def load_ipython_extension(ip):'
546 """Load the extension in IPython."""
620 """Load the extension in IPython."""
547 auto_reload = AutoreloadMagics(ip)
621 auto_reload = AutoreloadMagics(ip)
548 ip.register_magics(auto_reload)
622 ip.register_magics(auto_reload)
549 ip.events.register('pre_run_cell', auto_reload.pre_run_cell)
623 ip.events.register("pre_run_cell", auto_reload.pre_run_cell)
550 ip.events.register('post_execute', auto_reload.post_execute_hook)
624 ip.events.register("post_execute", auto_reload.post_execute_hook)
@@ -35,20 +35,20 b' from IPython.core.events import EventManager, pre_run_cell'
35
35
36 noop = lambda *a, **kw: None
36 noop = lambda *a, **kw: None
37
37
38 class FakeShell:
39
38
39 class FakeShell:
40 def __init__(self):
40 def __init__(self):
41 self.ns = {}
41 self.ns = {}
42 self.user_ns = self.ns
42 self.user_ns = self.ns
43 self.user_ns_hidden = {}
43 self.user_ns_hidden = {}
44 self.events = EventManager(self, {'pre_run_cell', pre_run_cell})
44 self.events = EventManager(self, {"pre_run_cell", pre_run_cell})
45 self.auto_magics = AutoreloadMagics(shell=self)
45 self.auto_magics = AutoreloadMagics(shell=self)
46 self.events.register('pre_run_cell', self.auto_magics.pre_run_cell)
46 self.events.register("pre_run_cell", self.auto_magics.pre_run_cell)
47
47
48 register_magics = set_hook = noop
48 register_magics = set_hook = noop
49
49
50 def run_code(self, code):
50 def run_code(self, code):
51 self.events.trigger('pre_run_cell')
51 self.events.trigger("pre_run_cell")
52 exec(code, self.user_ns)
52 exec(code, self.user_ns)
53 self.auto_magics.post_execute_hook()
53 self.auto_magics.post_execute_hook()
54
54
@@ -111,20 +111,22 b' class Fixture(TestCase):'
111 time.sleep(1.05)
111 time.sleep(1.05)
112
112
113 # Write
113 # Write
114 with open(filename, 'w') as f:
114 with open(filename, "w") as f:
115 f.write(content)
115 f.write(content)
116
116
117 def new_module(self, code):
117 def new_module(self, code):
118 code = textwrap.dedent(code)
118 code = textwrap.dedent(code)
119 mod_name, mod_fn = self.get_module()
119 mod_name, mod_fn = self.get_module()
120 with open(mod_fn, 'w') as f:
120 with open(mod_fn, "w") as f:
121 f.write(code)
121 f.write(code)
122 return mod_name, mod_fn
122 return mod_name, mod_fn
123
123
124
124 #-----------------------------------------------------------------------------
125 # -----------------------------------------------------------------------------
125 # Test automatic reloading
126 # Test automatic reloading
126 #-----------------------------------------------------------------------------
127 # -----------------------------------------------------------------------------
127
128
129
128 def pickle_get_current_class(obj):
130 def pickle_get_current_class(obj):
129 """
131 """
130 Original issue comes from pickle; hence the name.
132 Original issue comes from pickle; hence the name.
@@ -136,25 +138,36 b' def pickle_get_current_class(obj):'
136 obj2 = getattr(obj2, subpath)
138 obj2 = getattr(obj2, subpath)
137 return obj2
139 return obj2
138
140
139 class TestAutoreload(Fixture):
140
141
142 class TestAutoreload(Fixture):
141 def test_reload_enums(self):
143 def test_reload_enums(self):
142 mod_name, mod_fn = self.new_module(textwrap.dedent("""
144 mod_name, mod_fn = self.new_module(
145 textwrap.dedent(
146 """
143 from enum import Enum
147 from enum import Enum
144 class MyEnum(Enum):
148 class MyEnum(Enum):
145 A = 'A'
149 A = 'A'
146 B = 'B'
150 B = 'B'
147 """))
151 """
152 )
153 )
148 self.shell.magic_autoreload("2")
154 self.shell.magic_autoreload("2")
149 self.shell.magic_aimport(mod_name)
155 self.shell.magic_aimport(mod_name)
150 self.write_file(mod_fn, textwrap.dedent("""
156 self.write_file(
157 mod_fn,
158 textwrap.dedent(
159 """
151 from enum import Enum
160 from enum import Enum
152 class MyEnum(Enum):
161 class MyEnum(Enum):
153 A = 'A'
162 A = 'A'
154 B = 'B'
163 B = 'B'
155 C = 'C'
164 C = 'C'
156 """))
165 """
157 with tt.AssertNotPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
166 ),
167 )
168 with tt.AssertNotPrints(
169 ("[autoreload of %s failed:" % mod_name), channel="stderr"
170 ):
158 self.shell.run_code("pass") # trigger another reload
171 self.shell.run_code("pass") # trigger another reload
159
172
160 def test_reload_class_type(self):
173 def test_reload_class_type(self):
@@ -195,7 +208,9 b' class TestAutoreload(Fixture):'
195
208
196 def test_reload_class_attributes(self):
209 def test_reload_class_attributes(self):
197 self.shell.magic_autoreload("2")
210 self.shell.magic_autoreload("2")
198 mod_name, mod_fn = self.new_module(textwrap.dedent("""
211 mod_name, mod_fn = self.new_module(
212 textwrap.dedent(
213 """
199 class MyClass:
214 class MyClass:
200
215
201 def __init__(self, a=10):
216 def __init__(self, a=10):
@@ -241,16 +256,99 b' class TestAutoreload(Fixture):'
241
256
242 self.shell.run_code("second = MyClass(5)")
257 self.shell.run_code("second = MyClass(5)")
243
258
244 for object_name in {'first', 'second'}:
259 for object_name in {"first", "second"}:
245 self.shell.run_code("{object_name}.power(5)".format(object_name=object_name))
260 self.shell.run_code(f"{object_name}.power(5)")
246 with nt.assert_raises(AttributeError):
261 with nt.assert_raises(AttributeError):
247 self.shell.run_code("{object_name}.cube()".format(object_name=object_name))
262 self.shell.run_code(f"{object_name}.cube()")
248 with nt.assert_raises(AttributeError):
263 with nt.assert_raises(AttributeError):
249 self.shell.run_code("{object_name}.square()".format(object_name=object_name))
264 self.shell.run_code(f"{object_name}.square()")
250 self.shell.run_code("{object_name}.b".format(object_name=object_name))
265 self.shell.run_code(f"{object_name}.b")
251 self.shell.run_code("{object_name}.a".format(object_name=object_name))
266 self.shell.run_code(f"{object_name}.a")
252 with nt.assert_raises(AttributeError):
267 with nt.assert_raises(AttributeError):
253 self.shell.run_code("{object_name}.toto".format(object_name=object_name))
268 self.shell.run_code(f"{object_name}.toto")
269
270 def test_autoload_newly_added_objects(self):
271 self.shell.magic_autoreload("3")
272 mod_code = """
273 def func1(): pass
274 """
275 mod_name, mod_fn = self.new_module(textwrap.dedent(mod_code))
276 self.shell.run_code(f"from {mod_name} import *")
277 self.shell.run_code("func1()")
278 with nt.assert_raises(NameError):
279 self.shell.run_code("func2()")
280 with nt.assert_raises(NameError):
281 self.shell.run_code("t = Test()")
282 with nt.assert_raises(NameError):
283 self.shell.run_code("number")
284
285 # ----------- TEST NEW OBJ LOADED --------------------------
286
287 new_code = """
288 def func1(): pass
289 def func2(): pass
290 class Test: pass
291 number = 0
292 from enum import Enum
293 class TestEnum(Enum):
294 A = 'a'
295 """
296 self.write_file(mod_fn, textwrap.dedent(new_code))
297
298 # test function now exists in shell's namespace namespace
299 self.shell.run_code("func2()")
300 # test function now exists in module's dict
301 self.shell.run_code(f"import sys; sys.modules['{mod_name}'].func2()")
302 # test class now exists
303 self.shell.run_code("t = Test()")
304 # test global built-in var now exists
305 self.shell.run_code("number")
306 # test the enumerations gets loaded succesfully
307 self.shell.run_code("TestEnum.A")
308
309 # ----------- TEST NEW OBJ CAN BE CHANGED --------------------
310
311 new_code = """
312 def func1(): return 'changed'
313 def func2(): return 'changed'
314 class Test:
315 def new_func(self):
316 return 'changed'
317 number = 1
318 from enum import Enum
319 class TestEnum(Enum):
320 A = 'a'
321 B = 'added'
322 """
323 self.write_file(mod_fn, textwrap.dedent(new_code))
324 self.shell.run_code("assert func1() == 'changed'")
325 self.shell.run_code("assert func2() == 'changed'")
326 self.shell.run_code("t = Test(); assert t.new_func() == 'changed'")
327 self.shell.run_code("assert number == 1")
328 self.shell.run_code("assert TestEnum.B.value == 'added'")
329
330 # ----------- TEST IMPORT FROM MODULE --------------------------
331
332 new_mod_code = """
333 from enum import Enum
334 class Ext(Enum):
335 A = 'ext'
336 def ext_func():
337 return 'ext'
338 class ExtTest:
339 def meth(self):
340 return 'ext'
341 ext_int = 2
342 """
343 new_mod_name, new_mod_fn = self.new_module(textwrap.dedent(new_mod_code))
344 current_mod_code = f"""
345 from {new_mod_name} import *
346 """
347 self.write_file(mod_fn, textwrap.dedent(current_mod_code))
348 self.shell.run_code("assert Ext.A.value == 'ext'")
349 self.shell.run_code("assert ext_func() == 'ext'")
350 self.shell.run_code("t = ExtTest(); assert t.meth() == 'ext'")
351 self.shell.run_code("assert ext_int == 2")
254
352
255 def _check_smoketest(self, use_aimport=True):
353 def _check_smoketest(self, use_aimport=True):
256 """
354 """
@@ -258,7 +356,8 b' class TestAutoreload(Fixture):'
258 '%autoreload 1' or '%autoreload 2'
356 '%autoreload 1' or '%autoreload 2'
259 """
357 """
260
358
261 mod_name, mod_fn = self.new_module("""
359 mod_name, mod_fn = self.new_module(
360 """
262 x = 9
361 x = 9
263
362
264 z = 123 # this item will be deleted
363 z = 123 # this item will be deleted
@@ -281,7 +380,8 b' class Baz(object):'
281 class Bar: # old-style class: weakref doesn't work for it on Python < 2.7
380 class Bar: # old-style class: weakref doesn't work for it on Python < 2.7
282 def foo(self):
381 def foo(self):
283 return 1
382 return 1
284 """)
383 """
384 )
285
385
286 #
386 #
287 # Import module, and mark for reloading
387 # Import module, and mark for reloading
@@ -300,8 +400,9 b" class Bar: # old-style class: weakref doesn't work for it on Python < 2.7"
300 self.shell.run_code("import %s" % mod_name)
400 self.shell.run_code("import %s" % mod_name)
301 stream = StringIO()
401 stream = StringIO()
302 self.shell.magic_aimport("", stream=stream)
402 self.shell.magic_aimport("", stream=stream)
303 nt.assert_true("Modules to reload:\nall-except-skipped" in
403 nt.assert_true(
304 stream.getvalue())
404 "Modules to reload:\nall-except-skipped" in stream.getvalue()
405 )
305 nt.assert_in(mod_name, self.shell.ns)
406 nt.assert_in(mod_name, self.shell.ns)
306
407
307 mod = sys.modules[mod_name]
408 mod = sys.modules[mod_name]
@@ -336,20 +437,29 b" class Bar: # old-style class: weakref doesn't work for it on Python < 2.7"
336 # Simulate a failed reload: no reload should occur and exactly
437 # Simulate a failed reload: no reload should occur and exactly
337 # one error message should be printed
438 # one error message should be printed
338 #
439 #
339 self.write_file(mod_fn, """
440 self.write_file(
441 mod_fn,
442 """
340 a syntax error
443 a syntax error
341 """)
444 """,
445 )
342
446
343 with tt.AssertPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
447 with tt.AssertPrints(
448 ("[autoreload of %s failed:" % mod_name), channel="stderr"
449 ):
344 self.shell.run_code("pass") # trigger reload
450 self.shell.run_code("pass") # trigger reload
345 with tt.AssertNotPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
451 with tt.AssertNotPrints(
452 ("[autoreload of %s failed:" % mod_name), channel="stderr"
453 ):
346 self.shell.run_code("pass") # trigger another reload
454 self.shell.run_code("pass") # trigger another reload
347 check_module_contents()
455 check_module_contents()
348
456
349 #
457 #
350 # Rewrite module (this time reload should succeed)
458 # Rewrite module (this time reload should succeed)
351 #
459 #
352 self.write_file(mod_fn, """
460 self.write_file(
461 mod_fn,
462 """
353 x = 10
463 x = 10
354
464
355 def foo(y):
465 def foo(y):
@@ -367,11 +477,12 b' class Baz(object):'
367 class Bar: # old-style class
477 class Bar: # old-style class
368 def foo(self):
478 def foo(self):
369 return 2
479 return 2
370 """)
480 """,
481 )
371
482
372 def check_module_contents():
483 def check_module_contents():
373 nt.assert_equal(mod.x, 10)
484 nt.assert_equal(mod.x, 10)
374 nt.assert_false(hasattr(mod, 'z'))
485 nt.assert_false(hasattr(mod, "z"))
375
486
376 nt.assert_equal(old_foo(0), 4) # superreload magic!
487 nt.assert_equal(old_foo(0), 4) # superreload magic!
377 nt.assert_equal(mod.foo(0), 4)
488 nt.assert_equal(mod.foo(0), 4)
@@ -383,8 +494,8 b' class Bar: # old-style class'
383 nt.assert_equal(old_obj.quux, 43)
494 nt.assert_equal(old_obj.quux, 43)
384 nt.assert_equal(obj.quux, 43)
495 nt.assert_equal(obj.quux, 43)
385
496
386 nt.assert_false(hasattr(old_obj, 'zzz'))
497 nt.assert_false(hasattr(old_obj, "zzz"))
387 nt.assert_false(hasattr(obj, 'zzz'))
498 nt.assert_false(hasattr(obj, "zzz"))
388
499
389 obj2 = mod.Bar()
500 obj2 = mod.Bar()
390 nt.assert_equal(old_obj2.foo(), 2)
501 nt.assert_equal(old_obj2.foo(), 2)
@@ -408,17 +519,19 b' class Bar: # old-style class'
408 self.shell.magic_aimport("-" + mod_name)
519 self.shell.magic_aimport("-" + mod_name)
409 stream = StringIO()
520 stream = StringIO()
410 self.shell.magic_aimport("", stream=stream)
521 self.shell.magic_aimport("", stream=stream)
411 nt.assert_true(("Modules to skip:\n%s" % mod_name) in
522 nt.assert_true(("Modules to skip:\n%s" % mod_name) in stream.getvalue())
412 stream.getvalue())
413
523
414 # This should succeed, although no such module exists
524 # This should succeed, although no such module exists
415 self.shell.magic_aimport("-tmpmod_as318989e89ds")
525 self.shell.magic_aimport("-tmpmod_as318989e89ds")
416 else:
526 else:
417 self.shell.magic_autoreload("0")
527 self.shell.magic_autoreload("0")
418
528
419 self.write_file(mod_fn, """
529 self.write_file(
530 mod_fn,
531 """
420 x = -99
532 x = -99
421 """)
533 """,
534 )
422
535
423 self.shell.run_code("pass") # trigger reload
536 self.shell.run_code("pass") # trigger reload
424 self.shell.run_code("pass")
537 self.shell.run_code("pass")
@@ -440,8 +553,3 b' x = -99'
440
553
441 def test_smoketest_autoreload(self):
554 def test_smoketest_autoreload(self):
442 self._check_smoketest(use_aimport=False)
555 self._check_smoketest(use_aimport=False)
443
444
445
446
447
@@ -32,15 +32,42 b' import os'
32 import sys
32 import sys
33
33
34 from IPython.utils.version import check_version
34 from IPython.utils.version import check_version
35 from IPython.external.qt_loaders import (load_qt, loaded_api, QT_API_PYSIDE,
35 from IPython.external.qt_loaders import (
36 QT_API_PYSIDE2, QT_API_PYQT, QT_API_PYQT5,
36 load_qt,
37 QT_API_PYQTv1, QT_API_PYQT_DEFAULT)
37 loaded_api,
38 enum_factory,
39 # QT6
40 QT_API_PYQT6,
41 QT_API_PYSIDE6,
42 # QT5
43 QT_API_PYQT5,
44 QT_API_PYSIDE2,
45 # QT4
46 QT_API_PYQTv1,
47 QT_API_PYQT,
48 QT_API_PYSIDE,
49 # default
50 QT_API_PYQT_DEFAULT,
51 )
52
53 _qt_apis = (
54 # QT6
55 QT_API_PYQT6,
56 QT_API_PYSIDE6,
57 # QT5
58 QT_API_PYQT5,
59 QT_API_PYSIDE2,
60 # QT4
61 QT_API_PYQTv1,
62 QT_API_PYQT,
63 QT_API_PYSIDE,
64 # default
65 QT_API_PYQT_DEFAULT,
66 )
38
67
39 _qt_apis = (QT_API_PYSIDE, QT_API_PYSIDE2, QT_API_PYQT, QT_API_PYQT5, QT_API_PYQTv1,
40 QT_API_PYQT_DEFAULT)
41
68
42 #Constraints placed on an imported matplotlib
43 def matplotlib_options(mpl):
69 def matplotlib_options(mpl):
70 """Constraints placed on an imported matplotlib."""
44 if mpl is None:
71 if mpl is None:
45 return
72 return
46 backend = mpl.rcParams.get('backend', None)
73 backend = mpl.rcParams.get('backend', None)
@@ -66,9 +93,7 b' def matplotlib_options(mpl):'
66 mpqt)
93 mpqt)
67
94
68 def get_options():
95 def get_options():
69 """Return a list of acceptable QT APIs, in decreasing order of
96 """Return a list of acceptable QT APIs, in decreasing order of preference."""
70 preference
71 """
72 #already imported Qt somewhere. Use that
97 #already imported Qt somewhere. Use that
73 loaded = loaded_api()
98 loaded = loaded_api()
74 if loaded is not None:
99 if loaded is not None:
@@ -83,13 +108,22 b' def get_options():'
83 qt_api = os.environ.get('QT_API', None)
108 qt_api = os.environ.get('QT_API', None)
84 if qt_api is None:
109 if qt_api is None:
85 #no ETS variable. Ask mpl, then use default fallback path
110 #no ETS variable. Ask mpl, then use default fallback path
86 return matplotlib_options(mpl) or [QT_API_PYQT_DEFAULT, QT_API_PYSIDE,
111 return matplotlib_options(mpl) or [
87 QT_API_PYQT5, QT_API_PYSIDE2]
112 QT_API_PYQT_DEFAULT,
113 QT_API_PYQT6,
114 QT_API_PYSIDE6,
115 QT_API_PYQT5,
116 QT_API_PYSIDE2,
117 QT_API_PYQT,
118 QT_API_PYSIDE,
119 ]
88 elif qt_api not in _qt_apis:
120 elif qt_api not in _qt_apis:
89 raise RuntimeError("Invalid Qt API %r, valid values are: %r" %
121 raise RuntimeError("Invalid Qt API %r, valid values are: %r" %
90 (qt_api, ', '.join(_qt_apis)))
122 (qt_api, ', '.join(_qt_apis)))
91 else:
123 else:
92 return [qt_api]
124 return [qt_api]
93
125
126
94 api_opts = get_options()
127 api_opts = get_options()
95 QtCore, QtGui, QtSvg, QT_API = load_qt(api_opts)
128 QtCore, QtGui, QtSvg, QT_API = load_qt(api_opts)
129 enum_helper = enum_factory(QT_API, QtCore)
@@ -10,25 +10,40 b' be accessed directly from the outside'
10 """
10 """
11 import sys
11 import sys
12 import types
12 import types
13 from functools import partial
13 from functools import partial, lru_cache
14 from importlib import import_module
14 import operator
15
15
16 from IPython.utils.version import check_version
16 from IPython.utils.version import check_version
17
17
18 # Available APIs.
18 # ### Available APIs.
19 QT_API_PYQT = 'pyqt' # Force version 2
19 # Qt6
20 QT_API_PYQT6 = "pyqt6"
21 QT_API_PYSIDE6 = "pyside6"
22
23 # Qt5
20 QT_API_PYQT5 = 'pyqt5'
24 QT_API_PYQT5 = 'pyqt5'
21 QT_API_PYQTv1 = 'pyqtv1' # Force version 2
22 QT_API_PYQT_DEFAULT = 'pyqtdefault' # use system default for version 1 vs. 2
23 QT_API_PYSIDE = 'pyside'
24 QT_API_PYSIDE2 = 'pyside2'
25 QT_API_PYSIDE2 = 'pyside2'
25
26
26 api_to_module = {QT_API_PYSIDE2: 'PySide2',
27 # Qt4
27 QT_API_PYSIDE: 'PySide',
28 QT_API_PYQT = "pyqt" # Force version 2
28 QT_API_PYQT: 'PyQt4',
29 QT_API_PYQTv1 = "pyqtv1" # Force version 2
29 QT_API_PYQTv1: 'PyQt4',
30 QT_API_PYSIDE = "pyside"
30 QT_API_PYQT5: 'PyQt5',
31
31 QT_API_PYQT_DEFAULT: 'PyQt4',
32 QT_API_PYQT_DEFAULT = "pyqtdefault" # use system default for version 1 vs. 2
33
34 api_to_module = {
35 # Qt6
36 QT_API_PYQT6: "PyQt6",
37 QT_API_PYSIDE6: "PySide6",
38 # Qt5
39 QT_API_PYQT5: "PyQt5",
40 QT_API_PYSIDE2: "PySide2",
41 # Qt4
42 QT_API_PYSIDE: "PySide",
43 QT_API_PYQT: "PyQt4",
44 QT_API_PYQTv1: "PyQt4",
45 # default
46 QT_API_PYQT_DEFAULT: "PyQt6",
32 }
47 }
33
48
34
49
@@ -56,6 +71,7 b' class ImportDenier(object):'
56 already imported an Incompatible QT Binding: %s
71 already imported an Incompatible QT Binding: %s
57 """ % (fullname, loaded_api()))
72 """ % (fullname, loaded_api()))
58
73
74
59 ID = ImportDenier()
75 ID = ImportDenier()
60 sys.meta_path.insert(0, ID)
76 sys.meta_path.insert(0, ID)
61
77
@@ -63,23 +79,11 b' sys.meta_path.insert(0, ID)'
63 def commit_api(api):
79 def commit_api(api):
64 """Commit to a particular API, and trigger ImportErrors on subsequent
80 """Commit to a particular API, and trigger ImportErrors on subsequent
65 dangerous imports"""
81 dangerous imports"""
82 modules = set(api_to_module.values())
66
83
67 if api == QT_API_PYSIDE2:
84 modules.remove(api_to_module[api])
68 ID.forbid('PySide')
85 for mod in modules:
69 ID.forbid('PyQt4')
86 ID.forbid(mod)
70 ID.forbid('PyQt5')
71 elif api == QT_API_PYSIDE:
72 ID.forbid('PySide2')
73 ID.forbid('PyQt4')
74 ID.forbid('PyQt5')
75 elif api == QT_API_PYQT5:
76 ID.forbid('PySide2')
77 ID.forbid('PySide')
78 ID.forbid('PyQt4')
79 else: # There are three other possibilities, all representing PyQt4
80 ID.forbid('PyQt5')
81 ID.forbid('PySide2')
82 ID.forbid('PySide')
83
87
84
88
85 def loaded_api():
89 def loaded_api():
@@ -90,19 +94,24 b' def loaded_api():'
90
94
91 Returns
95 Returns
92 -------
96 -------
93 None, 'pyside2', 'pyside', 'pyqt', 'pyqt5', or 'pyqtv1'
97 None, 'pyside6', 'pyqt6', 'pyside2', 'pyside', 'pyqt', 'pyqt5', 'pyqtv1'
94 """
98 """
95 if 'PyQt4.QtCore' in sys.modules:
99 if sys.modules.get("PyQt6.QtCore"):
100 return QT_API_PYQT6
101 elif sys.modules.get("PySide6.QtCore"):
102 return QT_API_PYSIDE6
103 elif sys.modules.get("PyQt5.QtCore"):
104 return QT_API_PYQT5
105 elif sys.modules.get("PySide2.QtCore"):
106 return QT_API_PYSIDE2
107 elif sys.modules.get("PyQt4.QtCore"):
96 if qtapi_version() == 2:
108 if qtapi_version() == 2:
97 return QT_API_PYQT
109 return QT_API_PYQT
98 else:
110 else:
99 return QT_API_PYQTv1
111 return QT_API_PYQTv1
100 elif 'PySide.QtCore' in sys.modules:
112 elif sys.modules.get("PySide.QtCore"):
101 return QT_API_PYSIDE
113 return QT_API_PYSIDE
102 elif 'PySide2.QtCore' in sys.modules:
114
103 return QT_API_PYSIDE2
104 elif 'PyQt5.QtCore' in sys.modules:
105 return QT_API_PYQT5
106 return None
115 return None
107
116
108
117
@@ -122,7 +131,7 b' def has_binding(api):'
122 from importlib.util import find_spec
131 from importlib.util import find_spec
123
132
124 required = ['QtCore', 'QtGui', 'QtSvg']
133 required = ['QtCore', 'QtGui', 'QtSvg']
125 if api in (QT_API_PYQT5, QT_API_PYSIDE2):
134 if api in (QT_API_PYQT5, QT_API_PYSIDE2, QT_API_PYQT6, QT_API_PYSIDE6):
126 # QT5 requires QtWidgets too
135 # QT5 requires QtWidgets too
127 required.append('QtWidgets')
136 required.append('QtWidgets')
128
137
@@ -174,7 +183,7 b' def can_import(api):'
174
183
175 current = loaded_api()
184 current = loaded_api()
176 if api == QT_API_PYQT_DEFAULT:
185 if api == QT_API_PYQT_DEFAULT:
177 return current in [QT_API_PYQT, QT_API_PYQTv1, None]
186 return current in [QT_API_PYQT6, None]
178 else:
187 else:
179 return current in [api, None]
188 return current in [api, None]
180
189
@@ -238,6 +247,28 b' def import_pyqt5():'
238 return QtCore, QtGuiCompat, QtSvg, api
247 return QtCore, QtGuiCompat, QtSvg, api
239
248
240
249
250 def import_pyqt6():
251 """
252 Import PyQt6
253
254 ImportErrors rasied within this function are non-recoverable
255 """
256
257 from PyQt6 import QtCore, QtSvg, QtWidgets, QtGui
258
259 # Alias PyQt-specific functions for PySide compatibility.
260 QtCore.Signal = QtCore.pyqtSignal
261 QtCore.Slot = QtCore.pyqtSlot
262
263 # Join QtGui and QtWidgets for Qt4 compatibility.
264 QtGuiCompat = types.ModuleType("QtGuiCompat")
265 QtGuiCompat.__dict__.update(QtGui.__dict__)
266 QtGuiCompat.__dict__.update(QtWidgets.__dict__)
267
268 api = QT_API_PYQT6
269 return QtCore, QtGuiCompat, QtSvg, api
270
271
241 def import_pyside():
272 def import_pyside():
242 """
273 """
243 Import PySide
274 Import PySide
@@ -264,6 +295,23 b' def import_pyside2():'
264 return QtCore, QtGuiCompat, QtSvg, QT_API_PYSIDE2
295 return QtCore, QtGuiCompat, QtSvg, QT_API_PYSIDE2
265
296
266
297
298 def import_pyside6():
299 """
300 Import PySide6
301
302 ImportErrors raised within this function are non-recoverable
303 """
304 from PySide6 import QtGui, QtCore, QtSvg, QtWidgets, QtPrintSupport
305
306 # Join QtGui and QtWidgets for Qt4 compatibility.
307 QtGuiCompat = types.ModuleType("QtGuiCompat")
308 QtGuiCompat.__dict__.update(QtGui.__dict__)
309 QtGuiCompat.__dict__.update(QtWidgets.__dict__)
310 QtGuiCompat.__dict__.update(QtPrintSupport.__dict__)
311
312 return QtCore, QtGuiCompat, QtSvg, QT_API_PYSIDE6
313
314
267 def load_qt(api_options):
315 def load_qt(api_options):
268 """
316 """
269 Attempt to import Qt, given a preference list
317 Attempt to import Qt, given a preference list
@@ -291,12 +339,18 b' def load_qt(api_options):'
291 an incompatible library has already been installed)
339 an incompatible library has already been installed)
292 """
340 """
293 loaders = {
341 loaders = {
342 # Qt6
343 QT_API_PYQT6: import_pyqt6,
344 QT_API_PYSIDE6: import_pyside6,
345 # Qt5
346 QT_API_PYQT5: import_pyqt5,
294 QT_API_PYSIDE2: import_pyside2,
347 QT_API_PYSIDE2: import_pyside2,
348 # Qt4
295 QT_API_PYSIDE: import_pyside,
349 QT_API_PYSIDE: import_pyside,
296 QT_API_PYQT: import_pyqt4,
350 QT_API_PYQT: import_pyqt4,
297 QT_API_PYQT5: import_pyqt5,
298 QT_API_PYQTv1: partial(import_pyqt4, version=1),
351 QT_API_PYQTv1: partial(import_pyqt4, version=1),
299 QT_API_PYQT_DEFAULT: partial(import_pyqt4, version=None)
352 # default
353 QT_API_PYQT_DEFAULT: import_pyqt6,
300 }
354 }
301
355
302 for api in api_options:
356 for api in api_options:
@@ -332,3 +386,16 b' def load_qt(api_options):'
332 has_binding(QT_API_PYSIDE),
386 has_binding(QT_API_PYSIDE),
333 has_binding(QT_API_PYSIDE2),
387 has_binding(QT_API_PYSIDE2),
334 api_options))
388 api_options))
389
390
391 def enum_factory(QT_API, QtCore):
392 """Construct an enum helper to account for PyQt5 <-> PyQt6 changes."""
393
394 @lru_cache(None)
395 def _enum(name):
396 # foo.bar.Enum.Entry (PyQt6) <=> foo.bar.Entry (non-PyQt6).
397 return operator.attrgetter(
398 name if QT_API == QT_API_PYQT6 else name.rpartition(".")[0]
399 )(sys.modules[QtCore.__package__])
400
401 return _enum
@@ -8,6 +8,8 b' from os import walk, sep, fsdecode'
8
8
9 from IPython.core.display import DisplayObject, TextDisplayObject
9 from IPython.core.display import DisplayObject, TextDisplayObject
10
10
11 from typing import Tuple, Iterable
12
11 __all__ = ['Audio', 'IFrame', 'YouTubeVideo', 'VimeoVideo', 'ScribdDocument',
13 __all__ = ['Audio', 'IFrame', 'YouTubeVideo', 'VimeoVideo', 'ScribdDocument',
12 'FileLink', 'FileLinks', 'Code']
14 'FileLink', 'FileLinks', 'Code']
13
15
@@ -159,7 +161,7 b' class Audio(DisplayObject):'
159 return val
161 return val
160
162
161 @staticmethod
163 @staticmethod
162 def _validate_and_normalize_with_numpy(data, normalize):
164 def _validate_and_normalize_with_numpy(data, normalize) -> Tuple[bytes, int]:
163 import numpy as np
165 import numpy as np
164
166
165 data = np.array(data, dtype=float)
167 data = np.array(data, dtype=float)
@@ -178,8 +180,7 b' class Audio(DisplayObject):'
178 max_abs_value = np.max(np.abs(data))
180 max_abs_value = np.max(np.abs(data))
179 normalization_factor = Audio._get_normalization_factor(max_abs_value, normalize)
181 normalization_factor = Audio._get_normalization_factor(max_abs_value, normalize)
180 scaled = data / normalization_factor * 32767
182 scaled = data / normalization_factor * 32767
181 return scaled.astype('<h').tostring(), nchan
183 return scaled.astype("<h").tobytes(), nchan
182
183
184
184 @staticmethod
185 @staticmethod
185 def _validate_and_normalize_without_numpy(data, normalize):
186 def _validate_and_normalize_without_numpy(data, normalize):
@@ -262,13 +263,18 b' class IFrame(object):'
262 src="{src}{params}"
263 src="{src}{params}"
263 frameborder="0"
264 frameborder="0"
264 allowfullscreen
265 allowfullscreen
266 {extras}
265 ></iframe>
267 ></iframe>
266 """
268 """
267
269
268 def __init__(self, src, width, height, **kwargs):
270 def __init__(self, src, width, height, extras: Iterable[str] = None, **kwargs):
271 if extras is None:
272 extras = []
273
269 self.src = src
274 self.src = src
270 self.width = width
275 self.width = width
271 self.height = height
276 self.height = height
277 self.extras = extras
272 self.params = kwargs
278 self.params = kwargs
273
279
274 def _repr_html_(self):
280 def _repr_html_(self):
@@ -278,10 +284,14 b' class IFrame(object):'
278 params = "?" + urlencode(self.params)
284 params = "?" + urlencode(self.params)
279 else:
285 else:
280 params = ""
286 params = ""
281 return self.iframe.format(src=self.src,
287 return self.iframe.format(
288 src=self.src,
282 width=self.width,
289 width=self.width,
283 height=self.height,
290 height=self.height,
284 params=params)
291 params=params,
292 extras=" ".join(self.extras),
293 )
294
285
295
286 class YouTubeVideo(IFrame):
296 class YouTubeVideo(IFrame):
287 """Class for embedding a YouTube Video in an IPython session, based on its video id.
297 """Class for embedding a YouTube Video in an IPython session, based on its video id.
@@ -309,9 +319,12 b' class YouTubeVideo(IFrame):'
309 will be inserted in the document.
319 will be inserted in the document.
310 """
320 """
311
321
312 def __init__(self, id, width=400, height=300, **kwargs):
322 def __init__(self, id, width=400, height=300, allow_autoplay=False, **kwargs):
313 self.id=id
323 self.id=id
314 src = "https://www.youtube.com/embed/{0}".format(id)
324 src = "https://www.youtube.com/embed/{0}".format(id)
325 if allow_autoplay:
326 extras = list(kwargs.get("extras", [])) + ['allow="autoplay"']
327 kwargs.update(autoplay=1, extras=extras)
315 super(YouTubeVideo, self).__init__(src, width, height, **kwargs)
328 super(YouTubeVideo, self).__init__(src, width, height, **kwargs)
316
329
317 def _repr_jpeg_(self):
330 def _repr_jpeg_(self):
@@ -6,7 +6,6 b' Contributions are *very* welcome.'
6 """
6 """
7
7
8 import os
8 import os
9 import pipes
10 import shlex
9 import shlex
11 import subprocess
10 import subprocess
12 import sys
11 import sys
@@ -47,9 +46,9 b' def install_editor(template, wait=False):'
47 def call_editor(self, filename, line=0):
46 def call_editor(self, filename, line=0):
48 if line is None:
47 if line is None:
49 line = 0
48 line = 0
50 cmd = template.format(filename=pipes.quote(filename), line=line)
49 cmd = template.format(filename=shlex.quote(filename), line=line)
51 print(">", cmd)
50 print(">", cmd)
52 # pipes.quote doesn't work right on Windows, but it does after splitting
51 # shlex.quote doesn't work right on Windows, but it does after splitting
53 if sys.platform.startswith('win'):
52 if sys.platform.startswith('win'):
54 cmd = shlex.split(cmd)
53 cmd = shlex.split(cmd)
55 proc = subprocess.Popen(cmd, shell=True)
54 proc = subprocess.Popen(cmd, shell=True)
@@ -111,7 +111,8 b" def latex_to_png(s, encode=False, backend=None, wrap=False, color='Black',"
111
111
112 def latex_to_png_mpl(s, wrap, color='Black', scale=1.0):
112 def latex_to_png_mpl(s, wrap, color='Black', scale=1.0):
113 try:
113 try:
114 from matplotlib import mathtext
114 from matplotlib import figure, font_manager, mathtext
115 from matplotlib.backends import backend_agg
115 from pyparsing import ParseFatalException
116 from pyparsing import ParseFatalException
116 except ImportError:
117 except ImportError:
117 return None
118 return None
@@ -122,11 +123,18 b" def latex_to_png_mpl(s, wrap, color='Black', scale=1.0):"
122 s = u'${0}$'.format(s)
123 s = u'${0}$'.format(s)
123
124
124 try:
125 try:
125 mt = mathtext.MathTextParser('bitmap')
126 prop = font_manager.FontProperties(size=12)
126 f = BytesIO()
127 dpi = 120*scale
127 dpi = 120 * scale
128 mt.to_png(f, s, fontsize=12, dpi=dpi, color=color)
128 buffer = BytesIO()
129 return f.getvalue()
129
130 # Adapted from mathtext.math_to_image
131 parser = mathtext.MathTextParser("path")
132 width, height, depth, _, _ = parser.parse(s, dpi=72, prop=prop)
133 fig = figure.Figure(figsize=(width / 72, height / 72))
134 fig.text(0, depth / height, s, fontproperties=prop, color=color)
135 backend_agg.FigureCanvasAgg(fig)
136 fig.savefig(buffer, dpi=dpi, format="png", transparent=True)
137 return buffer.getvalue()
130 except (ValueError, RuntimeError, ParseFatalException):
138 except (ValueError, RuntimeError, ParseFatalException):
131 return None
139 return None
132
140
@@ -626,7 +626,7 b' def _default_pprint(obj, p, cycle):'
626 def _seq_pprinter_factory(start, end):
626 def _seq_pprinter_factory(start, end):
627 """
627 """
628 Factory that returns a pprint function useful for sequences. Used by
628 Factory that returns a pprint function useful for sequences. Used by
629 the default pprint for tuples, dicts, and lists.
629 the default pprint for tuples and lists.
630 """
630 """
631 def inner(obj, p, cycle):
631 def inner(obj, p, cycle):
632 if cycle:
632 if cycle:
@@ -15,9 +15,6 b''
15 # Stdlib imports
15 # Stdlib imports
16 import time
16 import time
17
17
18 # Third-party imports
19 import nose.tools as nt
20
21 # Our own imports
18 # Our own imports
22 from IPython.lib import backgroundjobs as bg
19 from IPython.lib import backgroundjobs as bg
23
20
@@ -49,7 +46,7 b' def test_result():'
49 jobs = bg.BackgroundJobManager()
46 jobs = bg.BackgroundJobManager()
50 j = jobs.new(sleeper)
47 j = jobs.new(sleeper)
51 j.join()
48 j.join()
52 nt.assert_equal(j.result['interval'], t_short)
49 assert j.result["interval"] == t_short
53
50
54
51
55 def test_flush():
52 def test_flush():
@@ -57,10 +54,10 b' def test_flush():'
57 jobs = bg.BackgroundJobManager()
54 jobs = bg.BackgroundJobManager()
58 j = jobs.new(sleeper)
55 j = jobs.new(sleeper)
59 j.join()
56 j.join()
60 nt.assert_equal(len(jobs.completed), 1)
57 assert len(jobs.completed) == 1
61 nt.assert_equal(len(jobs.dead), 0)
58 assert len(jobs.dead) == 0
62 jobs.flush()
59 jobs.flush()
63 nt.assert_equal(len(jobs.completed), 0)
60 assert len(jobs.completed) == 0
64
61
65
62
66 def test_dead():
63 def test_dead():
@@ -68,10 +65,10 b' def test_dead():'
68 jobs = bg.BackgroundJobManager()
65 jobs = bg.BackgroundJobManager()
69 j = jobs.new(crasher)
66 j = jobs.new(crasher)
70 j.join()
67 j.join()
71 nt.assert_equal(len(jobs.completed), 0)
68 assert len(jobs.completed) == 0
72 nt.assert_equal(len(jobs.dead), 1)
69 assert len(jobs.dead) == 1
73 jobs.flush()
70 jobs.flush()
74 nt.assert_equal(len(jobs.dead), 0)
71 assert len(jobs.dead) == 0
75
72
76
73
77 def test_longer():
74 def test_longer():
@@ -81,8 +78,8 b' def test_longer():'
81 # job as running, but not so long that it makes the test suite noticeably
78 # job as running, but not so long that it makes the test suite noticeably
82 # slower.
79 # slower.
83 j = jobs.new(sleeper, 0.1)
80 j = jobs.new(sleeper, 0.1)
84 nt.assert_equal(len(jobs.running), 1)
81 assert len(jobs.running) == 1
85 nt.assert_equal(len(jobs.completed), 0)
82 assert len(jobs.completed) == 0
86 j.join()
83 j.join()
87 nt.assert_equal(len(jobs.running), 0)
84 assert len(jobs.running) == 0
88 nt.assert_equal(len(jobs.completed), 1)
85 assert len(jobs.completed) == 1
@@ -60,7 +60,8 b' def test_existing_path_FileLink():'
60 fl = display.FileLink(tf.name)
60 fl = display.FileLink(tf.name)
61 actual = fl._repr_html_()
61 actual = fl._repr_html_()
62 expected = "<a href='%s' target='_blank'>%s</a><br>" % (tf.name,tf.name)
62 expected = "<a href='%s' target='_blank'>%s</a><br>" % (tf.name, tf.name)
63 nt.assert_equal(actual,expected)
63 assert actual == expected
64
64
65
65 def test_existing_path_FileLink_repr():
66 def test_existing_path_FileLink_repr():
66 """FileLink: Calling repr() functions as expected on existing filepath
67 """FileLink: Calling repr() functions as expected on existing filepath
@@ -69,7 +70,8 b' def test_existing_path_FileLink_repr():'
69 fl = display.FileLink(tf.name)
70 fl = display.FileLink(tf.name)
70 actual = repr(fl)
71 actual = repr(fl)
71 expected = tf.name
72 expected = tf.name
72 nt.assert_equal(actual,expected)
73 assert actual == expected
74
73
75
74 def test_error_on_directory_to_FileLink():
76 def test_error_on_directory_to_FileLink():
75 """FileLink: Raises error when passed directory
77 """FileLink: Raises error when passed directory
@@ -111,7 +113,8 b' def test_existing_path_FileLinks():'
111 (tf1.name.replace("\\","/"),split(tf1.name)[1])]
113 (tf1.name.replace("\\","/"),split(tf1.name)[1])]
112 expected.sort()
114 expected.sort()
113 # We compare the sorted list of links here as that's more reliable
115 # We compare the sorted list of links here as that's more reliable
114 nt.assert_equal(actual,expected)
116 assert actual == expected
117
115
118
116 def test_existing_path_FileLinks_alt_formatter():
119 def test_existing_path_FileLinks_alt_formatter():
117 """FileLinks: Calling _repr_html_ functions as expected w/ an alt formatter
120 """FileLinks: Calling _repr_html_ functions as expected w/ an alt formatter
@@ -128,7 +131,8 b' def test_existing_path_FileLinks_alt_formatter():'
128 expected = ["hello","world"]
131 expected = ["hello","world"]
129 expected.sort()
132 expected.sort()
130 # We compare the sorted list of links here as that's more reliable
133 # We compare the sorted list of links here as that's more reliable
131 nt.assert_equal(actual,expected)
134 assert actual == expected
135
132
136
133 def test_existing_path_FileLinks_repr():
137 def test_existing_path_FileLinks_repr():
134 """FileLinks: Calling repr() functions as expected on existing directory """
138 """FileLinks: Calling repr() functions as expected on existing directory """
@@ -142,7 +146,8 b' def test_existing_path_FileLinks_repr():'
142 expected = ['%s/' % td, ' %s' % split(tf1.name)[1],' %s' % split(tf2.name)[1]]
146 expected = ['%s/' % td, ' %s' % split(tf1.name)[1],' %s' % split(tf2.name)[1]]
143 expected.sort()
147 expected.sort()
144 # We compare the sorted list of links here as that's more reliable
148 # We compare the sorted list of links here as that's more reliable
145 nt.assert_equal(actual,expected)
149 assert actual == expected
150
146
151
147 def test_existing_path_FileLinks_repr_alt_formatter():
152 def test_existing_path_FileLinks_repr_alt_formatter():
148 """FileLinks: Calling repr() functions as expected w/ alt formatter
153 """FileLinks: Calling repr() functions as expected w/ alt formatter
@@ -159,7 +164,8 b' def test_existing_path_FileLinks_repr_alt_formatter():'
159 expected = ["hello","world"]
164 expected = ["hello","world"]
160 expected.sort()
165 expected.sort()
161 # We compare the sorted list of links here as that's more reliable
166 # We compare the sorted list of links here as that's more reliable
162 nt.assert_equal(actual,expected)
167 assert actual == expected
168
163
169
164 def test_error_on_file_to_FileLinks():
170 def test_error_on_file_to_FileLinks():
165 """FileLinks: Raises error when passed file
171 """FileLinks: Raises error when passed file
@@ -178,11 +184,11 b' def test_recursive_FileLinks():'
178 fl = display.FileLinks(td)
184 fl = display.FileLinks(td)
179 actual = str(fl)
185 actual = str(fl)
180 actual = actual.split('\n')
186 actual = actual.split('\n')
181 nt.assert_equal(len(actual), 4, actual)
187 assert len(actual) == 4, actual
182 fl = display.FileLinks(td, recursive=False)
188 fl = display.FileLinks(td, recursive=False)
183 actual = str(fl)
189 actual = str(fl)
184 actual = actual.split('\n')
190 actual = actual.split('\n')
185 nt.assert_equal(len(actual), 2, actual)
191 assert len(actual) == 2, actual
186
192
187 def test_audio_from_file():
193 def test_audio_from_file():
188 path = pjoin(dirname(__file__), 'test.wav')
194 path = pjoin(dirname(__file__), 'test.wav')
@@ -194,13 +200,13 b' class TestAudioDataWithNumpy(TestCase):'
194 def test_audio_from_numpy_array(self):
200 def test_audio_from_numpy_array(self):
195 test_tone = get_test_tone()
201 test_tone = get_test_tone()
196 audio = display.Audio(test_tone, rate=44100)
202 audio = display.Audio(test_tone, rate=44100)
197 nt.assert_equal(len(read_wav(audio.data)), len(test_tone))
203 assert len(read_wav(audio.data)) == len(test_tone)
198
204
199 @skipif_not_numpy
205 @skipif_not_numpy
200 def test_audio_from_list(self):
206 def test_audio_from_list(self):
201 test_tone = get_test_tone()
207 test_tone = get_test_tone()
202 audio = display.Audio(list(test_tone), rate=44100)
208 audio = display.Audio(list(test_tone), rate=44100)
203 nt.assert_equal(len(read_wav(audio.data)), len(test_tone))
209 assert len(read_wav(audio.data)) == len(test_tone)
204
210
205 @skipif_not_numpy
211 @skipif_not_numpy
206 def test_audio_from_numpy_array_without_rate_raises(self):
212 def test_audio_from_numpy_array_without_rate_raises(self):
@@ -212,7 +218,7 b' class TestAudioDataWithNumpy(TestCase):'
212 for scale in [1, 0.5, 2]:
218 for scale in [1, 0.5, 2]:
213 audio = display.Audio(get_test_tone(scale), rate=44100)
219 audio = display.Audio(get_test_tone(scale), rate=44100)
214 actual_max_value = numpy.max(numpy.abs(read_wav(audio.data)))
220 actual_max_value = numpy.max(numpy.abs(read_wav(audio.data)))
215 nt.assert_equal(actual_max_value, expected_max_value)
221 assert actual_max_value == expected_max_value
216
222
217 @skipif_not_numpy
223 @skipif_not_numpy
218 def test_audio_data_without_normalization(self):
224 def test_audio_data_without_normalization(self):
@@ -223,7 +229,7 b' class TestAudioDataWithNumpy(TestCase):'
223 expected_max_value = int(max_int16 * test_tone_max_abs)
229 expected_max_value = int(max_int16 * test_tone_max_abs)
224 audio = display.Audio(test_tone, rate=44100, normalize=False)
230 audio = display.Audio(test_tone, rate=44100, normalize=False)
225 actual_max_value = numpy.max(numpy.abs(read_wav(audio.data)))
231 actual_max_value = numpy.max(numpy.abs(read_wav(audio.data)))
226 nt.assert_equal(actual_max_value, expected_max_value)
232 assert actual_max_value == expected_max_value
227
233
228 def test_audio_data_without_normalization_raises_for_invalid_data(self):
234 def test_audio_data_without_normalization_raises_for_invalid_data(self):
229 nt.assert_raises(
235 nt.assert_raises(
@@ -2,8 +2,6 b''
2 import sys
2 import sys
3 from unittest import mock
3 from unittest import mock
4
4
5 import nose.tools as nt
6
7 from IPython import get_ipython
5 from IPython import get_ipython
8 from IPython.lib import editorhooks
6 from IPython.lib import editorhooks
9
7
@@ -20,15 +18,15 b' def test_install_editor():'
20 with mock.patch('subprocess.Popen', fake_popen):
18 with mock.patch('subprocess.Popen', fake_popen):
21 get_ipython().hooks.editor('the file', 64)
19 get_ipython().hooks.editor('the file', 64)
22
20
23 nt.assert_equal(len(called), 1)
21 assert len(called) == 1
24 args = called[0]['args']
22 args = called[0]["args"]
25 kwargs = called[0]['kwargs']
23 kwargs = called[0]["kwargs"]
26
24
27 nt.assert_equal(kwargs, {'shell': True})
25 assert kwargs == {"shell": True}
28
26
29 if sys.platform.startswith('win'):
27 if sys.platform.startswith("win"):
30 expected = ['foo', '-l', '64', '-f', 'the file']
28 expected = ["foo", "-l", "64", "-f", "the file"]
31 else:
29 else:
32 expected = "foo -l 64 -f 'the file'"
30 expected = "foo -l 64 -f 'the file'"
33 cmd = args[0]
31 cmd = args[0]
34 nt.assert_equal(cmd, expected)
32 assert cmd == expected
@@ -25,7 +25,7 b' def test_check_latex_to_png_dvipng_fails_when_no_cmd(command):'
25 raise FindCmdError
25 raise FindCmdError
26
26
27 with patch.object(latextools, "find_cmd", mock_find_cmd):
27 with patch.object(latextools, "find_cmd", mock_find_cmd):
28 assert latextools.latex_to_png_dvipng("whatever", True) == None
28 assert latextools.latex_to_png_dvipng("whatever", True) is None
29
29
30
30
31 @contextmanager
31 @contextmanager
@@ -81,7 +81,7 b' def test_indentation():'
81 gotoutput = pretty.pretty(MyList(range(count)))
81 gotoutput = pretty.pretty(MyList(range(count)))
82 expectedoutput = "MyList(\n" + ",\n".join(" %d" % i for i in range(count)) + ")"
82 expectedoutput = "MyList(\n" + ",\n".join(" %d" % i for i in range(count)) + ")"
83
83
84 nt.assert_equal(gotoutput, expectedoutput)
84 assert gotoutput == expectedoutput
85
85
86
86
87 def test_dispatch():
87 def test_dispatch():
@@ -92,7 +92,7 b' def test_dispatch():'
92 gotoutput = pretty.pretty(MyDict())
92 gotoutput = pretty.pretty(MyDict())
93 expectedoutput = "MyDict(...)"
93 expectedoutput = "MyDict(...)"
94
94
95 nt.assert_equal(gotoutput, expectedoutput)
95 assert gotoutput == expectedoutput
96
96
97
97
98 def test_callability_checking():
98 def test_callability_checking():
@@ -103,7 +103,7 b' def test_callability_checking():'
103 gotoutput = pretty.pretty(Dummy2())
103 gotoutput = pretty.pretty(Dummy2())
104 expectedoutput = "Dummy1(...)"
104 expectedoutput = "Dummy1(...)"
105
105
106 nt.assert_equal(gotoutput, expectedoutput)
106 assert gotoutput == expectedoutput
107
107
108
108
109 @pytest.mark.parametrize(
109 @pytest.mark.parametrize(
@@ -135,7 +135,7 b' def test_sets(obj, expected_output):'
135 Test that set and frozenset use Python 3 formatting.
135 Test that set and frozenset use Python 3 formatting.
136 """
136 """
137 got_output = pretty.pretty(obj)
137 got_output = pretty.pretty(obj)
138 nt.assert_equal(got_output, expected_output)
138 assert got_output == expected_output
139
139
140
140
141 @skip_without('xxlimited')
141 @skip_without('xxlimited')
@@ -145,14 +145,16 b' def test_pprint_heap_allocated_type():'
145 """
145 """
146 import xxlimited
146 import xxlimited
147 output = pretty.pretty(xxlimited.Null)
147 output = pretty.pretty(xxlimited.Null)
148 nt.assert_equal(output, 'xxlimited.Null')
148 assert output == "xxlimited.Null"
149
149
150
150 def test_pprint_nomod():
151 def test_pprint_nomod():
151 """
152 """
152 Test that pprint works for classes with no __module__.
153 Test that pprint works for classes with no __module__.
153 """
154 """
154 output = pretty.pretty(NoModule)
155 output = pretty.pretty(NoModule)
155 nt.assert_equal(output, 'NoModule')
156 assert output == "NoModule"
157
156
158
157 def test_pprint_break():
159 def test_pprint_break():
158 """
160 """
@@ -160,7 +162,7 b' def test_pprint_break():'
160 """
162 """
161 output = pretty.pretty(Breaking())
163 output = pretty.pretty(Breaking())
162 expected = "TG: Breaking(\n ):"
164 expected = "TG: Breaking(\n ):"
163 nt.assert_equal(output, expected)
165 assert output == expected
164
166
165 def test_pprint_break_repr():
167 def test_pprint_break_repr():
166 """
168 """
@@ -168,11 +170,11 b' def test_pprint_break_repr():'
168 """
170 """
169 output = pretty.pretty([[BreakingRepr()]])
171 output = pretty.pretty([[BreakingRepr()]])
170 expected = "[[Breaking(\n )]]"
172 expected = "[[Breaking(\n )]]"
171 nt.assert_equal(output, expected)
173 assert output == expected
172
174
173 output = pretty.pretty([[BreakingRepr()]*2])
175 output = pretty.pretty([[BreakingRepr()]*2])
174 expected = "[[Breaking(\n ),\n Breaking(\n )]]"
176 expected = "[[Breaking(\n ),\n Breaking(\n )]]"
175 nt.assert_equal(output, expected)
177 assert output == expected
176
178
177 def test_bad_repr():
179 def test_bad_repr():
178 """Don't catch bad repr errors"""
180 """Don't catch bad repr errors"""
@@ -258,7 +260,7 b" ClassWithMeta = MetaClass('ClassWithMeta')"
258
260
259 def test_metaclass_repr():
261 def test_metaclass_repr():
260 output = pretty.pretty(ClassWithMeta)
262 output = pretty.pretty(ClassWithMeta)
261 nt.assert_equal(output, "[CUSTOM REPR FOR CLASS ClassWithMeta]")
263 assert output == "[CUSTOM REPR FOR CLASS ClassWithMeta]"
262
264
263
265
264 def test_unicode_repr():
266 def test_unicode_repr():
@@ -271,9 +273,9 b' def test_unicode_repr():'
271
273
272 c = C()
274 c = C()
273 p = pretty.pretty(c)
275 p = pretty.pretty(c)
274 nt.assert_equal(p, u)
276 assert p == u
275 p = pretty.pretty([c])
277 p = pretty.pretty([c])
276 nt.assert_equal(p, u'[%s]' % u)
278 assert p == u"[%s]" % u
277
279
278
280
279 def test_basic_class():
281 def test_basic_class():
@@ -290,10 +292,11 b' def test_basic_class():'
290 printer.flush()
292 printer.flush()
291 output = stream.getvalue()
293 output = stream.getvalue()
292
294
293 nt.assert_equal(output, '%s.MyObj' % __name__)
295 assert output == "%s.MyObj" % __name__
294 nt.assert_true(type_pprint_wrapper.called)
296 nt.assert_true(type_pprint_wrapper.called)
295
297
296
298
299 # TODO : pytest.mark.parametrise once nose is gone.
297 def test_collections_defaultdict():
300 def test_collections_defaultdict():
298 # Create defaultdicts with cycles
301 # Create defaultdicts with cycles
299 a = defaultdict()
302 a = defaultdict()
@@ -311,9 +314,10 b' def test_collections_defaultdict():'
311 (b, "defaultdict(list, {'key': defaultdict(...)})"),
314 (b, "defaultdict(list, {'key': defaultdict(...)})"),
312 ]
315 ]
313 for obj, expected in cases:
316 for obj, expected in cases:
314 nt.assert_equal(pretty.pretty(obj), expected)
317 assert pretty.pretty(obj) == expected
315
318
316
319
320 # TODO : pytest.mark.parametrise once nose is gone.
317 def test_collections_ordereddict():
321 def test_collections_ordereddict():
318 # Create OrderedDict with cycle
322 # Create OrderedDict with cycle
319 a = OrderedDict()
323 a = OrderedDict()
@@ -335,9 +339,10 b' def test_collections_ordereddict():'
335 (a, "OrderedDict([('key', OrderedDict(...))])"),
339 (a, "OrderedDict([('key', OrderedDict(...))])"),
336 ]
340 ]
337 for obj, expected in cases:
341 for obj, expected in cases:
338 nt.assert_equal(pretty.pretty(obj), expected)
342 assert pretty.pretty(obj) == expected
339
343
340
344
345 # TODO : pytest.mark.parametrise once nose is gone.
341 def test_collections_deque():
346 def test_collections_deque():
342 # Create deque with cycle
347 # Create deque with cycle
343 a = deque()
348 a = deque()
@@ -369,8 +374,10 b' def test_collections_deque():'
369 (a, 'deque([deque(...)])'),
374 (a, 'deque([deque(...)])'),
370 ]
375 ]
371 for obj, expected in cases:
376 for obj, expected in cases:
372 nt.assert_equal(pretty.pretty(obj), expected)
377 assert pretty.pretty(obj) == expected
373
378
379
380 # TODO : pytest.mark.parametrise once nose is gone.
374 def test_collections_counter():
381 def test_collections_counter():
375 class MyCounter(Counter):
382 class MyCounter(Counter):
376 pass
383 pass
@@ -380,8 +387,9 b' def test_collections_counter():'
380 (MyCounter(a=1), "MyCounter({'a': 1})"),
387 (MyCounter(a=1), "MyCounter({'a': 1})"),
381 ]
388 ]
382 for obj, expected in cases:
389 for obj, expected in cases:
383 nt.assert_equal(pretty.pretty(obj), expected)
390 assert pretty.pretty(obj) == expected
384
391
392 # TODO : pytest.mark.parametrise once nose is gone.
385 def test_mappingproxy():
393 def test_mappingproxy():
386 MP = types.MappingProxyType
394 MP = types.MappingProxyType
387 underlying_dict = {}
395 underlying_dict = {}
@@ -424,9 +432,10 b' def test_mappingproxy():'
424 "{2: mappingproxy({2: {...}, 3: {...}}), 3: {...}}"),
432 "{2: mappingproxy({2: {...}, 3: {...}}), 3: {...}}"),
425 ]
433 ]
426 for obj, expected in cases:
434 for obj, expected in cases:
427 nt.assert_equal(pretty.pretty(obj), expected)
435 assert pretty.pretty(obj) == expected
428
436
429
437
438 # TODO : pytest.mark.parametrise once nose is gone.
430 def test_simplenamespace():
439 def test_simplenamespace():
431 SN = types.SimpleNamespace
440 SN = types.SimpleNamespace
432
441
@@ -444,7 +453,7 b' def test_simplenamespace():'
444 (sn_recursive, "namespace(first=namespace(...), second=namespace(...))"),
453 (sn_recursive, "namespace(first=namespace(...), second=namespace(...))"),
445 ]
454 ]
446 for obj, expected in cases:
455 for obj, expected in cases:
447 nt.assert_equal(pretty.pretty(obj), expected)
456 assert pretty.pretty(obj) == expected
448
457
449
458
450 def test_pretty_environ():
459 def test_pretty_environ():
@@ -452,7 +461,7 b' def test_pretty_environ():'
452 # reindent to align with 'environ' prefix
461 # reindent to align with 'environ' prefix
453 dict_indented = dict_repr.replace('\n', '\n' + (' ' * len('environ')))
462 dict_indented = dict_repr.replace('\n', '\n' + (' ' * len('environ')))
454 env_repr = pretty.pretty(os.environ)
463 env_repr = pretty.pretty(os.environ)
455 nt.assert_equal(env_repr, 'environ' + dict_indented)
464 assert env_repr == "environ" + dict_indented
456
465
457
466
458 def test_function_pretty():
467 def test_function_pretty():
@@ -460,7 +469,8 b' def test_function_pretty():'
460 # posixpath is a pure python module, its interface is consistent
469 # posixpath is a pure python module, its interface is consistent
461 # across Python distributions
470 # across Python distributions
462 import posixpath
471 import posixpath
463 nt.assert_equal(pretty.pretty(posixpath.join), '<function posixpath.join(a, *p)>')
472
473 assert pretty.pretty(posixpath.join) == "<function posixpath.join(a, *p)>"
464
474
465 # custom function
475 # custom function
466 def meaning_of_life(question=None):
476 def meaning_of_life(question=None):
@@ -489,4 +499,4 b' def test_custom_repr():'
489 oc = OrderedCounter("abracadabra")
499 oc = OrderedCounter("abracadabra")
490 nt.assert_in("OrderedCounter(OrderedDict", pretty.pretty(oc))
500 nt.assert_in("OrderedCounter(OrderedDict", pretty.pretty(oc))
491
501
492 nt.assert_equal(pretty.pretty(MySet()), 'mine')
502 assert pretty.pretty(MySet()) == "mine"
@@ -1,24 +1,25 b''
1 # coding: utf-8
1 # coding: utf-8
2 from IPython.lib import passwd
2 from IPython.lib import passwd
3 from IPython.lib.security import passwd_check, salt_len
3 from IPython.lib.security import passwd_check, salt_len
4 import nose.tools as nt
5
4
6 def test_passwd_structure():
5 def test_passwd_structure():
7 p = passwd('passphrase')
6 p = passwd("passphrase")
8 algorithm, salt, hashed = p.split(':')
7 algorithm, salt, hashed = p.split(":")
9 nt.assert_equal(algorithm, 'sha1')
8 assert algorithm == "sha1"
10 nt.assert_equal(len(salt), salt_len)
9 assert len(salt) == salt_len
11 nt.assert_equal(len(hashed), 40)
10 assert len(hashed) == 40
12
11
13 def test_roundtrip():
12 def test_roundtrip():
14 p = passwd('passphrase')
13 p = passwd("passphrase")
15 nt.assert_equal(passwd_check(p, 'passphrase'), True)
14 assert passwd_check(p, "passphrase") is True
15
16
16
17 def test_bad():
17 def test_bad():
18 p = passwd('passphrase')
18 p = passwd('passphrase')
19 nt.assert_equal(passwd_check(p, p), False)
19 assert passwd_check(p, p) is False
20 nt.assert_equal(passwd_check(p, 'a:b:c:d'), False)
20 assert passwd_check(p, "a:b:c:d") is False
21 nt.assert_equal(passwd_check(p, 'a:b'), False)
21 assert passwd_check(p, "a:b") is False
22
22
23
23 def test_passwd_check_unicode():
24 def test_passwd_check_unicode():
24 # GH issue #4524
25 # GH issue #4524
@@ -200,6 +200,7 b' from io import StringIO'
200 # Third-party
200 # Third-party
201 from docutils.parsers.rst import directives
201 from docutils.parsers.rst import directives
202 from docutils.parsers.rst import Directive
202 from docutils.parsers.rst import Directive
203 from sphinx.util import logging
203
204
204 # Our own
205 # Our own
205 from traitlets.config import Config
206 from traitlets.config import Config
@@ -557,15 +558,20 b' class EmbeddedSphinxShell(object):'
557 filename = self.directive.state.document.current_source
558 filename = self.directive.state.document.current_source
558 lineno = self.directive.state.document.current_line
559 lineno = self.directive.state.document.current_line
559
560
561 # Use sphinx logger for warnings
562 logger = logging.getLogger(__name__)
563
560 # output any exceptions raised during execution to stdout
564 # output any exceptions raised during execution to stdout
561 # unless :okexcept: has been specified.
565 # unless :okexcept: has been specified.
562 if not is_okexcept and (("Traceback" in processed_output) or ("SyntaxError" in processed_output)):
566 if not is_okexcept and (
563 s = "\nException in %s at block ending on line %s\n" % (filename, lineno)
567 ("Traceback" in processed_output) or ("SyntaxError" in processed_output)
568 ):
569 s = "\n>>>" + ("-" * 73) + "\n"
570 s += "Exception in %s at block ending on line %s\n" % (filename, lineno)
564 s += "Specify :okexcept: as an option in the ipython:: block to suppress this message\n"
571 s += "Specify :okexcept: as an option in the ipython:: block to suppress this message\n"
565 sys.stdout.write('\n\n>>>' + ('-' * 73))
572 s += processed_output + "\n"
566 sys.stdout.write(s)
573 s += "<<<" + ("-" * 73)
567 sys.stdout.write(processed_output)
574 logger.warning(s)
568 sys.stdout.write('<<<' + ('-' * 73) + '\n\n')
569 if self.warning_is_error:
575 if self.warning_is_error:
570 raise RuntimeError('Non Expected exception in `{}` line {}'.format(filename, lineno))
576 raise RuntimeError('Non Expected exception in `{}` line {}'.format(filename, lineno))
571
577
@@ -573,15 +579,15 b' class EmbeddedSphinxShell(object):'
573 # unless :okwarning: has been specified.
579 # unless :okwarning: has been specified.
574 if not is_okwarning:
580 if not is_okwarning:
575 for w in ws:
581 for w in ws:
576 s = "\nWarning in %s at block ending on line %s\n" % (filename, lineno)
582 s = "\n>>>" + ("-" * 73) + "\n"
583 s += "Warning in %s at block ending on line %s\n" % (filename, lineno)
577 s += "Specify :okwarning: as an option in the ipython:: block to suppress this message\n"
584 s += "Specify :okwarning: as an option in the ipython:: block to suppress this message\n"
578 sys.stdout.write('\n\n>>>' + ('-' * 73))
585 s += ("-" * 76) + "\n"
579 sys.stdout.write(s)
586 s += warnings.formatwarning(
580 sys.stdout.write(('-' * 76) + '\n')
587 w.message, w.category, w.filename, w.lineno, w.line
581 s=warnings.formatwarning(w.message, w.category,
588 )
582 w.filename, w.lineno, w.line)
589 s += "<<<" + ("-" * 73)
583 sys.stdout.write(s)
590 logger.warning(s)
584 sys.stdout.write('<<<' + ('-' * 73) + '\n')
585 if self.warning_is_error:
591 if self.warning_is_error:
586 raise RuntimeError('Non Expected warning in `{}` line {}'.format(filename, lineno))
592 raise RuntimeError('Non Expected warning in `{}` line {}'.format(filename, lineno))
587
593
@@ -1002,6 +1008,9 b' class IPythonDirective(Directive):'
1002 lines = ['.. code-block:: ipython', '']
1008 lines = ['.. code-block:: ipython', '']
1003 figures = []
1009 figures = []
1004
1010
1011 # Use sphinx logger for warnings
1012 logger = logging.getLogger(__name__)
1013
1005 for part in parts:
1014 for part in parts:
1006 block = block_parser(part, rgxin, rgxout, promptin, promptout)
1015 block = block_parser(part, rgxin, rgxout, promptin, promptout)
1007 if len(block):
1016 if len(block):
@@ -1020,7 +1029,7 b' class IPythonDirective(Directive):'
1020 if self.shell.warning_is_error:
1029 if self.shell.warning_is_error:
1021 raise RuntimeError(message)
1030 raise RuntimeError(message)
1022 else:
1031 else:
1023 warnings.warn(message)
1032 logger.warning(message)
1024
1033
1025 for figure in figures:
1034 for figure in figures:
1026 lines.append('')
1035 lines.append('')
@@ -7,6 +7,7 b' from IPython.core.debugger import Pdb'
7 from IPython.core.completer import IPCompleter
7 from IPython.core.completer import IPCompleter
8 from .ptutils import IPythonPTCompleter
8 from .ptutils import IPythonPTCompleter
9 from .shortcuts import create_ipython_shortcuts
9 from .shortcuts import create_ipython_shortcuts
10 from . import embed
10
11
11 from pygments.token import Token
12 from pygments.token import Token
12 from prompt_toolkit.shortcuts.prompt import PromptSession
13 from prompt_toolkit.shortcuts.prompt import PromptSession
@@ -131,6 +132,18 b' class TerminalPdb(Pdb):'
131 except Exception:
132 except Exception:
132 raise
133 raise
133
134
135 def do_interact(self, arg):
136 ipshell = embed.InteractiveShellEmbed(
137 config=self.shell.config,
138 banner1="*interactive*",
139 exit_msg="*exiting interactive console...*",
140 )
141 global_ns = self.curframe.f_globals
142 ipshell(
143 module=sys.modules.get(global_ns["__name__"], None),
144 local_ns=self.curframe_locals,
145 )
146
134
147
135 def set_trace(frame=None):
148 def set_trace(frame=None):
136 """
149 """
@@ -148,6 +161,6 b" if __name__ == '__main__':"
148 # happened after hitting "c", this is needed in order to
161 # happened after hitting "c", this is needed in order to
149 # be able to quit the debugging session (see #9950).
162 # be able to quit the debugging session (see #9950).
150 old_trace_dispatch = pdb.Pdb.trace_dispatch
163 old_trace_dispatch = pdb.Pdb.trace_dispatch
151 pdb.Pdb = TerminalPdb
164 pdb.Pdb = TerminalPdb # type: ignore
152 pdb.Pdb.trace_dispatch = old_trace_dispatch
165 pdb.Pdb.trace_dispatch = old_trace_dispatch # type: ignore
153 pdb.main()
166 pdb.main()
@@ -19,6 +19,8 b' from IPython.terminal.ipapp import load_default_config'
19 from traitlets import Bool, CBool, Unicode
19 from traitlets import Bool, CBool, Unicode
20 from IPython.utils.io import ask_yes_no
20 from IPython.utils.io import ask_yes_no
21
21
22 from typing import Set
23
22 class KillEmbedded(Exception):pass
24 class KillEmbedded(Exception):pass
23
25
24 # kept for backward compatibility as IPython 6 was released with
26 # kept for backward compatibility as IPython 6 was released with
@@ -123,17 +125,17 b' class InteractiveShellEmbed(TerminalInteractiveShell):'
123 help="Automatically set the terminal title"
125 help="Automatically set the terminal title"
124 ).tag(config=True)
126 ).tag(config=True)
125
127
126 _inactive_locations = set()
128 _inactive_locations: Set[str] = set()
129
130 def _disable_init_location(self):
131 """Disable the current Instance creation location"""
132 InteractiveShellEmbed._inactive_locations.add(self._init_location_id)
127
133
128 @property
134 @property
129 def embedded_active(self):
135 def embedded_active(self):
130 return (self._call_location_id not in InteractiveShellEmbed._inactive_locations)\
136 return (self._call_location_id not in InteractiveShellEmbed._inactive_locations)\
131 and (self._init_location_id not in InteractiveShellEmbed._inactive_locations)
137 and (self._init_location_id not in InteractiveShellEmbed._inactive_locations)
132
138
133 def _disable_init_location(self):
134 """Disable the current Instance creation location"""
135 InteractiveShellEmbed._inactive_locations.add(self._init_location_id)
136
137 @embedded_active.setter
139 @embedded_active.setter
138 def embedded_active(self, value):
140 def embedded_active(self, value):
139 if value:
141 if value:
@@ -334,7 +336,7 b' class InteractiveShellEmbed(TerminalInteractiveShell):'
334 self.compile.flags = orig_compile_flags
336 self.compile.flags = orig_compile_flags
335
337
336
338
337 def embed(**kwargs):
339 def embed(*, header="", compile_flags=None, **kwargs):
338 """Call this to embed IPython at the current point in your program.
340 """Call this to embed IPython at the current point in your program.
339
341
340 The first invocation of this will create an :class:`InteractiveShellEmbed`
342 The first invocation of this will create an :class:`InteractiveShellEmbed`
@@ -360,8 +362,6 b' def embed(**kwargs):'
360 config argument.
362 config argument.
361 """
363 """
362 config = kwargs.get('config')
364 config = kwargs.get('config')
363 header = kwargs.pop('header', u'')
364 compile_flags = kwargs.pop('compile_flags', None)
365 if config is None:
365 if config is None:
366 config = load_default_config()
366 config = load_default_config()
367 config.InteractiveShellEmbed = config.TerminalInteractiveShell
367 config.InteractiveShellEmbed = config.TerminalInteractiveShell
@@ -204,9 +204,8 b' class TerminalInteractiveShell(InteractiveShell):'
204
204
205 @observe('editing_mode')
205 @observe('editing_mode')
206 def _editing_mode(self, change):
206 def _editing_mode(self, change):
207 u_mode = change.new.upper()
208 if self.pt_app:
207 if self.pt_app:
209 self.pt_app.editing_mode = u_mode
208 self.pt_app.editing_mode = getattr(EditingMode, change.new.upper())
210
209
211 @observe('autoformatter')
210 @observe('autoformatter')
212 def _autoformatter_changed(self, change):
211 def _autoformatter_changed(self, change):
@@ -615,6 +614,13 b' class TerminalInteractiveShell(InteractiveShell):'
615
614
616 self.restore_term_title()
615 self.restore_term_title()
617
616
617 # try to call some at-exit operation optimistically as some things can't
618 # be done during interpreter shutdown. this is technically inaccurate as
619 # this make mainlool not re-callable, but that should be a rare if not
620 # in existent use case.
621
622 self._atexit_once()
623
618
624
619 _inputhook = None
625 _inputhook = None
620 def inputhook(self, context):
626 def inputhook(self, context):
@@ -7,13 +7,20 b' aliases = {'
7 }
7 }
8
8
9 backends = [
9 backends = [
10 'qt', 'qt4', 'qt5',
10 "qt",
11 'gtk', 'gtk2', 'gtk3',
11 "qt4",
12 'tk',
12 "qt5",
13 'wx',
13 "qt6",
14 'pyglet', 'glut',
14 "gtk",
15 'osx',
15 "gtk2",
16 'asyncio'
16 "gtk3",
17 "gtk4",
18 "tk",
19 "wx",
20 "pyglet",
21 "glut",
22 "osx",
23 "asyncio",
17 ]
24 ]
18
25
19 registered = {}
26 registered = {}
@@ -22,6 +29,7 b' def register(name, inputhook):'
22 """Register the function *inputhook* as an event loop integration."""
29 """Register the function *inputhook* as an event loop integration."""
23 registered[name] = inputhook
30 registered[name] = inputhook
24
31
32
25 class UnknownBackend(KeyError):
33 class UnknownBackend(KeyError):
26 def __init__(self, name):
34 def __init__(self, name):
27 self.name = name
35 self.name = name
@@ -31,6 +39,7 b' class UnknownBackend(KeyError):'
31 "Supported event loops are: {}").format(self.name,
39 "Supported event loops are: {}").format(self.name,
32 ', '.join(backends + sorted(registered)))
40 ', '.join(backends + sorted(registered)))
33
41
42
34 def get_inputhook_name_and_func(gui):
43 def get_inputhook_name_and_func(gui):
35 if gui in registered:
44 if gui in registered:
36 return gui, registered[gui]
45 return gui, registered[gui]
@@ -42,9 +51,12 b' def get_inputhook_name_and_func(gui):'
42 return get_inputhook_name_and_func(aliases[gui])
51 return get_inputhook_name_and_func(aliases[gui])
43
52
44 gui_mod = gui
53 gui_mod = gui
45 if gui == 'qt5':
54 if gui == "qt5":
46 os.environ['QT_API'] = 'pyqt5'
55 os.environ["QT_API"] = "pyqt5"
47 gui_mod = 'qt'
56 gui_mod = "qt"
57 elif gui == "qt6":
58 os.environ["QT_API"] = "pyqt6"
59 gui_mod = "qt"
48
60
49 mod = importlib.import_module('IPython.terminal.pt_inputhooks.'+gui_mod)
61 mod = importlib.import_module('IPython.terminal.pt_inputhooks.'+gui_mod)
50 return gui, mod.inputhook
62 return gui, mod.inputhook
@@ -9,7 +9,7 b' import ctypes'
9 import ctypes.util
9 import ctypes.util
10 from threading import Event
10 from threading import Event
11
11
12 objc = ctypes.cdll.LoadLibrary(ctypes.util.find_library('objc'))
12 objc = ctypes.cdll.LoadLibrary(ctypes.util.find_library("objc")) # type: ignore
13
13
14 void_p = ctypes.c_void_p
14 void_p = ctypes.c_void_p
15
15
@@ -37,7 +37,7 b' def C(classname):'
37 # end obj-c boilerplate from appnope
37 # end obj-c boilerplate from appnope
38
38
39 # CoreFoundation C-API calls we will use:
39 # CoreFoundation C-API calls we will use:
40 CoreFoundation = ctypes.cdll.LoadLibrary(ctypes.util.find_library('CoreFoundation'))
40 CoreFoundation = ctypes.cdll.LoadLibrary(ctypes.util.find_library("CoreFoundation")) # type: ignore
41
41
42 CFFileDescriptorCreate = CoreFoundation.CFFileDescriptorCreate
42 CFFileDescriptorCreate = CoreFoundation.CFFileDescriptorCreate
43 CFFileDescriptorCreate.restype = void_p
43 CFFileDescriptorCreate.restype = void_p
@@ -1,6 +1,6 b''
1 import sys
1 import sys
2 import os
2 import os
3 from IPython.external.qt_for_kernel import QtCore, QtGui
3 from IPython.external.qt_for_kernel import QtCore, QtGui, enum_helper
4 from IPython import get_ipython
4 from IPython import get_ipython
5
5
6 # If we create a QApplication, keep a reference to it so that it doesn't get
6 # If we create a QApplication, keep a reference to it so that it doesn't get
@@ -9,6 +9,11 b' _appref = None'
9 _already_warned = False
9 _already_warned = False
10
10
11
11
12 def _exec(obj):
13 # exec on PyQt6, exec_ elsewhere.
14 obj.exec() if hasattr(obj, "exec") else obj.exec_()
15
16
12 def _reclaim_excepthook():
17 def _reclaim_excepthook():
13 shell = get_ipython()
18 shell = get_ipython()
14 if shell is not None:
19 if shell is not None:
@@ -32,7 +37,16 b' def inputhook(context):'
32 'variable. Deactivate Qt5 code.'
37 'variable. Deactivate Qt5 code.'
33 )
38 )
34 return
39 return
35 QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
40 try:
41 QtCore.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
42 except AttributeError: # Only for Qt>=5.6, <6.
43 pass
44 try:
45 QtCore.QApplication.setHighDpiScaleFactorRoundingPolicy(
46 QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough
47 )
48 except AttributeError: # Only for Qt>=5.14.
49 pass
36 _appref = app = QtGui.QApplication([" "])
50 _appref = app = QtGui.QApplication([" "])
37
51
38 # "reclaim" IPython sys.excepthook after event loop starts
52 # "reclaim" IPython sys.excepthook after event loop starts
@@ -55,8 +69,9 b' def inputhook(context):'
55 else:
69 else:
56 # On POSIX platforms, we can use a file descriptor to quit the event
70 # On POSIX platforms, we can use a file descriptor to quit the event
57 # loop when there is input ready to read.
71 # loop when there is input ready to read.
58 notifier = QtCore.QSocketNotifier(context.fileno(),
72 notifier = QtCore.QSocketNotifier(
59 QtCore.QSocketNotifier.Read)
73 context.fileno(), enum_helper("QtCore.QSocketNotifier.Type").Read
74 )
60 try:
75 try:
61 # connect the callback we care about before we turn it on
76 # connect the callback we care about before we turn it on
62 # lambda is necessary as PyQT inspect the function signature to know
77 # lambda is necessary as PyQT inspect the function signature to know
@@ -65,6 +80,6 b' def inputhook(context):'
65 notifier.setEnabled(True)
80 notifier.setEnabled(True)
66 # only start the event loop we are not already flipped
81 # only start the event loop we are not already flipped
67 if not context.input_is_ready():
82 if not context.input_is_ready():
68 event_loop.exec_()
83 _exec(event_loop)
69 finally:
84 finally:
70 notifier.setEnabled(False)
85 notifier.setEnabled(False)
@@ -52,10 +52,8 b' def process_handler(cmd, callback, stderr=subprocess.PIPE):'
52 A command to be executed by the system, using :class:`subprocess.Popen`.
52 A command to be executed by the system, using :class:`subprocess.Popen`.
53 If a string is passed, it will be run in the system shell. If a list is
53 If a string is passed, it will be run in the system shell. If a list is
54 passed, it will be used directly as arguments.
54 passed, it will be used directly as arguments.
55
56 callback : callable
55 callback : callable
57 A one-argument function that will be called with the Popen object.
56 A one-argument function that will be called with the Popen object.
58
59 stderr : file descriptor number, optional
57 stderr : file descriptor number, optional
60 By default this is set to ``subprocess.PIPE``, but you can also pass the
58 By default this is set to ``subprocess.PIPE``, but you can also pass the
61 value ``subprocess.STDOUT`` to force the subprocess' stderr to go into
59 value ``subprocess.STDOUT`` to force the subprocess' stderr to go into
1 NO CONTENT: modified file
NO CONTENT: modified file
1 NO CONTENT: modified file
NO CONTENT: modified file
1 NO CONTENT: modified file
NO CONTENT: modified file
1 NO CONTENT: modified file
NO CONTENT: modified file
1 NO CONTENT: modified file
NO CONTENT: modified file
@@ -28,12 +28,10 b' def extract_vars(*names,**kw):'
28 *names : str
28 *names : str
29 One or more variable names which will be extracted from the caller's
29 One or more variable names which will be extracted from the caller's
30 frame.
30 frame.
31
31 **kw : integer, optional
32 depth : integer, optional
33 How many frames in the stack to walk when looking for your variables.
32 How many frames in the stack to walk when looking for your variables.
34 The default is 0, which will use the frame where the call was made.
33 The default is 0, which will use the frame where the call was made.
35
34
36
37 Examples
35 Examples
38 --------
36 --------
39 ::
37 ::
@@ -22,7 +22,6 b' def complete_object(obj, prev_completions):'
22 The object to complete.
22 The object to complete.
23 prev_completions : list
23 prev_completions : list
24 List of attributes discovered so far.
24 List of attributes discovered so far.
25
26 This should return the list of attributes in obj. If you only wish to
25 This should return the list of attributes in obj. If you only wish to
27 add to the attributes already discovered normally, return
26 add to the attributes already discovered normally, return
28 own_attrs + prev_completions.
27 own_attrs + prev_completions.
1 NO CONTENT: modified file
NO CONTENT: modified file
@@ -114,10 +114,8 b' class Tee(object):'
114 ----------
114 ----------
115 file_or_name : filename or open filehandle (writable)
115 file_or_name : filename or open filehandle (writable)
116 File that will be duplicated
116 File that will be duplicated
117
118 mode : optional, valid mode for open().
117 mode : optional, valid mode for open().
119 If a filename was give, open with this mode.
118 If a filename was give, open with this mode.
120
121 channel : str, one of ['stdout', 'stderr']
119 channel : str, one of ['stdout', 'stderr']
122 """
120 """
123 if channel not in ['stdout', 'stderr']:
121 if channel not in ['stdout', 'stderr']:
@@ -196,7 +194,6 b" def temp_pyfile(src, ext='.py'):"
196 ----------
194 ----------
197 src : string or list of strings (no need for ending newlines if list)
195 src : string or list of strings (no need for ending newlines if list)
198 Source code to be written to the file.
196 Source code to be written to the file.
199
200 ext : optional, string
197 ext : optional, string
201 Extension for the generated file.
198 Extension for the generated file.
202
199
@@ -43,14 +43,13 b' class Struct(dict):'
43
43
44 Parameters
44 Parameters
45 ----------
45 ----------
46 args : dict, Struct
46 *args : dict, Struct
47 Initialize with one dict or Struct
47 Initialize with one dict or Struct
48 kw : dict
48 **kw : dict
49 Initialize with key, value pairs.
49 Initialize with key, value pairs.
50
50
51 Examples
51 Examples
52 --------
52 --------
53
54 >>> s = Struct(a=10,b=30)
53 >>> s = Struct(a=10,b=30)
55 >>> s.a
54 >>> s.a
56 10
55 10
@@ -68,7 +67,6 b' class Struct(dict):'
68
67
69 Examples
68 Examples
70 --------
69 --------
71
72 >>> s = Struct()
70 >>> s = Struct()
73 >>> s['a'] = 10
71 >>> s['a'] = 10
74 >>> s.allow_new_attr(False)
72 >>> s.allow_new_attr(False)
@@ -95,7 +93,6 b' class Struct(dict):'
95
93
96 Examples
94 Examples
97 --------
95 --------
98
99 >>> s = Struct()
96 >>> s = Struct()
100 >>> s.a = 10
97 >>> s.a = 10
101 >>> s.a
98 >>> s.a
@@ -130,7 +127,6 b' class Struct(dict):'
130
127
131 Examples
128 Examples
132 --------
129 --------
133
134 >>> s = Struct(a=10)
130 >>> s = Struct(a=10)
135 >>> s.a
131 >>> s.a
136 10
132 10
@@ -155,7 +151,6 b' class Struct(dict):'
155
151
156 Examples
152 Examples
157 --------
153 --------
158
159 >>> s = Struct(a=10,b=30)
154 >>> s = Struct(a=10,b=30)
160 >>> s2 = Struct(a=20,c=40)
155 >>> s2 = Struct(a=20,c=40)
161 >>> s += s2
156 >>> s += s2
@@ -170,7 +165,6 b' class Struct(dict):'
170
165
171 Examples
166 Examples
172 --------
167 --------
173
174 >>> s1 = Struct(a=10,b=30)
168 >>> s1 = Struct(a=10,b=30)
175 >>> s2 = Struct(a=20,c=40)
169 >>> s2 = Struct(a=20,c=40)
176 >>> s = s1 + s2
170 >>> s = s1 + s2
@@ -186,7 +180,6 b' class Struct(dict):'
186
180
187 Examples
181 Examples
188 --------
182 --------
189
190 >>> s1 = Struct(a=10,b=30)
183 >>> s1 = Struct(a=10,b=30)
191 >>> s2 = Struct(a=40)
184 >>> s2 = Struct(a=40)
192 >>> s = s1 - s2
185 >>> s = s1 - s2
@@ -202,7 +195,6 b' class Struct(dict):'
202
195
203 Examples
196 Examples
204 --------
197 --------
205
206 >>> s1 = Struct(a=10,b=30)
198 >>> s1 = Struct(a=10,b=30)
207 >>> s2 = Struct(a=40)
199 >>> s2 = Struct(a=40)
208 >>> s1 -= s2
200 >>> s1 -= s2
@@ -236,7 +228,6 b' class Struct(dict):'
236
228
237 Examples
229 Examples
238 --------
230 --------
239
240 >>> s = Struct(a=10,b=30)
231 >>> s = Struct(a=10,b=30)
241 >>> s2 = s.copy()
232 >>> s2 = s.copy()
242 >>> type(s2) is Struct
233 >>> type(s2) is Struct
@@ -251,7 +242,6 b' class Struct(dict):'
251
242
252 Examples
243 Examples
253 --------
244 --------
254
255 >>> s = Struct(a=10)
245 >>> s = Struct(a=10)
256 >>> s.hasattr('a')
246 >>> s.hasattr('a')
257 True
247 True
@@ -284,7 +274,7 b' class Struct(dict):'
284
274
285 Parameters
275 Parameters
286 ----------
276 ----------
287 __loc_data : dict, Struct
277 __loc_data__ : dict, Struct
288 The data to merge into self
278 The data to merge into self
289 __conflict_solve : dict
279 __conflict_solve : dict
290 The conflict policy dict. The keys are binary functions used to
280 The conflict policy dict. The keys are binary functions used to
@@ -292,12 +282,11 b' class Struct(dict):'
292 the keys the conflict resolution function applies to. Instead of
282 the keys the conflict resolution function applies to. Instead of
293 a list of strings a space separated string can be used, like
283 a list of strings a space separated string can be used, like
294 'a b c'.
284 'a b c'.
295 kw : dict
285 **kw : dict
296 Additional key, value pairs to merge in
286 Additional key, value pairs to merge in
297
287
298 Notes
288 Notes
299 -----
289 -----
300
301 The `__conflict_solve` dict is a dictionary of binary functions which will be used to
290 The `__conflict_solve` dict is a dictionary of binary functions which will be used to
302 solve key conflicts. Here is an example::
291 solve key conflicts. Here is an example::
303
292
@@ -338,7 +327,6 b' class Struct(dict):'
338
327
339 Examples
328 Examples
340 --------
329 --------
341
342 This show the default policy:
330 This show the default policy:
343
331
344 >>> s = Struct(a=10,b=30)
332 >>> s = Struct(a=10,b=30)
1 NO CONTENT: modified file
NO CONTENT: modified file
1 NO CONTENT: modified file
NO CONTENT: modified file
@@ -109,7 +109,7 b' def get_py_filename(name, force_win32=None):'
109 raise IOError('File `%r` not found.' % name)
109 raise IOError('File `%r` not found.' % name)
110
110
111
111
112 def filefind(filename, path_dirs=None):
112 def filefind(filename: str, path_dirs=None) -> str:
113 """Find a file by looking through a sequence of paths.
113 """Find a file by looking through a sequence of paths.
114
114
115 This iterates through a sequence of paths looking for a file and returns
115 This iterates through a sequence of paths looking for a file and returns
@@ -139,7 +139,12 b' def filefind(filename, path_dirs=None):'
139
139
140 Returns
140 Returns
141 -------
141 -------
142 Raises :exc:`IOError` or returns absolute path to file.
142 path : str
143 returns absolute path to file.
144
145 Raises
146 ------
147 IOError
143 """
148 """
144
149
145 # If paths are quoted, abspath gets confused, strip them...
150 # If paths are quoted, abspath gets confused, strip them...
@@ -178,7 +183,6 b' def get_home_dir(require_writable=False) -> str:'
178
183
179 Parameters
184 Parameters
180 ----------
185 ----------
181
182 require_writable : bool [default: False]
186 require_writable : bool [default: False]
183 if True:
187 if True:
184 guarantees the return value is a writable directory, otherwise
188 guarantees the return value is a writable directory, otherwise
1 NO CONTENT: modified file
NO CONTENT: modified file
1 NO CONTENT: modified file
NO CONTENT: modified file
@@ -15,7 +15,6 b''
15
15
16 import sys
16 import sys
17
17
18 import nose.tools as nt
19 import pytest
18 import pytest
20
19
21 from IPython.testing.decorators import skip_iptest_but_not_pytest
20 from IPython.testing.decorators import skip_iptest_but_not_pytest
@@ -75,18 +74,18 b' def test_rich_output_empty(method_mime):'
75 """RichOutput with no args"""
74 """RichOutput with no args"""
76 rich = capture.RichOutput()
75 rich = capture.RichOutput()
77 method, mime = method_mime
76 method, mime = method_mime
78 nt.assert_equal(getattr(rich, method)(), None)
77 assert getattr(rich, method)() is None
79
78
80 def test_rich_output():
79 def test_rich_output():
81 """test RichOutput basics"""
80 """test RichOutput basics"""
82 data = basic_data
81 data = basic_data
83 metadata = basic_metadata
82 metadata = basic_metadata
84 rich = capture.RichOutput(data=data, metadata=metadata)
83 rich = capture.RichOutput(data=data, metadata=metadata)
85 nt.assert_equal(rich._repr_html_(), data["text/html"])
84 assert rich._repr_html_() == data["text/html"]
86 nt.assert_equal(rich._repr_png_(), (data["image/png"], metadata["image/png"]))
85 assert rich._repr_png_() == (data["image/png"], metadata["image/png"])
87 nt.assert_equal(rich._repr_latex_(), None)
86 assert rich._repr_latex_() is None
88 nt.assert_equal(rich._repr_javascript_(), None)
87 assert rich._repr_javascript_() is None
89 nt.assert_equal(rich._repr_svg_(), None)
88 assert rich._repr_svg_() is None
90
89
91
90
92 @skip_iptest_but_not_pytest
91 @skip_iptest_but_not_pytest
@@ -96,7 +95,7 b' def test_rich_output_no_metadata(method_mime):'
96 data = full_data
95 data = full_data
97 rich = capture.RichOutput(data=data)
96 rich = capture.RichOutput(data=data)
98 method, mime = method_mime
97 method, mime = method_mime
99 nt.assert_equal(getattr(rich, method)(), data[mime])
98 assert getattr(rich, method)() == data[mime]
100
99
101
100
102 @skip_iptest_but_not_pytest
101 @skip_iptest_but_not_pytest
@@ -107,7 +106,7 b' def test_rich_output_metadata(method_mime):'
107 metadata = full_metadata
106 metadata = full_metadata
108 rich = capture.RichOutput(data=data, metadata=metadata)
107 rich = capture.RichOutput(data=data, metadata=metadata)
109 method, mime = method_mime
108 method, mime = method_mime
110 nt.assert_equal(getattr(rich, method)(), (data[mime], metadata[mime]))
109 assert getattr(rich, method)() == (data[mime], metadata[mime])
111
110
112 def test_rich_output_display():
111 def test_rich_output_display():
113 """test RichOutput.display
112 """test RichOutput.display
@@ -119,10 +118,10 b' def test_rich_output_display():'
119 rich = capture.RichOutput(data=data)
118 rich = capture.RichOutput(data=data)
120 with capture.capture_output() as cap:
119 with capture.capture_output() as cap:
121 rich.display()
120 rich.display()
122 nt.assert_equal(len(cap.outputs), 1)
121 assert len(cap.outputs) == 1
123 rich2 = cap.outputs[0]
122 rich2 = cap.outputs[0]
124 nt.assert_equal(rich2.data, rich.data)
123 assert rich2.data == rich.data
125 nt.assert_equal(rich2.metadata, rich.metadata)
124 assert rich2.metadata == rich.metadata
126
125
127 def test_capture_output():
126 def test_capture_output():
128 """capture_output works"""
127 """capture_output works"""
@@ -131,8 +130,8 b' def test_capture_output():'
131 print(hello_stdout, end="")
130 print(hello_stdout, end="")
132 print(hello_stderr, end="", file=sys.stderr)
131 print(hello_stderr, end="", file=sys.stderr)
133 rich.display()
132 rich.display()
134 nt.assert_equal(hello_stdout, cap.stdout)
133 assert hello_stdout == cap.stdout
135 nt.assert_equal(hello_stderr, cap.stderr)
134 assert hello_stderr == cap.stderr
136
135
137
136
138 def test_capture_output_no_stdout():
137 def test_capture_output_no_stdout():
@@ -142,9 +141,9 b' def test_capture_output_no_stdout():'
142 print(hello_stdout, end="")
141 print(hello_stdout, end="")
143 print(hello_stderr, end="", file=sys.stderr)
142 print(hello_stderr, end="", file=sys.stderr)
144 rich.display()
143 rich.display()
145 nt.assert_equal("", cap.stdout)
144 assert "" == cap.stdout
146 nt.assert_equal(hello_stderr, cap.stderr)
145 assert hello_stderr == cap.stderr
147 nt.assert_equal(len(cap.outputs), 1)
146 assert len(cap.outputs) == 1
148
147
149
148
150 def test_capture_output_no_stderr():
149 def test_capture_output_no_stderr():
@@ -155,9 +154,9 b' def test_capture_output_no_stderr():'
155 print(hello_stdout, end="")
154 print(hello_stdout, end="")
156 print(hello_stderr, end="", file=sys.stderr)
155 print(hello_stderr, end="", file=sys.stderr)
157 rich.display()
156 rich.display()
158 nt.assert_equal(hello_stdout, cap.stdout)
157 assert hello_stdout == cap.stdout
159 nt.assert_equal("", cap.stderr)
158 assert "" == cap.stderr
160 nt.assert_equal(len(cap.outputs), 1)
159 assert len(cap.outputs) == 1
161
160
162
161
163 def test_capture_output_no_display():
162 def test_capture_output_no_display():
@@ -167,6 +166,6 b' def test_capture_output_no_display():'
167 print(hello_stdout, end="")
166 print(hello_stdout, end="")
168 print(hello_stderr, end="", file=sys.stderr)
167 print(hello_stderr, end="", file=sys.stderr)
169 rich.display()
168 rich.display()
170 nt.assert_equal(hello_stdout, cap.stdout)
169 assert hello_stdout == cap.stdout
171 nt.assert_equal(hello_stderr, cap.stderr)
170 assert hello_stderr == cap.stderr
172 nt.assert_equal(cap.outputs, [])
171 assert cap.outputs == []
1 NO CONTENT: modified file
NO CONTENT: modified file
@@ -113,7 +113,7 b' def test_get_home_dir_1():'
113 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py"))
113 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py"))
114
114
115 home_dir = path.get_home_dir()
115 home_dir = path.get_home_dir()
116 nt.assert_equal(home_dir, unfrozen)
116 assert home_dir == unfrozen
117
117
118
118
119 @skip_if_not_win32
119 @skip_if_not_win32
@@ -127,7 +127,7 b' def test_get_home_dir_2():'
127 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower()
127 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower()
128
128
129 home_dir = path.get_home_dir(True)
129 home_dir = path.get_home_dir(True)
130 nt.assert_equal(home_dir, unfrozen)
130 assert home_dir == unfrozen
131
131
132
132
133 @skip_win32_py38
133 @skip_win32_py38
@@ -137,7 +137,7 b' def test_get_home_dir_3():'
137 env["HOME"] = HOME_TEST_DIR
137 env["HOME"] = HOME_TEST_DIR
138 home_dir = path.get_home_dir(True)
138 home_dir = path.get_home_dir(True)
139 # get_home_dir expands symlinks
139 # get_home_dir expands symlinks
140 nt.assert_equal(home_dir, os.path.realpath(env["HOME"]))
140 assert home_dir == os.path.realpath(env["HOME"])
141
141
142
142
143 @with_environment
143 @with_environment
@@ -181,7 +181,7 b' def test_get_home_dir_8():'
181 with patch.object(wreg, 'OpenKey', return_value=key()), \
181 with patch.object(wreg, 'OpenKey', return_value=key()), \
182 patch.object(wreg, 'QueryValueEx', return_value=[abspath(HOME_TEST_DIR)]):
182 patch.object(wreg, 'QueryValueEx', return_value=[abspath(HOME_TEST_DIR)]):
183 home_dir = path.get_home_dir()
183 home_dir = path.get_home_dir()
184 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
184 assert home_dir == abspath(HOME_TEST_DIR)
185
185
186 @with_environment
186 @with_environment
187 def test_get_xdg_dir_0():
187 def test_get_xdg_dir_0():
@@ -195,7 +195,7 b' def test_get_xdg_dir_0():'
195 env.pop('IPYTHONDIR', None)
195 env.pop('IPYTHONDIR', None)
196 env.pop('XDG_CONFIG_HOME', None)
196 env.pop('XDG_CONFIG_HOME', None)
197
197
198 nt.assert_equal(path.get_xdg_dir(), os.path.join('somewhere', '.config'))
198 assert path.get_xdg_dir() == os.path.join("somewhere", ".config")
199
199
200
200
201 @with_environment
201 @with_environment
@@ -208,7 +208,7 b' def test_get_xdg_dir_1():'
208 env.pop('IPYTHON_DIR', None)
208 env.pop('IPYTHON_DIR', None)
209 env.pop('IPYTHONDIR', None)
209 env.pop('IPYTHONDIR', None)
210 env.pop('XDG_CONFIG_HOME', None)
210 env.pop('XDG_CONFIG_HOME', None)
211 nt.assert_equal(path.get_xdg_dir(), None)
211 assert path.get_xdg_dir() is None
212
212
213 @with_environment
213 @with_environment
214 def test_get_xdg_dir_2():
214 def test_get_xdg_dir_2():
@@ -224,7 +224,7 b' def test_get_xdg_dir_2():'
224 if not os.path.exists(cfgdir):
224 if not os.path.exists(cfgdir):
225 os.makedirs(cfgdir)
225 os.makedirs(cfgdir)
226
226
227 nt.assert_equal(path.get_xdg_dir(), cfgdir)
227 assert path.get_xdg_dir() == cfgdir
228
228
229 @with_environment
229 @with_environment
230 def test_get_xdg_dir_3():
230 def test_get_xdg_dir_3():
@@ -240,7 +240,7 b' def test_get_xdg_dir_3():'
240 if not os.path.exists(cfgdir):
240 if not os.path.exists(cfgdir):
241 os.makedirs(cfgdir)
241 os.makedirs(cfgdir)
242
242
243 nt.assert_equal(path.get_xdg_dir(), None)
243 assert path.get_xdg_dir() is None
244
244
245 def test_filefind():
245 def test_filefind():
246 """Various tests for filefind"""
246 """Various tests for filefind"""
@@ -263,13 +263,13 b' def test_get_long_path_name_win32():'
263 # Test to see if the short path evaluates correctly.
263 # Test to see if the short path evaluates correctly.
264 short_path = os.path.join(tmpdir, 'THISIS~1')
264 short_path = os.path.join(tmpdir, 'THISIS~1')
265 evaluated_path = path.get_long_path_name(short_path)
265 evaluated_path = path.get_long_path_name(short_path)
266 nt.assert_equal(evaluated_path.lower(), long_path.lower())
266 assert evaluated_path.lower() == long_path.lower()
267
267
268
268
269 @dec.skip_win32
269 @dec.skip_win32
270 def test_get_long_path_name():
270 def test_get_long_path_name():
271 p = path.get_long_path_name('/usr/local')
271 p = path.get_long_path_name("/usr/local")
272 nt.assert_equal(p,'/usr/local')
272 assert p == "/usr/local"
273
273
274
274
275 class TestRaiseDeprecation(unittest.TestCase):
275 class TestRaiseDeprecation(unittest.TestCase):
@@ -300,18 +300,18 b' class TestRaiseDeprecation(unittest.TestCase):'
300 @with_environment
300 @with_environment
301 def test_get_py_filename():
301 def test_get_py_filename():
302 os.chdir(TMP_TEST_DIR)
302 os.chdir(TMP_TEST_DIR)
303 with make_tempfile('foo.py'):
303 with make_tempfile("foo.py"):
304 nt.assert_equal(path.get_py_filename('foo.py'), 'foo.py')
304 assert path.get_py_filename("foo.py") == "foo.py"
305 nt.assert_equal(path.get_py_filename('foo'), 'foo.py')
305 assert path.get_py_filename("foo") == "foo.py"
306 with make_tempfile('foo'):
306 with make_tempfile("foo"):
307 nt.assert_equal(path.get_py_filename('foo'), 'foo')
307 assert path.get_py_filename("foo") == "foo"
308 nt.assert_raises(IOError, path.get_py_filename, 'foo.py')
308 nt.assert_raises(IOError, path.get_py_filename, "foo.py")
309 nt.assert_raises(IOError, path.get_py_filename, 'foo')
309 nt.assert_raises(IOError, path.get_py_filename, "foo")
310 nt.assert_raises(IOError, path.get_py_filename, 'foo.py')
310 nt.assert_raises(IOError, path.get_py_filename, "foo.py")
311 true_fn = 'foo with spaces.py'
311 true_fn = "foo with spaces.py"
312 with make_tempfile(true_fn):
312 with make_tempfile(true_fn):
313 nt.assert_equal(path.get_py_filename('foo with spaces'), true_fn)
313 assert path.get_py_filename("foo with spaces") == true_fn
314 nt.assert_equal(path.get_py_filename('foo with spaces.py'), true_fn)
314 assert path.get_py_filename("foo with spaces.py") == true_fn
315 nt.assert_raises(IOError, path.get_py_filename, '"foo with spaces.py"')
315 nt.assert_raises(IOError, path.get_py_filename, '"foo with spaces.py"')
316 nt.assert_raises(IOError, path.get_py_filename, "'foo with spaces.py'")
316 nt.assert_raises(IOError, path.get_py_filename, "'foo with spaces.py'")
317
317
@@ -361,8 +361,7 b' class TestShellGlob(unittest.TestCase):'
361 def check_match(self, patterns, matches):
361 def check_match(self, patterns, matches):
362 with self.in_tempdir():
362 with self.in_tempdir():
363 # glob returns unordered list. that's why sorted is required.
363 # glob returns unordered list. that's why sorted is required.
364 nt.assert_equal(sorted(path.shellglob(patterns)),
364 assert sorted(path.shellglob(patterns)) == sorted(matches)
365 sorted(matches))
366
365
367 def common_cases(self):
366 def common_cases(self):
368 return [
367 return [
@@ -397,12 +396,13 b' class TestShellGlob(unittest.TestCase):'
397 yield (self.check_match, patterns, matches)
396 yield (self.check_match, patterns, matches)
398
397
399
398
399 # TODO : pytest.mark.parametrise once nose is gone.
400 def test_unescape_glob():
400 def test_unescape_glob():
401 nt.assert_equal(path.unescape_glob(r'\*\[\!\]\?'), '*[!]?')
401 assert path.unescape_glob(r"\*\[\!\]\?") == "*[!]?"
402 nt.assert_equal(path.unescape_glob(r'\\*'), r'\*')
402 assert path.unescape_glob(r"\\*") == r"\*"
403 nt.assert_equal(path.unescape_glob(r'\\\*'), r'\*')
403 assert path.unescape_glob(r"\\\*") == r"\*"
404 nt.assert_equal(path.unescape_glob(r'\\a'), r'\a')
404 assert path.unescape_glob(r"\\a") == r"\a"
405 nt.assert_equal(path.unescape_glob(r'\a'), r'\a')
405 assert path.unescape_glob(r"\a") == r"\a"
406
406
407
407
408 @onlyif_unicode_paths
408 @onlyif_unicode_paths
@@ -431,17 +431,19 b' class TestLinkOrCopy(unittest.TestCase):'
431 return os.path.join(self.tempdir.name, *args)
431 return os.path.join(self.tempdir.name, *args)
432
432
433 def assert_inode_not_equal(self, a, b):
433 def assert_inode_not_equal(self, a, b):
434 nt.assert_not_equal(os.stat(a).st_ino, os.stat(b).st_ino,
434 assert (
435 "%r and %r do reference the same indoes" %(a, b))
435 os.stat(a).st_ino != os.stat(b).st_ino
436 ), "%r and %r do reference the same indoes" % (a, b)
436
437
437 def assert_inode_equal(self, a, b):
438 def assert_inode_equal(self, a, b):
438 nt.assert_equal(os.stat(a).st_ino, os.stat(b).st_ino,
439 assert (
439 "%r and %r do not reference the same indoes" %(a, b))
440 os.stat(a).st_ino == os.stat(b).st_ino
441 ), "%r and %r do not reference the same indoes" % (a, b)
440
442
441 def assert_content_equal(self, a, b):
443 def assert_content_equal(self, a, b):
442 with open(a) as a_f:
444 with open(a) as a_f:
443 with open(b) as b_f:
445 with open(b) as b_f:
444 nt.assert_equal(a_f.read(), b_f.read())
446 assert a_f.read() == b_f.read()
445
447
446 @skip_win32
448 @skip_win32
447 def test_link_successful(self):
449 def test_link_successful(self):
@@ -489,4 +491,4 b' class TestLinkOrCopy(unittest.TestCase):'
489 path.link_or_copy(self.src, dst)
491 path.link_or_copy(self.src, dst)
490 path.link_or_copy(self.src, dst)
492 path.link_or_copy(self.src, dst)
491 self.assert_inode_equal(self.src, dst)
493 self.assert_inode_equal(self.src, dst)
492 nt.assert_equal(sorted(os.listdir(self.tempdir.name)), ['src', 'target'])
494 assert sorted(os.listdir(self.tempdir.name)) == ["src", "target"]
@@ -67,6 +67,7 b' def test_find_cmd_fail():'
67 nt.assert_raises(FindCmdError,find_cmd,'asdfasdf')
67 nt.assert_raises(FindCmdError,find_cmd,'asdfasdf')
68
68
69
69
70 # TODO: move to pytest.mark.parametrize once nose gone
70 @dec.skip_win32
71 @dec.skip_win32
71 def test_arg_split():
72 def test_arg_split():
72 """Ensure that argument lines are correctly split like in a shell."""
73 """Ensure that argument lines are correctly split like in a shell."""
@@ -80,8 +81,10 b' def test_arg_split():'
80 ['something "with quotes"', ['something', '"with quotes"']],
81 ['something "with quotes"', ['something', '"with quotes"']],
81 ]
82 ]
82 for argstr, argv in tests:
83 for argstr, argv in tests:
83 nt.assert_equal(arg_split(argstr), argv)
84 assert arg_split(argstr) == argv
84
85
86
87 # TODO: move to pytest.mark.parametrize once nose gone
85 @dec.skip_if_not_win32
88 @dec.skip_if_not_win32
86 def test_arg_split_win32():
89 def test_arg_split_win32():
87 """Ensure that argument lines are correctly split like in a shell."""
90 """Ensure that argument lines are correctly split like in a shell."""
@@ -92,7 +95,7 b' def test_arg_split_win32():'
92 ['something "with quotes"', ['something', 'with quotes']],
95 ['something "with quotes"', ['something', 'with quotes']],
93 ]
96 ]
94 for argstr, argv in tests:
97 for argstr, argv in tests:
95 nt.assert_equal(arg_split(argstr), argv)
98 assert arg_split(argstr) == argv
96
99
97
100
98 class SubProcessTestCase(tt.TempFileMixin):
101 class SubProcessTestCase(tt.TempFileMixin):
1 NO CONTENT: modified file
NO CONTENT: modified file
@@ -32,31 +32,31 b' def test_columnize():'
32 items = [l*size for l in 'abcd']
32 items = [l*size for l in 'abcd']
33
33
34 out = text.columnize(items, displaywidth=80)
34 out = text.columnize(items, displaywidth=80)
35 nt.assert_equal(out, 'aaaaa bbbbb ccccc ddddd\n')
35 assert out == "aaaaa bbbbb ccccc ddddd\n"
36 out = text.columnize(items, displaywidth=25)
36 out = text.columnize(items, displaywidth=25)
37 nt.assert_equal(out, 'aaaaa ccccc\nbbbbb ddddd\n')
37 assert out == "aaaaa ccccc\nbbbbb ddddd\n"
38 out = text.columnize(items, displaywidth=12)
38 out = text.columnize(items, displaywidth=12)
39 nt.assert_equal(out, 'aaaaa ccccc\nbbbbb ddddd\n')
39 assert out == "aaaaa ccccc\nbbbbb ddddd\n"
40 out = text.columnize(items, displaywidth=10)
40 out = text.columnize(items, displaywidth=10)
41 nt.assert_equal(out, 'aaaaa\nbbbbb\nccccc\nddddd\n')
41 assert out == "aaaaa\nbbbbb\nccccc\nddddd\n"
42
42
43 out = text.columnize(items, row_first=True, displaywidth=80)
43 out = text.columnize(items, row_first=True, displaywidth=80)
44 nt.assert_equal(out, 'aaaaa bbbbb ccccc ddddd\n')
44 assert out == "aaaaa bbbbb ccccc ddddd\n"
45 out = text.columnize(items, row_first=True, displaywidth=25)
45 out = text.columnize(items, row_first=True, displaywidth=25)
46 nt.assert_equal(out, 'aaaaa bbbbb\nccccc ddddd\n')
46 assert out == "aaaaa bbbbb\nccccc ddddd\n"
47 out = text.columnize(items, row_first=True, displaywidth=12)
47 out = text.columnize(items, row_first=True, displaywidth=12)
48 nt.assert_equal(out, 'aaaaa bbbbb\nccccc ddddd\n')
48 assert out == "aaaaa bbbbb\nccccc ddddd\n"
49 out = text.columnize(items, row_first=True, displaywidth=10)
49 out = text.columnize(items, row_first=True, displaywidth=10)
50 nt.assert_equal(out, 'aaaaa\nbbbbb\nccccc\nddddd\n')
50 assert out == "aaaaa\nbbbbb\nccccc\nddddd\n"
51
51
52 out = text.columnize(items, displaywidth=40, spread=True)
52 out = text.columnize(items, displaywidth=40, spread=True)
53 nt.assert_equal(out, 'aaaaa bbbbb ccccc ddddd\n')
53 assert out == "aaaaa bbbbb ccccc ddddd\n"
54 out = text.columnize(items, displaywidth=20, spread=True)
54 out = text.columnize(items, displaywidth=20, spread=True)
55 nt.assert_equal(out, 'aaaaa ccccc\nbbbbb ddddd\n')
55 assert out == "aaaaa ccccc\nbbbbb ddddd\n"
56 out = text.columnize(items, displaywidth=12, spread=True)
56 out = text.columnize(items, displaywidth=12, spread=True)
57 nt.assert_equal(out, 'aaaaa ccccc\nbbbbb ddddd\n')
57 assert out == "aaaaa ccccc\nbbbbb ddddd\n"
58 out = text.columnize(items, displaywidth=10, spread=True)
58 out = text.columnize(items, displaywidth=10, spread=True)
59 nt.assert_equal(out, 'aaaaa\nbbbbb\nccccc\nddddd\n')
59 assert out == "aaaaa\nbbbbb\nccccc\nddddd\n"
60
60
61
61
62 def test_columnize_random():
62 def test_columnize_random():
@@ -77,38 +77,43 b' def test_columnize_random():'
77 print("size of each element :\n %s" % rand_len)
77 print("size of each element :\n %s" % rand_len)
78 assert False, "row_first={0}".format(row_first)
78 assert False, "row_first={0}".format(row_first)
79
79
80
81 # TODO: pytest mark.parametrize once nose removed.
80 def test_columnize_medium():
82 def test_columnize_medium():
81 """Test with inputs than shouldn't be wider than 80"""
83 """Test with inputs than shouldn't be wider than 80"""
82 size = 40
84 size = 40
83 items = [l*size for l in 'abc']
85 items = [l*size for l in 'abc']
84 for row_first in [True, False]:
86 for row_first in [True, False]:
85 out = text.columnize(items, row_first=row_first, displaywidth=80)
87 out = text.columnize(items, row_first=row_first, displaywidth=80)
86 nt.assert_equal(out, '\n'.join(items+['']), "row_first={0}".format(row_first))
88 assert out == "\n".join(items + [""]), "row_first={0}".format(row_first)
89
87
90
91 # TODO: pytest mark.parametrize once nose removed.
88 def test_columnize_long():
92 def test_columnize_long():
89 """Test columnize with inputs longer than the display window"""
93 """Test columnize with inputs longer than the display window"""
90 size = 11
94 size = 11
91 items = [l*size for l in 'abc']
95 items = [l*size for l in 'abc']
92 for row_first in [True, False]:
96 for row_first in [True, False]:
93 out = text.columnize(items, row_first=row_first, displaywidth=size-1)
97 out = text.columnize(items, row_first=row_first, displaywidth=size - 1)
94 nt.assert_equal(out, '\n'.join(items+['']), "row_first={0}".format(row_first))
98 assert out == "\n".join(items + [""]), "row_first={0}".format(row_first)
99
95
100
96 def eval_formatter_check(f):
101 def eval_formatter_check(f):
97 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os, u=u"café", b="café")
102 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os, u=u"café", b="café")
98 s = f.format("{n} {n//4} {stuff.split()[0]}", **ns)
103 s = f.format("{n} {n//4} {stuff.split()[0]}", **ns)
99 nt.assert_equal(s, "12 3 hello")
104 assert s == "12 3 hello"
100 s = f.format(' '.join(['{n//%i}'%i for i in range(1,8)]), **ns)
105 s = f.format(' '.join(['{n//%i}'%i for i in range(1,8)]), **ns)
101 nt.assert_equal(s, "12 6 4 3 2 2 1")
106 assert s == "12 6 4 3 2 2 1"
102 s = f.format('{[n//i for i in range(1,8)]}', **ns)
107 s = f.format('{[n//i for i in range(1,8)]}', **ns)
103 nt.assert_equal(s, "[12, 6, 4, 3, 2, 2, 1]")
108 assert s == "[12, 6, 4, 3, 2, 2, 1]"
104 s = f.format("{stuff!s}", **ns)
109 s = f.format("{stuff!s}", **ns)
105 nt.assert_equal(s, ns['stuff'])
110 assert s == ns["stuff"]
106 s = f.format("{stuff!r}", **ns)
111 s = f.format("{stuff!r}", **ns)
107 nt.assert_equal(s, repr(ns['stuff']))
112 assert s == repr(ns["stuff"])
108
113
109 # Check with unicode:
114 # Check with unicode:
110 s = f.format("{u}", **ns)
115 s = f.format("{u}", **ns)
111 nt.assert_equal(s, ns['u'])
116 assert s == ns["u"]
112 # This decodes in a platform dependent manner, but it shouldn't error out
117 # This decodes in a platform dependent manner, but it shouldn't error out
113 s = f.format("{b}", **ns)
118 s = f.format("{b}", **ns)
114
119
@@ -117,11 +122,11 b' def eval_formatter_check(f):'
117 def eval_formatter_slicing_check(f):
122 def eval_formatter_slicing_check(f):
118 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
123 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
119 s = f.format(" {stuff.split()[:]} ", **ns)
124 s = f.format(" {stuff.split()[:]} ", **ns)
120 nt.assert_equal(s, " ['hello', 'there'] ")
125 assert s == " ['hello', 'there'] "
121 s = f.format(" {stuff.split()[::-1]} ", **ns)
126 s = f.format(" {stuff.split()[::-1]} ", **ns)
122 nt.assert_equal(s, " ['there', 'hello'] ")
127 assert s == " ['there', 'hello'] "
123 s = f.format("{stuff[::2]}", **ns)
128 s = f.format("{stuff[::2]}", **ns)
124 nt.assert_equal(s, ns['stuff'][::2])
129 assert s == ns["stuff"][::2]
125
130
126 nt.assert_raises(SyntaxError, f.format, "{n:x}", **ns)
131 nt.assert_raises(SyntaxError, f.format, "{n:x}", **ns)
127
132
@@ -129,13 +134,13 b' def eval_formatter_no_slicing_check(f):'
129 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
134 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
130
135
131 s = f.format('{n:x} {pi**2:+f}', **ns)
136 s = f.format('{n:x} {pi**2:+f}', **ns)
132 nt.assert_equal(s, "c +9.869604")
137 assert s == "c +9.869604"
133
138
134 s = f.format('{stuff[slice(1,4)]}', **ns)
139 s = f.format("{stuff[slice(1,4)]}", **ns)
135 nt.assert_equal(s, 'ell')
140 assert s == "ell"
136
141
137 s = f.format("{a[:]}", a=[1, 2])
142 s = f.format("{a[:]}", a=[1, 2])
138 nt.assert_equal(s, "[1, 2]")
143 assert s == "[1, 2]"
139
144
140 def test_eval_formatter():
145 def test_eval_formatter():
141 f = text.EvalFormatter()
146 f = text.EvalFormatter()
@@ -154,29 +159,16 b' def test_dollar_formatter():'
154
159
155 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
160 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
156 s = f.format("$n", **ns)
161 s = f.format("$n", **ns)
157 nt.assert_equal(s, "12")
162 assert s == "12"
158 s = f.format("$n.real", **ns)
163 s = f.format("$n.real", **ns)
159 nt.assert_equal(s, "12")
164 assert s == "12"
160 s = f.format("$n/{stuff[:5]}", **ns)
165 s = f.format("$n/{stuff[:5]}", **ns)
161 nt.assert_equal(s, "12/hello")
166 assert s == "12/hello"
162 s = f.format("$n $$HOME", **ns)
167 s = f.format("$n $$HOME", **ns)
163 nt.assert_equal(s, "12 $HOME")
168 assert s == "12 $HOME"
164 s = f.format("${foo}", foo="HOME")
169 s = f.format("${foo}", foo="HOME")
165 nt.assert_equal(s, "$HOME")
170 assert s == "$HOME"
166
167
168 def test_long_substr():
169 data = ['hi']
170 nt.assert_equal(text.long_substr(data), 'hi')
171
172
173 def test_long_substr2():
174 data = ['abc', 'abd', 'abf', 'ab']
175 nt.assert_equal(text.long_substr(data), 'ab')
176
171
177 def test_long_substr_empty():
178 data = []
179 nt.assert_equal(text.long_substr(data), '')
180
172
181 def test_strip_email():
173 def test_strip_email():
182 src = """\
174 src = """\
@@ -189,25 +181,25 b' def test_strip_email():'
189 ... return x+1
181 ... return x+1
190 ...
182 ...
191 >>> zz = f(2.5)"""
183 >>> zz = f(2.5)"""
192 nt.assert_equal(text.strip_email_quotes(src), cln)
184 assert text.strip_email_quotes(src) == cln
193
185
194
186
195 def test_strip_email2():
187 def test_strip_email2():
196 src = '> > > list()'
188 src = '> > > list()'
197 cln = 'list()'
189 cln = 'list()'
198 nt.assert_equal(text.strip_email_quotes(src), cln)
190 assert text.strip_email_quotes(src) == cln
199
191
200 def test_LSString():
192 def test_LSString():
201 lss = text.LSString("abc\ndef")
193 lss = text.LSString("abc\ndef")
202 nt.assert_equal(lss.l, ['abc', 'def'])
194 assert lss.l == ["abc", "def"]
203 nt.assert_equal(lss.s, 'abc def')
195 assert lss.s == "abc def"
204 lss = text.LSString(os.getcwd())
196 lss = text.LSString(os.getcwd())
205 nt.assert_is_instance(lss.p[0], Path)
197 nt.assert_is_instance(lss.p[0], Path)
206
198
207 def test_SList():
199 def test_SList():
208 sl = text.SList(['a 11', 'b 1', 'a 2'])
200 sl = text.SList(["a 11", "b 1", "a 2"])
209 nt.assert_equal(sl.n, 'a 11\nb 1\na 2')
201 assert sl.n == "a 11\nb 1\na 2"
210 nt.assert_equal(sl.s, 'a 11 b 1 a 2')
202 assert sl.s == "a 11 b 1 a 2"
211 nt.assert_equal(sl.grep(lambda x: x.startswith('a')), text.SList(['a 11', 'a 2']))
203 assert sl.grep(lambda x: x.startswith("a")) == text.SList(["a 11", "a 2"])
212 nt.assert_equal(sl.fields(0), text.SList(['a', 'b', 'a']))
204 assert sl.fields(0) == text.SList(["a", "b", "a"])
213 nt.assert_equal(sl.sort(field=1, nums=True), text.SList(['b 1', 'a 2', 'a 11']))
205 assert sl.sort(field=1, nums=True) == text.SList(["b 1", "a 2", "a 11"])
@@ -10,6 +10,7 b' Inheritance diagram:'
10
10
11 import os
11 import os
12 import re
12 import re
13 import string
13 import sys
14 import sys
14 import textwrap
15 import textwrap
15 from string import Formatter
16 from string import Formatter
@@ -252,7 +253,6 b' def indent(instr,nspaces=4, ntabs=0, flatten=False):'
252
253
253 Parameters
254 Parameters
254 ----------
255 ----------
255
256 instr : basestring
256 instr : basestring
257 The string to be indented.
257 The string to be indented.
258 nspaces : int (default: 4)
258 nspaces : int (default: 4)
@@ -266,7 +266,6 b' def indent(instr,nspaces=4, ntabs=0, flatten=False):'
266
266
267 Returns
267 Returns
268 -------
268 -------
269
270 str|unicode : string indented by ntabs and nspaces.
269 str|unicode : string indented by ntabs and nspaces.
271
270
272 """
271 """
@@ -390,7 +389,6 b' def wrap_paragraphs(text, ncols=80):'
390
389
391 Returns
390 Returns
392 -------
391 -------
393
394 list of complete paragraphs, wrapped to fill `ncols` columns.
392 list of complete paragraphs, wrapped to fill `ncols` columns.
395 """
393 """
396 paragraph_re = re.compile(r'\n(\s*\n)+', re.MULTILINE)
394 paragraph_re = re.compile(r'\n(\s*\n)+', re.MULTILINE)
@@ -408,22 +406,6 b' def wrap_paragraphs(text, ncols=80):'
408 return out_ps
406 return out_ps
409
407
410
408
411 def long_substr(data):
412 """Return the longest common substring in a list of strings.
413
414 Credit: http://stackoverflow.com/questions/2892931/longest-common-substring-from-more-than-two-strings-python
415 """
416 substr = ''
417 if len(data) > 1 and len(data[0]) > 0:
418 for i in range(len(data[0])):
419 for j in range(len(data[0])-i+1):
420 if j > len(substr) and all(data[0][i:i+j] in x for x in data):
421 substr = data[0][i:i+j]
422 elif len(data) == 1:
423 substr = data[0]
424 return substr
425
426
427 def strip_email_quotes(text):
409 def strip_email_quotes(text):
428 """Strip leading email quotation characters ('>').
410 """Strip leading email quotation characters ('>').
429
411
@@ -457,20 +439,23 b' def strip_email_quotes(text):'
457 Out[5]: '> > text\\n> > more\\nlast different'
439 Out[5]: '> > text\\n> > more\\nlast different'
458 """
440 """
459 lines = text.splitlines()
441 lines = text.splitlines()
460 matches = set()
442 strip_len = 0
461 for line in lines:
443
462 prefix = re.match(r'^(\s*>[ >]*)', line)
444 for characters in zip(*lines):
463 if prefix:
445 # Check if all characters in this position are the same
464 matches.add(prefix.group(1))
446 if len(set(characters)) > 1:
465 else:
466 break
447 break
448 prefix_char = characters[0]
449
450 if prefix_char in string.whitespace or prefix_char == ">":
451 strip_len += 1
467 else:
452 else:
468 prefix = long_substr(list(matches))
453 break
469 if prefix:
454
470 strip = len(prefix)
455 text = "\n".join([ln[strip_len:] for ln in lines])
471 text = '\n'.join([ ln[strip:] for ln in lines])
472 return text
456 return text
473
457
458
474 def strip_ansi(source):
459 def strip_ansi(source):
475 """
460 """
476 Remove ansi escape codes from text.
461 Remove ansi escape codes from text.
@@ -651,7 +636,6 b' def compute_item_matrix(items, row_first=False, empty=None, *args, **kwargs) :'
651
636
652 Parameters
637 Parameters
653 ----------
638 ----------
654
655 items
639 items
656 list of strings to columize
640 list of strings to columize
657 row_first : (default False)
641 row_first : (default False)
@@ -666,14 +650,11 b' def compute_item_matrix(items, row_first=False, empty=None, *args, **kwargs) :'
666
650
667 Returns
651 Returns
668 -------
652 -------
669
670 strings_matrix
653 strings_matrix
671
672 nested list of string, the outer most list contains as many list as
654 nested list of string, the outer most list contains as many list as
673 rows, the innermost lists have each as many element as columns. If the
655 rows, the innermost lists have each as many element as columns. If the
674 total number of elements in `items` does not equal the product of
656 total number of elements in `items` does not equal the product of
675 rows*columns, the last element of some lists are filled with `None`.
657 rows*columns, the last element of some lists are filled with `None`.
676
677 dict_info
658 dict_info
678 some info to make columnize easier:
659 some info to make columnize easier:
679
660
@@ -713,14 +694,11 b" def columnize(items, row_first=False, separator=' ', displaywidth=80, spread=Fa"
713 ----------
694 ----------
714 items : sequence of strings
695 items : sequence of strings
715 The strings to process.
696 The strings to process.
716
717 row_first : (default False)
697 row_first : (default False)
718 Whether to compute columns for a row-first matrix instead of
698 Whether to compute columns for a row-first matrix instead of
719 column-first (default).
699 column-first (default).
720
721 separator : str, optional [default is two spaces]
700 separator : str, optional [default is two spaces]
722 The string that separates columns.
701 The string that separates columns.
723
724 displaywidth : int, optional [default is 80]
702 displaywidth : int, optional [default is 80]
725 Width of the display in number of characters.
703 Width of the display in number of characters.
726
704
@@ -23,6 +23,11 b' import time'
23 # If possible (Unix), use the resource module instead of time.clock()
23 # If possible (Unix), use the resource module instead of time.clock()
24 try:
24 try:
25 import resource
25 import resource
26 except ImportError:
27 resource = None
28
29 # Some implementations (like jyputerlite) don't have getrusage
30 if resource is not None and hasattr(resource, "getrusage"):
26 def clocku():
31 def clocku():
27 """clocku() -> floating point number
32 """clocku() -> floating point number
28
33
@@ -56,7 +61,9 b' try:'
56
61
57 Similar to clock(), but return a tuple of user/system times."""
62 Similar to clock(), but return a tuple of user/system times."""
58 return resource.getrusage(resource.RUSAGE_SELF)[:2]
63 return resource.getrusage(resource.RUSAGE_SELF)[:2]
59 except ImportError:
64
65
66 else:
60 # There is no distinction of user/system time under windows, so we just use
67 # There is no distinction of user/system time under windows, so we just use
61 # time.perff_counter() for everything...
68 # time.perff_counter() for everything...
62 clocku = clocks = clock = time.perf_counter
69 clocku = clocks = clock = time.perf_counter
@@ -28,7 +28,6 b' def line_at_cursor(cell, cursor_pos=0):'
28
28
29 Parameters
29 Parameters
30 ----------
30 ----------
31
32 cell: str
31 cell : str
33 multiline block of text
32 multiline block of text
34 cursor_pos: integer
33 cursor_pos : integer
@@ -36,7 +35,6 b' def line_at_cursor(cell, cursor_pos=0):'
36
35
37 Returns
36 Returns
38 -------
37 -------
39
40 (line, offset): (string, integer)
38 (line, offset): (string, integer)
41 The line with the current cursor, and the character offset of the start of the line.
39 The line with the current cursor, and the character offset of the start of the line.
42 """
40 """
@@ -66,7 +64,6 b' def token_at_cursor(cell, cursor_pos=0):'
66
64
67 Parameters
65 Parameters
68 ----------
66 ----------
69
70 cell : unicode
67 cell : unicode
71 A block of Python code
68 A block of Python code
72 cursor_pos : int
69 cursor_pos : int
@@ -4,8 +4,8 b''
4 .. image:: https://img.shields.io/pypi/v/IPython.svg
4 .. image:: https://img.shields.io/pypi/v/IPython.svg
5 :target: https://pypi.python.org/pypi/ipython
5 :target: https://pypi.python.org/pypi/ipython
6
6
7 .. image:: https://img.shields.io/travis/ipython/ipython.svg
7 .. image:: https://github.com/ipython/ipython/actions/workflows/test.yml/badge.svg
8 :target: https://travis-ci.org/ipython/ipython
8 :target: https://github.com/ipython/ipython/actions/workflows/test.yml)
9
9
10 .. image:: https://www.codetriage.com/ipython/ipython/badges/users.svg
10 .. image:: https://www.codetriage.com/ipython/ipython/badges/users.svg
11 :target: https://www.codetriage.com/ipython/ipython/
11 :target: https://www.codetriage.com/ipython/ipython/
@@ -1,4 +1,4 b''
1 -e .
1 -e .[test]
2 ipykernel
2 ipykernel
3 setuptools>=18.5
3 setuptools>=18.5
4 sphinx
4 sphinx
@@ -6,3 +6,4 b' sphinx-rtd-theme'
6 docrepr
6 docrepr
7 matplotlib
7 matplotlib
8 stack_data
8 stack_data
9 pytest
@@ -18,7 +18,7 b''
18 import sys, os
18 import sys, os
19 from pathlib import Path
19 from pathlib import Path
20
20
21 # http://read-the-docs.readthedocs.io/en/latest/faq.html
21 # https://read-the-docs.readthedocs.io/en/latest/faq.html
22 ON_RTD = os.environ.get('READTHEDOCS', None) == 'True'
22 ON_RTD = os.environ.get('READTHEDOCS', None) == 'True'
23
23
24 if ON_RTD:
24 if ON_RTD:
@@ -151,7 +151,7 b' Complete Example'
151
151
152 Here is a full example of a magic package. You can distribute magics using
152 Here is a full example of a magic package. You can distribute magics using
153 setuptools, distutils, or any other distribution tools like `flit
153 setuptools, distutils, or any other distribution tools like `flit
154 <http://flit.readthedocs.io>`_ for pure Python packages.
154 <https://flit.readthedocs.io>`_ for pure Python packages.
155
155
156 When distributing magics as part of a package, recommended best practice is to
156 When distributing magics as part of a package, recommended best practice is to
157 execute the registration inside the `load_ipython_extension` as demonstrated in
157 execute the registration inside the `load_ipython_extension` as demonstrated in
@@ -7,9 +7,9 b' loop, so you can use both a GUI and an interactive prompt together. IPython'
7 supports a number of common GUI toolkits, but from IPython 3.0, it is possible
7 supports a number of common GUI toolkits, but from IPython 3.0, it is possible
8 to integrate other event loops without modifying IPython itself.
8 to integrate other event loops without modifying IPython itself.
9
9
10 Supported event loops include ``qt4``, ``qt5``, ``gtk2``, ``gtk3``, ``wx``,
10 Supported event loops include ``qt4``, ``qt5``, ``gtk2``, ``gtk3``, ``gtk4``,
11 ``osx`` and ``tk``. Make sure the event loop you specify matches the GUI
11 ``wx``, ``osx`` and ``tk``. Make sure the event loop you specify matches the
12 toolkit used by your own code.
12 GUI toolkit used by your own code.
13
13
14 To make IPython GUI event loop integration occur automatically at every
14 To make IPython GUI event loop integration occur automatically at every
15 startup, set the ``c.InteractiveShellApp.gui`` configuration key in your
15 startup, set the ``c.InteractiveShellApp.gui`` configuration key in your
@@ -24,14 +24,59 b' returns a list of objects which are possible keys in a subscript expression'
24 Rich display
24 Rich display
25 ============
25 ============
26
26
27 The notebook and the Qt console can display richer representations of objects.
27 Custom methods
28 To use this, you can define any of a number of ``_repr_*_()`` methods. Note that
28 ----------------------
29 these are surrounded by single, not double underscores.
29 IPython can display richer representations of objects.
30
30 To do this, you can define ``_ipython_display_()``, or any of a number of
31 Both the notebook and the Qt console can display ``svg``, ``png`` and ``jpeg``
31 ``_repr_*_()`` methods.
32 representations. The notebook can also display ``html``, ``javascript``,
32 Note that these are surrounded by single, not double underscores.
33 ``markdown`` and ``latex``. If the methods don't exist, or return ``None``, it
33
34 falls back to a standard ``repr()``.
34 .. list-table:: Supported ``_repr_*_`` methods
35 :widths: 20 15 15 15
36 :header-rows: 1
37
38 * - Format
39 - REPL
40 - Notebook
41 - Qt Console
42 * - ``_repr_pretty_``
43 - yes
44 - yes
45 - yes
46 * - ``_repr_svg_``
47 - no
48 - yes
49 - yes
50 * - ``_repr_png_``
51 - no
52 - yes
53 - yes
54 * - ``_repr_jpeg_``
55 - no
56 - yes
57 - yes
58 * - ``_repr_html_``
59 - no
60 - yes
61 - no
62 * - ``_repr_javascript_``
63 - no
64 - yes
65 - no
66 * - ``_repr_markdown_``
67 - no
68 - yes
69 - no
70 * - ``_repr_latex_``
71 - no
72 - yes
73 - no
74 * - ``_repr_mimebundle_``
75 - no
76 - ?
77 - ?
78
79 If the methods don't exist, or return ``None``, the standard ``repr()`` is used.
35
80
36 For example::
81 For example::
37
82
@@ -42,43 +87,61 b' For example::'
42 def _repr_html_(self):
87 def _repr_html_(self):
43 return "<h1>" + self.text + "</h1>"
88 return "<h1>" + self.text + "</h1>"
44
89
45 We often want to provide frontends with guidance on how to display the data. To
46 support this, ``_repr_*_()`` methods can also return a ``(data, metadata)``
47 tuple where ``metadata`` is a dictionary containing arbitrary key-value pairs for
48 the frontend to interpret. An example use case is ``_repr_jpeg_()``, which can
49 be set to return a jpeg image and a ``{'height': 400, 'width': 600}`` dictionary
50 to inform the frontend how to size the image.
51
90
52 There are also two more powerful display methods:
91 Special methods
92 ^^^^^^^^^^^^^^^
93
94 Pretty printing
95 """""""""""""""
96
97 To customize how your object is pretty-printed, add a ``_repr_pretty_`` method
98 to the class.
99 The method should accept a pretty printer, and a boolean that indicates whether
100 the printer detected a cycle.
101 The method should act on the printer to produce your customized pretty output.
102 Here is an example::
103
104 class MyObject(object):
105
106 def _repr_pretty_(self, p, cycle):
107 if cycle:
108 p.text('MyObject(...)')
109 else:
110 p.text('MyObject[...]')
111
112 For details on how to use the pretty printer, see :py:mod:`IPython.lib.pretty`.
113
114 More powerful methods
115 """""""""""""""""""""
53
116
54 .. class:: MyObject
117 .. class:: MyObject
55
118
56 .. method:: _repr_mimebundle_(include=None, exclude=None)
119 .. method:: _repr_mimebundle_(include=None, exclude=None)
57
120
58 Should return a dictionary of multiple formats, keyed by mimetype, or a tuple
121 Should return a dictionary of multiple formats, keyed by mimetype, or a tuple
59 of two dictionaries: *data, metadata*. If this returns something, other
122 of two dictionaries: *data, metadata* (see :ref:`Metadata`).
60 ``_repr_*_`` methods are ignored. The method should take keyword arguments
123 If this returns something, other ``_repr_*_`` methods are ignored.
61 ``include`` and ``exclude``, though it is not required to respect them.
124 The method should take keyword arguments ``include`` and ``exclude``, though
125 it is not required to respect them.
62
126
63 .. method:: _ipython_display_()
127 .. method:: _ipython_display_()
64
128
65 Displays the object as a side effect; the return value is ignored. If this
129 Displays the object as a side effect; the return value is ignored. If this
66 is defined, all other display methods are ignored.
130 is defined, all other display methods are ignored.
131 This method is ignored in the REPL.
67
132
68 To customize how the REPL pretty-prints your object, add a `_repr_pretty_`
69 method to the class. The method should accept a pretty printer, and a boolean
70 that indicates whether the printer detected a cycle. The method should act on
71 the printer to produce your customized pretty output. Here is an example::
72
133
73 class MyObject(object):
134 Metadata
135 ^^^^^^^^
136
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)``
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
141 be set to return a jpeg image and a ``{'height': 400, 'width': 600}`` dictionary
142 to inform the frontend how to size the image.
74
143
75 def _repr_pretty_(self, p, cycle):
76 if cycle:
77 p.text('MyObject(...)')
78 else:
79 p.text('MyObject[...]')
80
144
81 For details, see :py:mod:`IPython.lib.pretty`.
82
145
83 Formatters for third-party types
146 Formatters for third-party types
84 --------------------------------
147 --------------------------------
@@ -103,7 +166,7 b' Rarely, you might want to display a custom traceback when reporting an'
103 exception. To do this, define the custom traceback using
166 exception. To do this, define the custom traceback using
104 `_render_traceback_(self)` method which returns a list of strings, one string
167 `_render_traceback_(self)` method which returns a list of strings, one string
105 for each line of the traceback. For example, the `ipyparallel
168 for each line of the traceback. For example, the `ipyparallel
106 <http://ipyparallel.readthedocs.io/>`__ a parallel computing framework for
169 <https://ipyparallel.readthedocs.io/>`__ a parallel computing framework for
107 IPython, does this to display errors from multiple engines.
170 IPython, does this to display errors from multiple engines.
108
171
109 Please be conservative in using this feature; by replacing the default traceback
172 Please be conservative in using this feature; by replacing the default traceback
@@ -119,11 +119,6 b' which adds a directory called ``profile_<name>`` to your IPython directory. Then'
119 you can load this profile by adding ``--profile=<name>`` to your command line
119 you can load this profile by adding ``--profile=<name>`` to your command line
120 options. Profiles are supported by all IPython applications.
120 options. Profiles are supported by all IPython applications.
121
121
122 IPython ships with some sample profiles in :file:`IPython/config/profile`. If
123 you create profiles with the name of one of our shipped profiles, these config
124 files will be copied over instead of starting with the automatically generated
125 config files.
126
127 IPython extends the config loader for Python files so that you can inherit
122 IPython extends the config loader for Python files so that you can inherit
128 config from another profile. To do this, use a line like this in your Python
123 config from another profile. To do this, use a line like this in your Python
129 config file:
124 config file:
@@ -5,4 +5,4 b' Connection Diagrams of The IPython ZMQ Cluster'
5 ==============================================
5 ==============================================
6
6
7 IPython parallel has moved to ipyparallel -
7 IPython parallel has moved to ipyparallel -
8 see :ref:`ipyparallel:parallel_connections` for the documentation.
8 see :ref:`ipyparallel:/reference/connections.md` for the documentation.
@@ -5,4 +5,4 b' Messaging for Parallel Computing'
5 ================================
5 ================================
6
6
7 IPython parallel has moved to ipyparallel -
7 IPython parallel has moved to ipyparallel -
8 see :ref:`ipyparallel:parallel_messages` for the documentation.
8 see :ref:`ipyparallel:/reference/messages.md` for the documentation.
@@ -7,7 +7,7 b' You can now re-use the kernel machinery in IPython to easily make new kernels.'
7 This is useful for languages that have Python bindings, such as `Octave
7 This is useful for languages that have Python bindings, such as `Octave
8 <http://www.gnu.org/software/octave/>`_ (via
8 <http://www.gnu.org/software/octave/>`_ (via
9 `Oct2Py <http://blink1073.github.io/oct2py/>`_), or languages
9 `Oct2Py <http://blink1073.github.io/oct2py/>`_), or languages
10 where the REPL can be controlled in a tty using `pexpect <http://pexpect.readthedocs.io/en/latest/>`_,
10 where the REPL can be controlled in a tty using `pexpect <https://pexpect.readthedocs.io/en/latest/>`_,
11 such as bash.
11 such as bash.
12
12
13 .. seealso::
13 .. seealso::
@@ -57,7 +57,7 b' features:'
57
57
58 The Command line interface inherits the above functionality and adds
58 The Command line interface inherits the above functionality and adds
59
59
60 * real multi-line editing thanks to `prompt_toolkit <http://python-prompt-toolkit.readthedocs.io/en/stable/>`_.
60 * real multi-line editing thanks to `prompt_toolkit <https://python-prompt-toolkit.readthedocs.io/en/stable/>`_.
61
61
62 * syntax highlighting as you type.
62 * syntax highlighting as you type.
63
63
@@ -69,7 +69,7 b' it allows:'
69 * the object to create a rich display of Html, Images, Latex, Sound and
69 * the object to create a rich display of Html, Images, Latex, Sound and
70 Video.
70 Video.
71
71
72 * interactive widgets with the use of the `ipywidgets <http://ipywidgets.readthedocs.io/en/stable/>`_ package.
72 * interactive widgets with the use of the `ipywidgets <https://ipywidgets.readthedocs.io/en/stable/>`_ package.
73
73
74
74
75 This documentation will walk you through most of the features of the IPython
75 This documentation will walk you through most of the features of the IPython
@@ -102,9 +102,9 b' repository <http://github.com/ipython/ipython>`_.'
102
102
103 .. seealso::
103 .. seealso::
104
104
105 `Jupyter documentation <http://jupyter.readthedocs.io/en/latest/>`__
105 `Jupyter documentation <https://jupyter.readthedocs.io/en/latest/>`__
106 The Jupyter documentation provides information about the Notebook code and other Jupyter sub-projects.
106 The Jupyter documentation provides information about the Notebook code and other Jupyter sub-projects.
107 `ipyparallel documentation <http://ipyparallel.readthedocs.io/en/latest/>`__
107 `ipyparallel documentation <https://ipyparallel.readthedocs.io/en/latest/>`__
108 Formerly ``IPython.parallel``.
108 Formerly ``IPython.parallel``.
109
109
110
110
@@ -51,7 +51,7 b' for more help see'
51
51
52 .. seealso::
52 .. seealso::
53
53
54 `Installing Jupyter <http://jupyter.readthedocs.io/en/latest/install.html>`__
54 `Installing Jupyter <https://jupyter.readthedocs.io/en/latest/install.html>`__
55 The Notebook, nbconvert, and many other former pieces of IPython are now
55 The Notebook, nbconvert, and many other former pieces of IPython are now
56 part of Project Jupyter.
56 part of Project Jupyter.
57
57
@@ -29,4 +29,4 b' done some work in the classic Python REPL.'
29 .. seealso::
29 .. seealso::
30
30
31 `A Qt Console for Jupyter <https://jupyter.org/qtconsole/>`__
31 `A Qt Console for Jupyter <https://jupyter.org/qtconsole/>`__
32 `The Jupyter Notebook <http://jupyter-notebook.readthedocs.io/en/latest/>`__
32 `The Jupyter Notebook <https://jupyter-notebook.readthedocs.io/en/latest/>`__
@@ -3,20 +3,20 b' Python vs IPython'
3 =================
3 =================
4
4
5 This document is meant to highlight the main differences between the Python
5 This document is meant to highlight the main differences between the Python
6 language and what are the specific construct you can do only in IPython.
6 language and what are the specific constructs you can do only in IPython.
7
7
8 Unless expressed otherwise all of the construct you will see here will raise a
8 Unless expressed otherwise all of the constructs you will see here will raise a
9 ``SyntaxError`` if run in a pure Python shell, or if executing in a Python
9 ``SyntaxError`` if run in a pure Python shell, or if executing in a Python
10 script.
10 script.
11
11
12 Each of these features are described more in detail in further part of the documentation.
12 Each of these features is described more in detail in the further parts of the documentation.
13
13
14
14
15 Quick overview:
15 Quick overview:
16 ===============
16 ===============
17
17
18
18
19 All the following construct are valid IPython syntax:
19 All the following constructs are valid IPython syntax:
20
20
21 .. code-block:: ipython
21 .. code-block:: ipython
22
22
@@ -46,9 +46,9 b' All the following construct are valid IPython syntax:'
46 .. code-block:: ipython
46 .. code-block:: ipython
47
47
48 In [1]: my_files = !ls ~/
48 In [1]: my_files = !ls ~/
49 In [1]: for i,file in enumerate(my_file):
49 In [1]: for i, file in enumerate(my_files):
50 ...: raw = !echo $file
50 ...: raw = !echo $file
51 ...: !echo {files[0].upper()} $raw
51 ...: !echo {file[0].upper()} $raw
52
52
53
53
54 .. code-block:: ipython
54 .. code-block:: ipython
@@ -58,8 +58,8 b' All the following construct are valid IPython syntax:'
58 ...: print $months[0];
58 ...: print $months[0];
59
59
60
60
61 Each of these construct is compiled by IPython into valid python code and will
61 Each of these constructs is compiled by IPython into valid python code and will
62 do most of the time what you expect it will do. Let see each of these example
62 do most of the time what you expect it will do. Let's see each of these examples
63 in more detail.
63 in more detail.
64
64
65
65
@@ -89,7 +89,7 b' shortcut to get help. A question mark alone will bring up the IPython help:'
89 -------------
89 -------------
90 ...
90 ...
91
91
92 A single question mark before, or after an object available in current
92 A single question mark before or after an object available in the current
93 namespace will show help relative to this object:
93 namespace will show help relative to this object:
94
94
95 .. code-block:: ipython
95 .. code-block:: ipython
@@ -127,7 +127,7 b' and if possible display the python source code of this object.'
127
127
128
128
129 If you are looking for an object, the use of wildcards ``*`` in conjunction
129 If you are looking for an object, the use of wildcards ``*`` in conjunction
130 with question mark will allow you to search current namespace for object with
130 with a question mark will allow you to search the current namespace for objects with
131 matching names:
131 matching names:
132
132
133 .. code-block:: ipython
133 .. code-block:: ipython
@@ -142,10 +142,10 b' Shell Assignment'
142 ================
142 ================
143
143
144
144
145 When doing interactive computing it is common to need to access the underlying shell.
145 When doing interactive computing it is a common need to access the underlying shell.
146 This is doable through the use of the exclamation mark ``!`` (or bang).
146 This is doable through the use of the exclamation mark ``!`` (or bang).
147
147
148 This allow to execute simple command when present in beginning of line:
148 This allows to execute simple commands when present in beginning of the line:
149
149
150 .. code-block:: ipython
150 .. code-block:: ipython
151
151
@@ -167,7 +167,7 b' Or edit file:'
167
167
168 The line after the bang can call any program installed in the underlying
168 The line after the bang can call any program installed in the underlying
169 shell, and support variable expansion in the form of ``$variable`` or ``{variable}``.
169 shell, and support variable expansion in the form of ``$variable`` or ``{variable}``.
170 The later form of expansion supports arbitrary python expression:
170 The later form of expansion supports arbitrary python expressions:
171
171
172 .. code-block:: ipython
172 .. code-block:: ipython
173
173
@@ -176,25 +176,24 b' The later form of expansion supports arbitrary python expression:'
176 In[2]: !mv $file {file.upper()}
176 In[2]: !mv $file {file.upper()}
177
177
178
178
179 The bang can also be present in the right hand side of an assignment, just
179 The bang (``!``) can also be present on the right hand side of an assignment, just
180 after the equal sign, or separated from it by a white space. In which case the
180 after the equal sign, or separated from it by a white space. In this case the
181 standard output of the command after the bang ``!`` will be split out into lines
181 standard output of the command after the bang will be split out into lines
182 in a list-like object and assign to the left hand side.
182 in a list-like object and assigned to the left hand side.
183
183
184 This allow you for example to put the list of files of the current working directory in a variable:
184 This allows you, for example, to put the list of files of the current working directory in a variable:
185
185
186 .. code-block:: ipython
186 .. code-block:: ipython
187
187
188 In[1]: my_files = !ls
188 In[1]: my_files = !ls
189
189
190
190
191 You can combine the different possibilities in for loops, condition, functions...:
191 You can combine the different possibilities in for loops, conditions, functions...:
192
192
193 .. code-block:: ipython
193 .. code-block:: ipython
194
194
195 my_files = !ls ~/
195 my_files = !ls ~/
196 b = "backup file"
196 for i, file in enumerate(my_files):
197 for i,file in enumerate(my_file):
198 raw = !echo $backup $file
197 raw = !echo $backup $file
199 !cp $file {file.split('.')[0]+'.bak'}
198 !cp $file {file.split('.')[0] + '.bak'}
200
199
@@ -202,19 +201,19 b' You can combine the different possibilities in for loops, condition, functions..'
202 Magics
201 Magics
203 ------
202 ------
204
203
205 Magics function are often present in the form of shell-like syntax, but are
204 Magic functions (magics) are often present in the form of shell-like syntax, but they are
206 under the hood python function. The syntax and assignment possibility are
205 python functions under the hood. The syntax and assignment possibilities are
207 similar to the one with the bang (``!``) syntax, but with more flexibility and
206 similar to the one with the bang (``!``) syntax, but with more flexibility and
208 power. Magic function start with a percent sign (``%``) or double percent (``%%``).
207 power. Magic functions start with a percent sign (``%``) or double percent signs (``%%``).
209
208
210 A magic call with a sign percent will act only on one line:
209 A magic call with a single percent sign will act only on one line:
211
210
212 .. code-block:: ipython
211 .. code-block:: ipython
213
212
214 In[1]: %xmode
213 In[1]: %xmode
215 Exception reporting mode: Verbose
214 Exception reporting mode: Verbose
216
215
217 And support assignment:
216 Magics support assignment:
218
217
219 .. code-block:: ipython
218 .. code-block:: ipython
220
219
@@ -224,7 +223,7 b' And support assignment:'
224 In [2]: results
223 In [2]: results
225 Out[2]: <TimeitResult : 1 loops, best of 1: 21.1 µs per loop>
224 Out[2]: <TimeitResult : 1 loops, best of 1: 21.1 µs per loop>
226
225
227 Magic with two percent sign can spread over multiple lines, but does not support assignment:
226 Magics with double percent signs (``%%``) can spread over multiple lines, but they do not support assignments:
228
227
229 .. code-block:: ipython
228 .. code-block:: ipython
230
229
@@ -239,11 +238,3 b' Magic with two percent sign can spread over multiple lines, but does not support'
239 devfs 190Ki 190Ki 0Bi 100% 656 0 100% /dev
238 devfs 190Ki 190Ki 0Bi 100% 656 0 100% /dev
240 map -hosts 0Bi 0Bi 0Bi 100% 0 0 100% /net
239 map -hosts 0Bi 0Bi 0Bi 100% 0 0 100% /net
241 map auto_home 0Bi 0Bi 0Bi 100% 0 0 100% /hom
240 map auto_home 0Bi 0Bi 0Bi 100% 0 0 100% /hom
242
243
244 Combining it all
245 ----------------
246
247 ::
248
249 find a snippet that combine all that into one thing!
@@ -11,8 +11,11 b' You start IPython with the command::'
11
11
12 $ ipython [options] files
12 $ ipython [options] files
13
13
14 If invoked with no options, it executes all the files listed in sequence and
14 If invoked with no options, it executes the file and exits, passing the
15 exits. If you add the ``-i`` flag, it drops you into the interpreter while still
15 remaining arguments to the script, just as if you had specified the same
16 command with python. You may need to specify `--` before args to be passed
17 to the script, to prevent IPython from attempting to parse them.
18 If you add the ``-i`` flag, it drops you into the interpreter while still
16 acknowledging any options you may have set in your ``ipython_config.py``. This
19 acknowledging any options you may have set in your ``ipython_config.py``. This
17 behavior is different from standard Python, which when called as python ``-i``
20 behavior is different from standard Python, which when called as python ``-i``
18 will only execute one file and ignore your configuration setup.
21 will only execute one file and ignore your configuration setup.
@@ -41,7 +44,7 b' the command-line by passing the full class name and a corresponding value; type'
41 <...snip...>
44 <...snip...>
42 --matplotlib=<CaselessStrEnum> (InteractiveShellApp.matplotlib)
45 --matplotlib=<CaselessStrEnum> (InteractiveShellApp.matplotlib)
43 Default: None
46 Default: None
44 Choices: ['auto', 'gtk', 'gtk3', 'inline', 'nbagg', 'notebook', 'osx', 'qt', 'qt4', 'qt5', 'tk', 'wx']
47 Choices: ['auto', 'gtk', 'gtk3', 'gtk4', 'inline', 'nbagg', 'notebook', 'osx', 'qt', 'qt4', 'qt5', 'tk', 'wx']
45 Configure matplotlib for interactive use with the default matplotlib
48 Configure matplotlib for interactive use with the default matplotlib
46 backend.
49 backend.
47 <...snip...>
50 <...snip...>
@@ -899,7 +902,8 b' For users, enabling GUI event loop integration is simple. You simple use the'
899 %gui [GUINAME]
902 %gui [GUINAME]
900
903
901 With no arguments, ``%gui`` removes all GUI support. Valid ``GUINAME``
904 With no arguments, ``%gui`` removes all GUI support. Valid ``GUINAME``
902 arguments include ``wx``, ``qt``, ``qt5``, ``gtk``, ``gtk3`` and ``tk``.
905 arguments include ``wx``, ``qt``, ``qt5``, ``gtk``, ``gtk3`` ``gtk4``, and
906 ``tk``.
903
907
904 Thus, to use wxPython interactively and create a running :class:`wx.App`
908 Thus, to use wxPython interactively and create a running :class:`wx.App`
905 object, do::
909 object, do::
@@ -1,5 +1,11 b''
1 .. _ipython_as_shell:
1 .. _ipython_as_shell:
2
2
3 .. note::
4
5 This page has been kept for historical reason. You most likely want to use
6 `Xonsh <https://xon.sh/>`__ instead of this.
7
8
3 =========================
9 =========================
4 IPython as a system shell
10 IPython as a system shell
5 =========================
11 =========================
@@ -44,6 +50,10 b' so you should be able to type any normal system command and have it executed.'
44 See ``%alias?`` and ``%unalias?`` for details on the alias facilities. See also
50 See ``%alias?`` and ``%unalias?`` for details on the alias facilities. See also
45 ``%rehashx?`` for details on the mechanism used to load $PATH.
51 ``%rehashx?`` for details on the mechanism used to load $PATH.
46
52
53 .. warning::
54
55 See info at the top of the page. You most likely want to use
56 `Xonsh <https://xon.sh/>`__ instead of this.
47
57
48 Directory management
58 Directory management
49 ====================
59 ====================
@@ -196,3 +206,9 b' provide a convenient ways to use contained text in different formats:'
196 * ``.s`` returns string with lines separated by single space (for
206 * ``.s`` returns string with lines separated by single space (for
197 convenient passing to system commands)
207 convenient passing to system commands)
198 * ``.p`` returns list of "path" objects from detected file names
208 * ``.p`` returns list of "path" objects from detected file names
209
210 .. error::
211
212 You went too far scroll back up. You most likely want to use
213 `Xonsh <https://xon.sh/>`__ instead of this.
214
@@ -90,7 +90,7 b' completion also works on file and directory names.'
90 Starting with IPython 6.0, if ``jedi`` is installed, IPython will try to pull
90 Starting with IPython 6.0, if ``jedi`` is installed, IPython will try to pull
91 completions from Jedi as well. This allows to not only inspect currently
91 completions from Jedi as well. This allows to not only inspect currently
92 existing objects, but also to infer completion statically without executing
92 existing objects, but also to infer completion statically without executing
93 code. There is nothing particular need to get this to work, simply use tab
93 code. There is nothing particular needed to get this to work, simply use tab
94 completion on more complex expressions like the following::
94 completion on more complex expressions like the following::
95
95
96 >>> data = ['Number of users', 123456]
96 >>> data = ['Number of users', 123456]
@@ -219,7 +219,7 b' different numbers which correspond to the Process ID of the kernel.'
219
219
220 You can read more about using `jupyter qtconsole
220 You can read more about using `jupyter qtconsole
221 <https://jupyter.org/qtconsole/>`_, and
221 <https://jupyter.org/qtconsole/>`_, and
222 `jupyter notebook <http://jupyter-notebook.readthedocs.io/en/latest/>`_. There
222 `jupyter notebook <https://jupyter-notebook.readthedocs.io/en/latest/>`_. There
223 is also a :ref:`message spec <messaging>` which documents the protocol for
223 is also a :ref:`message spec <messaging>` which documents the protocol for
224 communication between kernels
224 communication between kernels
225 and clients.
225 and clients.
@@ -234,7 +234,7 b' Interactive parallel computing'
234
234
235
235
236 This functionality is optional and now part of the `ipyparallel
236 This functionality is optional and now part of the `ipyparallel
237 <http://ipyparallel.readthedocs.io/>`_ project.
237 <https://ipyparallel.readthedocs.io/>`_ project.
238
238
239 Portability and Python requirements
239 Portability and Python requirements
240 -----------------------------------
240 -----------------------------------
@@ -152,6 +152,24 b' and "??", in much the same way it can be done when using the IPython prompt::'
152
152
153 Previously, "pinfo" or "pinfo2" command had to be used for this purpose.
153 Previously, "pinfo" or "pinfo2" command had to be used for this purpose.
154
154
155
156 Autoreload 3 feature
157 ====================
158
159 Example: When an IPython session is ran with the 'autoreload' extension loaded,
160 you will now have the option '3' to select which means the following:
161
162 1. replicate all functionality from option 2
163 2. autoload all new funcs/classes/enums/globals from the module when they're added
164 3. autoload all newly imported funcs/classes/enums/globals from external modules
165
166 Try ``%autoreload 3`` in an IPython session after running ``%load_ext autoreload``
167
168 For more information please see unit test -
169 extensions/tests/test_autoreload.py : 'test_autoload_newly_added_objects'
170
171 =======
172
155 .. DO NOT EDIT THIS LINE BEFORE RELEASE. FEATURE INSERTION POINT.
173 .. DO NOT EDIT THIS LINE BEFORE RELEASE. FEATURE INSERTION POINT.
156
174
157 As a reminder, IPython master has diverged from the 7.x branch, thus master may
175 As a reminder, IPython master has diverged from the 7.x branch, thus master may
@@ -1099,7 +1099,7 b' Pull Requests (793):'
1099 * :ghpull:`2274`: CLN: Use name to id mapping of notebooks instead of searching.
1099 * :ghpull:`2274`: CLN: Use name to id mapping of notebooks instead of searching.
1100 * :ghpull:`2270`: SSHLauncher tweaks
1100 * :ghpull:`2270`: SSHLauncher tweaks
1101 * :ghpull:`2269`: add missing location when disambiguating controller IP
1101 * :ghpull:`2269`: add missing location when disambiguating controller IP
1102 * :ghpull:`2263`: Allow docs to build on http://readthedocs.io/
1102 * :ghpull:`2263`: Allow docs to build on https://readthedocs.io/
1103 * :ghpull:`2256`: Adding data publication example notebook.
1103 * :ghpull:`2256`: Adding data publication example notebook.
1104 * :ghpull:`2255`: better flush iopub with AsyncResults
1104 * :ghpull:`2255`: better flush iopub with AsyncResults
1105 * :ghpull:`2261`: Fix: longest_substr([]) -> ''
1105 * :ghpull:`2261`: Fix: longest_substr([]) -> ''
@@ -309,7 +309,7 b' be started by calling ``ipython qtconsole``. The protocol is :ref:`documented'
309 <messaging>`.
309 <messaging>`.
310
310
311 The parallel computing framework has also been rewritten using ZMQ. The
311 The parallel computing framework has also been rewritten using ZMQ. The
312 protocol is described :ref:`here <parallel_messages>`, and the code is in the
312 protocol is described :ref:`here <ipyparallel:/reference/messages.md>`, and the code is in the
313 new :mod:`IPython.parallel` module.
313 new :mod:`IPython.parallel` module.
314
314
315 .. _python3_011:
315 .. _python3_011:
@@ -307,7 +307,7 b" and tab completions that don't clutter up your history."
307 :target: ../_images/ptshell_features.png
307 :target: ../_images/ptshell_features.png
308
308
309 These features are provided by the Python library `prompt_toolkit
309 These features are provided by the Python library `prompt_toolkit
310 <http://python-prompt-toolkit.readthedocs.io/en/stable/>`__, which replaces
310 <https://python-prompt-toolkit.readthedocs.io/en/stable/>`__, which replaces
311 ``readline`` throughout our terminal interface.
311 ``readline`` throughout our terminal interface.
312
312
313 Relying on this pure-Python, cross platform module also makes it simpler to
313 Relying on this pure-Python, cross platform module also makes it simpler to
@@ -2,6 +2,309 b''
2 7.x Series
2 7.x Series
3 ============
3 ============
4
4
5 .. _version 7.28:
6
7 IPython 7.28
8 ============
9
10
11 IPython 7.28 is again a minor release that mostly bring bugfixes, and couple of
12 improvement. Many thanks to MrMino, who again did all the work this month, and
13 made a number of documentation improvements.
14
15 Here is a non-exhaustive list of changes,
16
17 Fixes:
18
19 - async with doesn't allow newlines :ghpull:`13090`
20 - Dynamically changing to vi mode via %config magic) :ghpull:`13091`
21
22 Virtualenv handling fixes:
23
24 - init_virtualenv now uses Pathlib :ghpull:`12548`
25 - Fix Improper path comparison of virtualenv directories :ghpull:`13140`
26 - Fix virtual environment user warning for lower case pathes :ghpull:`13094`
27 - Adapt to all sorts of drive names for cygwin :ghpull:`13153`
28
29 New Features:
30
31 - enable autoplay in embed YouTube player :ghpull:`13133`
32
33 Documentation:
34
35 - Fix formatting for the core.interactiveshell documentation :ghpull:`13118`
36 - Fix broken ipyparallel's refs :ghpull:`13138`
37 - Improve formatting of %time documentation :ghpull:`13125`
38 - Reword the YouTubeVideo autoplay WN :ghpull:`13147`
39
40
41 Thanks
42 ------
43
44 Many thanks to all the contributors to this release. You can find all individual
45 contributions to this milestone `on github
46 <https://github.com/ipython/ipython/milestone/92>`__.
47
48 Thanks as well to the `D. E. Shaw group <https://deshaw.com/>`__ for sponsoring
49 work on IPython and related libraries.
50
51
52 .. _version 7.27:
53
54 IPython 7.27
55 ============
56
57 IPython 7.27 is a minor release that fixes a couple of issues and compatibility.
58
59 - Add support for GTK4 :ghpull:`131011`
60 - Add support for Qt6 :ghpull:`13085`
61 - Fix an issue with pip magic on windows :ghpull:`13093`
62
63 Thanks
64 ------
65
66 Many thanks to all the contributors to this release. You can find all individual
67 contributions to this milestone `on github
68 <https://github.com/ipython/ipython/milestone/91>`__.
69
70 Thanks as well to the `D. E. Shaw group <https://deshaw.com/>`__ for sponsoring
71 work on IPython and related libraries.
72
73 .. _version 7.26:
74
75 IPython 7.26
76 ============
77
78 IPython 7.26 is a minor release that fixes a couple of issues, updates in API
79 and Copyright/Licenses issues around various part of the codebase.
80
81 We'll highlight `this issue <https://github.com/ipython/ipython/issues/13039>`
82 pointing out we were including and refereeing to code from Stack Overflow which
83 was CC-BY-SA, hence incompatible with the BSD license of IPython. This lead us
84 to a rewriting of the corresponding logic which in our case was done in a more
85 efficient way (in our case we were searching string prefixes instead of full
86 strings).
87
88 You will notice also a number of documentation improvements and cleanup.
89
90 Of particular interest are the following Pull-requests:
91
92
93 - The IPython directive now uses Sphinx logging for warnings. :ghpull:`13030`.
94 - Add expiry days option to pastebin magic and change http protocol to https.
95 :ghpull:`13056`
96 - Make Ipython.utils.timing work with jupyterlite :ghpull:`13050`.
97
98
99
100 Thanks
101 ------
102
103 Many thanks to all the contributors to this release and in particular MrMino who
104 is doing most of the work those days. You can find all individual contributions
105 to this milestone `on github <https://github.com/ipython/ipython/milestone/90>`__.
106
107 Thanks as well to the `D. E. Shaw group <https://deshaw.com/>`__ for sponsoring
108 work on IPython and related libraries.
109
110
111 .. _version 7.25:
112
113 IPython 7.25
114 ============
115
116 IPython 7.25 is a minor release that contains a single bugfix, which is highly
117 recommended for all users of ipdb, ipython debugger %debug magic and similar.
118
119 Issuing commands like ``where`` from within the debugger would reset the
120 local variables changes made by the user. It is interesting to look at the root
121 cause of the issue as accessing an attribute (``frame.f_locals``) would trigger
122 this side effects.
123
124 Thanks in particular to the patience from the reporters at D.E. Shaw for their
125 initial bug report that was due to a similar coding oversight in an extension,
126 and who took time to debug and narrow down the problem.
127
128 Thanks
129 ------
130
131 Many thanks to all the contributors to this release you can find all individual
132 contributions to this milestone `on github <https://github.com/ipython/ipython/milestone/89>`__.
133
134 Thanks as well to the `D. E. Shaw group <https://deshaw.com/>`__ for sponsoring
135 work on IPython and related libraries.
136
137
138 .. _version 7.24:
139
140 IPython 7.24
141 ============
142
143 Third release of IPython for 2021, mostly containing bug fixes. A couple of not
144 typical updates:
145
146 Misc
147 ----
148
149
150 - Fix an issue where ``%recall`` would both succeeded and print an error message
151 it failed. :ghpull:`12952`
152 - Drop support for NumPy 1.16 – practically has no effect beyond indicating in
153 package metadata that we do not support it. :ghpull:`12937`
154
155 Debugger improvements
156 ---------------------
157
158 The debugger (and ``%debug`` magic) have been improved and can skip or hide frames
159 originating from files that are not writable to the user, as these are less
160 likely to be the source of errors, or be part of system files this can be a useful
161 addition when debugging long errors.
162
163 In addition to the global ``skip_hidden True|False`` command, the debugger has
164 gained finer grained control of predicates as to whether to a frame should be
165 considered hidden. So far 3 predicates are available :
166
167 - ``tbhide``: frames containing the local variable ``__tracebackhide__`` set to
168 True.
169 - ``readonly``: frames originating from readonly files, set to False.
170 - ``ipython_internal``: frames that are likely to be from IPython internal
171 code, set to True.
172
173 You can toggle individual predicates during a session with
174
175 .. code-block::
176
177 ipdb> skip_predicates readonly True
178
179 Read-only files will now be considered hidden frames.
180
181
182 You can call ``skip_predicates`` without arguments to see the states of current
183 predicates:
184
185 .. code-block::
186
187 ipdb> skip_predicates
188 current predicates:
189 tbhide : True
190 readonly : False
191 ipython_internal : True
192
193 If all predicates are set to ``False``, ``skip_hidden`` will practically have
194 no effect. We attempt to warn you when all predicates are False.
195
196 Note that the ``readonly`` predicate may increase disk access as we check for
197 file access permission for all frames on many command invocation, but is usually
198 cached by operating systems. Let us know if you encounter any issues.
199
200 As the IPython debugger does not use the traitlets infrastructure for
201 configuration, by editing your ``.pdbrc`` files and appending commands you would
202 like to be executed just before entering the interactive prompt. For example:
203
204
205 .. code::
206
207 # file : ~/.pdbrc
208 skip_predicates readonly True
209 skip_predicates tbhide False
210
211 Will hide read only frames by default and show frames marked with
212 ``__tracebackhide__``.
213
214
215
216
217 Thanks
218 ------
219
220 Many thanks to all the contributors to this release you can find all individual
221 contributions to this milestone `on github <https://github.com/ipython/ipython/milestone/87>`__.
222
223 Thanks as well to the `D. E. Shaw group <https://deshaw.com/>`__ for sponsoring
224 work on IPython and related libraries, in particular above mentioned
225 improvements to the debugger.
226
227
228
229
230 .. _version 7.23:
231
232 IPython 7.23 and 7.23.1
233 =======================
234
235
236 Third release of IPython for 2021, mostly containing bug fixes. A couple of not
237 typical updates:
238
239 - We moved to GitHub actions away from Travis-CI, the transition may not be
240 100% complete (not testing on nightly anymore), but as we ran out of
241 Travis-Ci hours on the IPython organisation that was a necessary step.
242 :ghpull:`12900`.
243
244 - We have a new dependency: ``matplotlib-inline``, which try to extract
245 matplotlib inline backend specific behavior. It is available on PyPI and
246 conda-forge thus should not be a problem to upgrade to this version. If you
247 are a package maintainer that might be an extra dependency to package first.
248 :ghpull:`12817` (IPython 7.23.1 fix a typo that made this change fail)
249
250 In the addition/new feature category, ``display()`` now have a ``clear=True``
251 option to clear the display if any further outputs arrives, allowing users to
252 avoid having to use ``clear_output()`` directly. :ghpull:`12823`.
253
254 In bug fixes category, this release fix an issue when printing tracebacks
255 containing Unicode characters :ghpull:`12758`.
256
257 In code cleanup category :ghpull:`12932` remove usage of some deprecated
258 functionality for compatibility with Python 3.10.
259
260
261
262 Thanks
263 ------
264
265 Many thanks to all the contributors to this release you can find all individual
266 contributions to this milestone `on github <https://github.com/ipython/ipython/milestone/86>`__.
267 In particular MrMino for responding to almost all new issues, and triaging many
268 of the old ones, as well as takluyver, minrk, willingc for reacting quikly when
269 we ran out of CI Hours.
270
271 Thanks as well to organisations, QuantStack (martinRenou and SylvainCorlay) for
272 extracting matplotlib inline backend into its own package, and the `D. E. Shaw group
273 <https://deshaw.com/>`__ for sponsoring work on IPython and related libraries.
274
275
276 .. _version 7.22:
277
278 IPython 7.22
279 ============
280
281 Second release of IPython for 2021, mostly containing bug fixes. Here is a quick
282 rundown of the few changes.
283
284 - Fix some ``sys.excepthook`` shenanigan when embedding with qt, recommended if
285 you – for example – use `napari <https://napari.org>`__. :ghpull:`12842`.
286 - Fix bug when using the new ipdb ``%context`` magic :ghpull:`12844`
287 - Couples of deprecation cleanup :ghpull:`12868`
288 - Update for new dpast.com api if you use the ``%pastbin`` magic. :ghpull:`12712`
289 - Remove support for numpy before 1.16. :ghpull:`12836`
290
291
292 Thanks
293 ------
294
295 We have a new team member that you should see more often on the IPython
296 repository, Błażej Michalik (@MrMino) have been doing regular contributions to
297 IPython, and spent time replying to many issues and guiding new users to the
298 codebase; they now have triage permissions to the IPython repository and we'll
299 work toward giving them more permission in the future.
300
301 Many thanks to all the contributors to this release you can find all individual
302 contributions to this milestone `on github <https://github.com/ipython/ipython/milestone/84>`__.
303
304 Thanks as well to organisations, QuantStack for working on debugger
305 compatibility for Xeus_python, and the `D. E. Shaw group
306 <https://deshaw.com/>`__ for sponsoring work on IPython and related libraries.
307
5 .. _version 721:
308 .. _version 721:
6
309
7 IPython 7.21
310 IPython 7.21
@@ -33,7 +336,7 b' Thanks'
33 ------
336 ------
34
337
35 Many thanks to all the contributors to this release you can find all individual
338 Many thanks to all the contributors to this release you can find all individual
36 contribution to this milestone `on github <https://github.com/ipython/ipython/milestone/83>`_.
339 contribution to this milestone `on github <https://github.com/ipython/ipython/milestone/83>`__.
37
340
38
341
39 .. _version 720:
342 .. _version 720:
@@ -82,14 +385,14 b' was exceptionally no release last month.'
82 - Docs docs formatting that make the install commands work on zsh
385 - Docs docs formatting that make the install commands work on zsh
83 :ghpull:`12587`
386 :ghpull:`12587`
84 - Always display the last frame in tracebacks even if hidden with
387 - Always display the last frame in tracebacks even if hidden with
85 ``__traceback_hide__`` :ghpull:`12601`
388 ``__tracebackhide__`` :ghpull:`12601`
86 - Avoid an issue where a callback can be registered multiple times.
389 - Avoid an issue where a callback can be registered multiple times.
87 :ghpull:`12625`
390 :ghpull:`12625`
88 - Avoid an issue in debugger mode where frames changes could be lost.
391 - Avoid an issue in debugger mode where frames changes could be lost.
89 :ghpull:`12627`
392 :ghpull:`12627`
90
393
91 - Never hide the frames that invoke a debugger, even if marked as hidden by
394 - Never hide the frames that invoke a debugger, even if marked as hidden by
92 ``__traceback_hide__`` :ghpull:`12631`
395 ``__tracebackhide__`` :ghpull:`12631`
93 - Fix calling the debugger in a recursive manner :ghpull:`12659`
396 - Fix calling the debugger in a recursive manner :ghpull:`12659`
94
397
95
398
@@ -240,7 +543,7 b' Change of API and exposed objects automatically detected using `frappuccino'
240 <https://pypi.org/project/frappuccino/>`_ (still in beta):
543 <https://pypi.org/project/frappuccino/>`_ (still in beta):
241
544
242
545
243 The following items are new and mostly related to understanding ``__tracebackbhide__``::
546 The following items are new and mostly related to understanding ``__tracebackbide__``::
244
547
245 + IPython.core.debugger.Pdb.do_down(self, arg)
548 + IPython.core.debugger.Pdb.do_down(self, arg)
246 + IPython.core.debugger.Pdb.do_skip_hidden(self, arg)
549 + IPython.core.debugger.Pdb.do_skip_hidden(self, arg)
@@ -150,6 +150,7 b''
150 "&nbsp;&nbsp;<a href='gui/gui-glut.py' target='_blank'>gui-glut.py</a><br>\n",
150 "&nbsp;&nbsp;<a href='gui/gui-glut.py' target='_blank'>gui-glut.py</a><br>\n",
151 "&nbsp;&nbsp;<a href='gui/gui-gtk.py' target='_blank'>gui-gtk.py</a><br>\n",
151 "&nbsp;&nbsp;<a href='gui/gui-gtk.py' target='_blank'>gui-gtk.py</a><br>\n",
152 "&nbsp;&nbsp;<a href='gui/gui-gtk3.py' target='_blank'>gui-gtk3.py</a><br>\n",
152 "&nbsp;&nbsp;<a href='gui/gui-gtk3.py' target='_blank'>gui-gtk3.py</a><br>\n",
153 "&nbsp;&nbsp;<a href='gui/gui-gtk4.py' target='_blank'>gui-gtk4.py</a><br>\n",
153 "&nbsp;&nbsp;<a href='gui/gui-pyglet.py' target='_blank'>gui-pyglet.py</a><br>\n",
154 "&nbsp;&nbsp;<a href='gui/gui-pyglet.py' target='_blank'>gui-pyglet.py</a><br>\n",
154 "&nbsp;&nbsp;<a href='gui/gui-qt.py' target='_blank'>gui-qt.py</a><br>\n",
155 "&nbsp;&nbsp;<a href='gui/gui-qt.py' target='_blank'>gui-qt.py</a><br>\n",
155 "&nbsp;&nbsp;<a href='gui/gui-tk.py' target='_blank'>gui-tk.py</a><br>\n",
156 "&nbsp;&nbsp;<a href='gui/gui-tk.py' target='_blank'>gui-tk.py</a><br>\n",
@@ -160,6 +161,7 b''
160 " gui-glut.py\n",
161 " gui-glut.py\n",
161 " gui-gtk.py\n",
162 " gui-gtk.py\n",
162 " gui-gtk3.py\n",
163 " gui-gtk3.py\n",
164 " gui-gtk4.py\n",
163 " gui-pyglet.py\n",
165 " gui-pyglet.py\n",
164 " gui-qt.py\n",
166 " gui-qt.py\n",
165 " gui-tk.py\n",
167 " gui-tk.py\n",
@@ -3180,6 +3180,7 b''
3180 "&nbsp;&nbsp;<a href='./gui/gui-glut.py' target='_blank'>gui-glut.py</a><br>\n",
3180 "&nbsp;&nbsp;<a href='./gui/gui-glut.py' target='_blank'>gui-glut.py</a><br>\n",
3181 "&nbsp;&nbsp;<a href='./gui/gui-gtk.py' target='_blank'>gui-gtk.py</a><br>\n",
3181 "&nbsp;&nbsp;<a href='./gui/gui-gtk.py' target='_blank'>gui-gtk.py</a><br>\n",
3182 "&nbsp;&nbsp;<a href='./gui/gui-gtk3.py' target='_blank'>gui-gtk3.py</a><br>\n",
3182 "&nbsp;&nbsp;<a href='./gui/gui-gtk3.py' target='_blank'>gui-gtk3.py</a><br>\n",
3183 "&nbsp;&nbsp;<a href='./gui/gui-gtk4.py' target='_blank'>gui-gtk4.py</a><br>\n",
3183 "&nbsp;&nbsp;<a href='./gui/gui-pyglet.py' target='_blank'>gui-pyglet.py</a><br>\n",
3184 "&nbsp;&nbsp;<a href='./gui/gui-pyglet.py' target='_blank'>gui-pyglet.py</a><br>\n",
3184 "&nbsp;&nbsp;<a href='./gui/gui-qt.py' target='_blank'>gui-qt.py</a><br>\n",
3185 "&nbsp;&nbsp;<a href='./gui/gui-qt.py' target='_blank'>gui-qt.py</a><br>\n",
3185 "&nbsp;&nbsp;<a href='./gui/gui-tk.py' target='_blank'>gui-tk.py</a><br>\n",
3186 "&nbsp;&nbsp;<a href='./gui/gui-tk.py' target='_blank'>gui-tk.py</a><br>\n",
@@ -3230,6 +3231,7 b''
3230 " gui-glut.py\n",
3231 " gui-glut.py\n",
3231 " gui-gtk.py\n",
3232 " gui-gtk.py\n",
3232 " gui-gtk3.py\n",
3233 " gui-gtk3.py\n",
3234 " gui-gtk4.py\n",
3233 " gui-pyglet.py\n",
3235 " gui-pyglet.py\n",
3234 " gui-qt.py\n",
3236 " gui-qt.py\n",
3235 " gui-tk.py\n",
3237 " gui-tk.py\n",
@@ -1,2 +1,7 b''
1 [metadata]
1 [metadata]
2 license_file = LICENSE
2 license_file = LICENSE
3
4 [velin]
5 ignore_patterns =
6 IPython/core/tests,
7 IPython/testing
@@ -171,27 +171,36 b' setuptools_extra_args = {}'
171 # setuptools requirements
171 # setuptools requirements
172
172
173 extras_require = dict(
173 extras_require = dict(
174 parallel = ['ipyparallel'],
174 parallel=["ipyparallel"],
175 qtconsole = ['qtconsole'],
175 qtconsole=["qtconsole"],
176 doc = ['Sphinx>=1.3'],
176 doc=["Sphinx>=1.3"],
177 test = ['nose>=0.10.1', 'requests', 'testpath', 'pygments', 'nbformat', 'ipykernel', 'numpy>=1.14'],
177 test=[
178 "nose>=0.10.1",
179 "requests",
180 "testpath",
181 "pygments",
182 "nbformat",
183 "ipykernel",
184 "numpy>=1.17",
185 ],
178 terminal = [],
186 terminal=[],
179 kernel = ['ipykernel'],
187 kernel=["ipykernel"],
180 nbformat = ['nbformat'],
188 nbformat=["nbformat"],
181 notebook = ['notebook', 'ipywidgets'],
189 notebook=["notebook", "ipywidgets"],
182 nbconvert = ['nbconvert'],
190 nbconvert=["nbconvert"],
183 )
191 )
184
192
185 install_requires = [
193 install_requires = [
186 'setuptools>=18.5',
194 "setuptools>=18.5",
187 'jedi>=0.16',
195 "jedi>=0.16",
188 'decorator',
196 "decorator",
189 'pickleshare',
197 "pickleshare",
190 'traitlets>=4.2',
198 "traitlets>=4.2",
191 'prompt_toolkit>=2.0.0,<3.1.0,!=3.0.0,!=3.0.1',
199 "prompt_toolkit>=2.0.0,<3.1.0,!=3.0.0,!=3.0.1",
192 'pygments',
200 "pygments",
193 'backcall',
201 "backcall",
194 'stack_data',
202 "stack_data",
203 "matplotlib-inline",
195 ]
204 ]
196
205
197 # Platform-specific dependencies:
206 # Platform-specific dependencies:
@@ -21,6 +21,7 b' WHITE=$(tput setaf 7)'
21 NOR=$(tput sgr0)
21 NOR=$(tput sgr0)
22
22
23
23
24 echo "Will use $EDITOR to edit files when necessary"
24 echo -n "PREV_RELEASE (X.y.z) [$PREV_RELEASE]: "
25 echo -n "PREV_RELEASE (X.y.z) [$PREV_RELEASE]: "
25 read input
26 read input
26 PREV_RELEASE=${input:-$PREV_RELEASE}
27 PREV_RELEASE=${input:-$PREV_RELEASE}
@@ -47,6 +48,18 b' ask_section(){'
47 }
48 }
48
49
49
50
51 maybe_edit(){
52 echo
53 echo $BLUE"$1"$NOR
54 echo -n $GREEN"Press e to Edit $1, any other keys to skip: "$NOR
55 read -n1 value
56 echo
57 if [ $value = 'e' ] ; then
58 $EDITOR $1
59 fi
60 }
61
62
50
63
51 echo
64 echo
52 if ask_section "Updating what's new with informations from docs/source/whatsnew/pr"
65 if ask_section "Updating what's new with informations from docs/source/whatsnew/pr"
@@ -104,6 +117,7 b' echo $GREEN"I tried ${RED}sed -i bkp -e \'/Uncomment/s/^# //g\' IPython/core/relea'
104 sed -i bkp -e '/Uncomment/s/^# //g' IPython/core/release.py
117 sed -i bkp -e '/Uncomment/s/^# //g' IPython/core/release.py
105 rm IPython/core/release.pybkp
118 rm IPython/core/release.pybkp
106 git diff
119 git diff
120 maybe_edit IPython/core/release.py
107
121
108 echo $GREEN"Press enter to continue"$NOR
122 echo $GREEN"Press enter to continue"$NOR
109 read
123 read
@@ -151,6 +165,7 b' then'
151 rm IPython/core/release.pybkp
165 rm IPython/core/release.pybkp
152 git diff
166 git diff
153 echo $GREEN"Please bump ${RED}the minor version number${NOR}"
167 echo $GREEN"Please bump ${RED}the minor version number${NOR}"
168 maybe_edit IPython/core/release.py
154 echo ${BLUE}"Do not commit yet – we'll do it later."$NOR
169 echo ${BLUE}"Do not commit yet – we'll do it later."$NOR
155
170
156
171
1 NO CONTENT: file was removed
NO CONTENT: file was removed
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