##// 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 13 # <full commit hash> # initial black-format
14 14 # <full commit hash> # rename something internal
15 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 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 3 on:
4 4 push:
5 branches: [ master ]
5 branches: [ master, 7.x]
6 6 pull_request:
7 branches: [ master ]
7 branches: [ master, 7.x]
8 8
9 9 jobs:
10 10 build:
@@ -26,8 +26,9 b' jobs:'
26 26 pip install mypy pyflakes flake8
27 27 - name: Lint with mypy
28 28 run: |
29 mypy IPython/terminal/ptutils.py
30 mypy IPython/core/c*.py
29 mypy -p IPython.terminal
30 mypy -p IPython.core.magics
31 31 - name: Lint with pyflakes
32 32 run: |
33 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 6 on:
7 7 push:
8 branches: [ master ]
8 branches: [ master, 7.x ]
9 9 pull_request:
10 branches: [ master ]
10 branches: [ master, 7.x ]
11 11
12 12 jobs:
13 13 build:
@@ -28,7 +28,7 b' jobs:'
28 28 - name: Install dependencies
29 29 run: |
30 30 python -m pip install --upgrade pip
31 pip install darker
31 pip install darker isort
32 32 - name: Lint with darker
33 33 run: |
34 34 darker -r 60625f241f298b5039cb2debc365db38aa7bb522 --check --diff . || (
@@ -6,6 +6,7 b' docs/man/*.gz'
6 6 docs/source/api/generated
7 7 docs/source/config/options
8 8 docs/source/config/shortcuts/*.csv
9 docs/source/savefig
9 10 docs/source/interactive/magics-generated.txt
10 11 docs/gh-pages
11 12 jupyter_notebook/notebook/static/mathjax
@@ -28,3 +29,4 b' __pycache__'
28 29 .python-version
29 30 venv*/
30 31 .idea/
32 .mypy_cache/
@@ -80,8 +80,7 b' def embed_kernel(module=None, local_ns=None, **kwargs):'
80 80 The module to load into IPython globals (default: caller)
81 81 local_ns : dict, optional
82 82 The namespace to load into IPython user namespace (default: caller)
83
84 kwargs : various, optional
83 **kwargs : various, optional
85 84 Further keyword args are relayed to the IPKernelApp constructor,
86 85 allowing configuration of the Kernel. Will only have an effect
87 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 112 Parameters
114 113 ----------
115
116 114 argv : list or None, optional
117 115 If unspecified or None, IPython will parse command-line options from sys.argv.
118 116 To prevent any command-line parsing, pass an empty list: `argv=[]`.
119 117 user_ns : dict, optional
120 118 specify this dictionary to initialize the IPython user namespace with particular values.
121 kwargs : various, optional
119 **kwargs : various, optional
122 120 Any other kwargs will be passed to the Application constructor,
123 121 such as `config`.
124 122 """
@@ -138,13 +136,12 b' def start_kernel(argv=None, **kwargs):'
138 136
139 137 Parameters
140 138 ----------
141
142 139 argv : list or None, optional
143 140 If unspecified or None, IPython will parse command-line options from sys.argv.
144 141 To prevent any command-line parsing, pass an empty list: `argv=[]`.
145 142 user_ns : dict, optional
146 143 specify this dictionary to initialize the IPython user namespace with particular values.
147 kwargs : various, optional
144 **kwargs : various, optional
148 145 Any other kwargs will be passed to the Application constructor,
149 146 such as `config`.
150 147 """
@@ -66,26 +66,48 b' else:'
66 66
67 67 # aliases and flags
68 68
69 base_aliases = {
70 'profile-dir' : 'ProfileDir.location',
71 'profile' : 'BaseIPythonApplication.profile',
72 'ipython-dir' : 'BaseIPythonApplication.ipython_dir',
73 'log-level' : 'Application.log_level',
74 'config' : 'BaseIPythonApplication.extra_config_file',
69 base_aliases = {}
70 if isinstance(Application.aliases, dict):
71 # traitlets 5
72 base_aliases.update(Application.aliases)
73 base_aliases.update(
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(
78 debug = ({'Application' : {'log_level' : logging.DEBUG}},
79 "set log level to logging.DEBUG (maximize logging output)"),
80 quiet = ({'Application' : {'log_level' : logging.CRITICAL}},
81 "set log level to logging.CRITICAL (minimize logging output)"),
82 init = ({'BaseIPythonApplication' : {
83 'copy_config_files' : True,
84 'auto_create' : True}
85 }, """Initialize profile with default config files. This is equivalent
83 base_flags = dict()
84 if isinstance(Application.flags, dict):
85 # traitlets 5
86 base_flags.update(Application.flags)
87 base_flags.update(
88 dict(
89 debug=(
90 {"Application": {"log_level": logging.DEBUG}},
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 105 to running `ipython profile create <profile>` prior to startup.
87 """)
106 """,
107 ),
88 108 )
109 )
110
89 111
90 112 class ProfileAwareConfigLoader(PyFileConfigLoader):
91 113 """A Python file config loader that is aware of IPython profiles."""
1 NO CONTENT: modified file
@@ -92,6 +92,10 b' class CachingCompiler(codeop.Compile):'
92 92 # (otherwise we'd lose our tracebacks).
93 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 100 def ast_parse(self, source, filename='<unknown>', symbol='exec'):
97 101 """Parse code to an AST with the current compiler flags active.
@@ -153,6 +157,10 b' class CachingCompiler(codeop.Compile):'
153 157 raw_code = transformed_code
154 158
155 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 164 entry = (
157 165 len(transformed_code),
158 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 764 Parameters
765 765 ----------
766 keys:
766 keys
767 767 list of keys in dictionary currently being completed.
768 prefix:
768 prefix
769 769 Part of the text already typed by the user. E.g. `mydict[b'fo`
770 delims:
770 delims
771 771 String of delimiters to consider when finding the current key.
772 772 extra_prefix: optional
773 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 994 Parameters
995 995 ----------
996 parameter:
996 parameter
997 997 Jedi's function `Param`
998 998
999 999 Returns
@@ -154,6 +154,17 b' def is_importable(module, attr, only_modules):'
154 154 else:
155 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 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 183 completions.extend( [attr for attr in dir(m) if
173 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 192 if m_is_init:
177 193 completions.extend(module_list(os.path.dirname(m.__file__)))
178 194 completions_set = {c for c in completions if isinstance(c, str)}
@@ -104,22 +104,16 b' class CrashHandler(object):'
104 104 app : Application
105 105 A running :class:`Application` instance, which will be queried at
106 106 crash time for internal information.
107
108 107 contact_name : str
109 108 A string with the name of the person to contact.
110
111 109 contact_email : str
112 110 A string with the email address of the contact.
113
114 111 bug_tracker : str
115 112 A string with the URL for your project's bug tracker.
116
117 113 show_crash_traceback : bool
118 114 If false, don't print the crash traceback on stderr, only generate
119 115 the on-disk report
120
121 Non-argument instance attributes:
122
116 Non-argument instance attributes
123 117 These instances contain some non-argument attributes which allow for
124 118 further customization of the crash handler's behavior. Please see the
125 119 source for further details.
@@ -33,6 +33,7 b' import linecache'
33 33 import sys
34 34 import warnings
35 35 import re
36 import os
36 37
37 38 from IPython import get_ipython
38 39 from IPython.utils import PyColorize
@@ -109,7 +110,6 b' class Tracer(object):'
109 110
110 111 Parameters
111 112 ----------
112
113 113 colors : str, optional
114 114 The name of the color scheme to use, it must be one of IPython's
115 115 valid color schemes. If not given, the function will default to
@@ -199,19 +199,37 b' class Pdb(OldPdb):'
199 199 for a standalone version that uses prompt_toolkit, see
200 200 `IPython.terminal.debugger.TerminalPdb` and
201 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 211 def __init__(self, color_scheme=None, completekey=None,
205 212 stdin=None, stdout=None, context=5, **kwargs):
206 213 """Create a new IPython debugger.
207 214
208 :param color_scheme: Deprecated, do not use.
209 :param completekey: Passed to pdb.Pdb.
210 :param stdin: Passed to pdb.Pdb.
211 :param stdout: Passed to pdb.Pdb.
212 :param context: Number of lines of source code context to show when
215 Parameters
216 ----------
217 color_scheme : default None
218 Deprecated, do not use.
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 227 displaying stacktrace information.
214 :param kwargs: Passed to pdb.Pdb.
228 **kwargs
229 Passed to pdb.Pdb.
230
231 Notes
232 -----
215 233 The possibilities are python version dependent, see the python
216 234 docs for more info.
217 235 """
@@ -281,6 +299,10 b' class Pdb(OldPdb):'
281 299 # Set the prompt - the default prompt is '(Pdb)'
282 300 self.prompt = prompt
283 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 307 def set_colors(self, scheme):
286 308 """Shorthand access to the color table scheme selector method."""
@@ -293,6 +315,26 b' class Pdb(OldPdb):'
293 315 self.initial_frame = frame
294 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 338 def hidden_frames(self, stack):
297 339 """
298 340 Given an index in the stack return whether it should be skipped.
@@ -303,14 +345,9 b' class Pdb(OldPdb):'
303 345 # locals whenever the .f_locals accessor is called, so we
304 346 # avoid calling it here to preserve self.curframe_locals.
305 347 # Futhermore, there is no good reason to hide the current frame.
306 ip_hide = [
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 ]
348 ip_hide = [self._hidden_predicate(s[0]) for s in stack]
312 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 351 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
315 352 return ip_hide
316 353
@@ -398,6 +435,28 b' class Pdb(OldPdb):'
398 435 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
399 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 460 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
402 461 if context is None:
403 462 context = self.context
@@ -422,10 +481,11 b' class Pdb(OldPdb):'
422 481 frame, lineno = frame_lineno
423 482
424 483 return_value = ''
425 if '__return__' in frame.f_locals:
426 rv = frame.f_locals['__return__']
484 loc_frame = self._get_frame_locals(frame)
485 if "__return__" in loc_frame:
486 rv = loc_frame["__return__"]
427 487 #return_value += '->'
428 return_value += reprlib.repr(rv) + '\n'
488 return_value += reprlib.repr(rv) + "\n"
429 489 ret.append(return_value)
430 490
431 491 #s = filename + '(' + `lineno` + ')'
@@ -437,10 +497,10 b' class Pdb(OldPdb):'
437 497 else:
438 498 func = "<lambda>"
439 499
440 call = ''
441 if func != '?':
442 if '__args__' in frame.f_locals:
443 args = reprlib.repr(frame.f_locals['__args__'])
500 call = ""
501 if func != "?":
502 if "__args__" in loc_frame:
503 args = reprlib.repr(loc_frame["__args__"])
444 504 else:
445 505 args = '()'
446 506 call = tpl_call % (func, args)
@@ -533,15 +593,68 b' class Pdb(OldPdb):'
533 593 except KeyboardInterrupt:
534 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 641 def do_skip_hidden(self, arg):
537 642 """
538 643 Change whether or not we should skip frames with the
539 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 651 self.skip_hidden = True
543 652 elif arg.strip().lower() in ("false", "no"):
544 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 659 def do_list(self, arg):
547 660 """Print lines of code from the current stack frame
@@ -581,7 +694,7 b' class Pdb(OldPdb):'
581 694
582 695 def getsourcelines(self, obj):
583 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 698 # must be a module frame: do not try to cut a block out of it
586 699 return lines, 1
587 700 elif inspect.ismodule(obj):
@@ -705,12 +818,14 b' class Pdb(OldPdb):'
705 818 def stop_here(self, frame):
706 819 hidden = False
707 820 if self.skip_hidden:
708 hidden = frame.f_locals.get("__tracebackhide__", False)
821 hidden = self._hidden_predicate(frame)
709 822 if hidden:
823 if self.report_skipped:
710 824 Colors = self.color_scheme_table.active_colors
711 825 ColorsNormal = Colors.Normal
712 print(f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n")
713
826 print(
827 f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n"
828 )
714 829 return super().stop_here(frame)
715 830
716 831 def do_up(self, arg):
@@ -721,7 +836,7 b' class Pdb(OldPdb):'
721 836 Will skip hidden frames.
722 837 """
723 838 # modified version of upstream that skips
724 # frames with __tracebackide__
839 # frames with __tracebackhide__
725 840 if self.curindex == 0:
726 841 self.error("Oldest frame")
727 842 return
@@ -1178,7 +1178,12 b' class Video(DisplayObject):'
1178 1178
1179 1179 @skip_doctest
1180 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 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 1201 **kwargs
1197 1202 Keyword args will be relayed to ``figure.canvas.print_figure``.
1198 1203 """
1199 from IPython.core.interactiveshell import InteractiveShell
1200 from IPython.core.pylabtools import select_figure_formats
1201 # build kwargs, starting with InlineBackend config
1202 kw = {}
1203 from ipykernel.pylab.config import InlineBackend
1204 cfg = InlineBackend.instance()
1205 kw.update(cfg.print_figure_kwargs)
1206 kw.update(**kwargs)
1207 shell = InteractiveShell.instance()
1208 select_figure_formats(shell, formats, **kw)
1204 warnings.warn(
1205 "`set_matplotlib_formats` is deprecated since IPython 7.23, directly "
1206 "use `matplotlib_inline.backend_inline.set_matplotlib_formats()`",
1207 DeprecationWarning,
1208 stacklevel=2,
1209 )
1210
1211 from matplotlib_inline.backend_inline import (
1212 set_matplotlib_formats as set_matplotlib_formats_orig,
1213 )
1214
1215 set_matplotlib_formats_orig(*formats, **kwargs)
1209 1216
1210 1217 @skip_doctest
1211 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 1227 By default, the inline backend used in the IPython Notebook will close all
1215 1228 matplotlib figures automatically after each cell is run. This means that
@@ -1229,6 +1242,15 b' def set_matplotlib_close(close=True):'
1229 1242 Should all matplotlib figures be automatically closed after each cell is
1230 1243 run?
1231 1244 """
1232 from ipykernel.pylab.config import InlineBackend
1233 cfg = InlineBackend.instance()
1234 cfg.close_figures = close
1245 warnings.warn(
1246 "`set_matplotlib_close` is deprecated since IPython 7.23, directly "
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 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 99 """Display a Python object in all frontends.
90 100
91 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 130 Set an id for the display.
121 131 This id can be used for updating this display area later via update_display.
122 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 137 Additional keyword-arguments are passed through to the display publisher.
125 138
126 139 Returns
127 140 -------
128
129 141 handle: DisplayHandle
130 142 Returns a handle on updatable displays for use with :func:`update_display`,
131 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 146 Examples
135 147 --------
136
137 148 >>> class Json(object):
138 149 ... def __init__(self, json):
139 150 ... self.json = json
@@ -172,12 +183,10 b' def display(*objs, include=None, exclude=None, metadata=None, transient=None, di'
172 183
173 184 See Also
174 185 --------
175
176 186 :func:`update_display`
177 187
178 188 Notes
179 189 -----
180
181 190 In Python, objects can declare their textual representation using the
182 191 `__repr__` method. IPython expands on this idea and allows objects to declare
183 192 other, rich representations including:
@@ -239,7 +248,6 b' def display(*objs, include=None, exclude=None, metadata=None, transient=None, di'
239 248 print(*objs)
240 249 return
241 250
242 raw = kwargs.pop('raw', False)
243 251 if transient is None:
244 252 transient = {}
245 253 if metadata is None:
@@ -263,6 +271,9 b' def display(*objs, include=None, exclude=None, metadata=None, transient=None, di'
263 271 if not raw:
264 272 format = InteractiveShell.instance().display_formatter.format
265 273
274 if clear:
275 clear_output(wait=True)
276
266 277 for obj in objs:
267 278 if raw:
268 279 publish_display_data(data=obj, metadata=metadata, **kwargs)
@@ -285,15 +296,13 b' def update_display(obj, *, display_id, **kwargs):'
285 296
286 297 Parameters
287 298 ----------
288
289 obj:
299 obj
290 300 The object with which to update the display
291 301 display_id: keyword-only
292 302 The id of the display to update
293 303
294 304 See Also
295 305 --------
296
297 306 :func:`display`
298 307 """
299 308 kwargs['update'] = True
@@ -328,10 +337,9 b' class DisplayHandle(object):'
328 337
329 338 Parameters
330 339 ----------
331
332 obj:
340 obj
333 341 object to display
334 **kwargs:
342 **kwargs
335 343 additional keyword arguments passed to display
336 344 """
337 345 display(obj, display_id=self.display_id, **kwargs)
@@ -341,10 +349,9 b' class DisplayHandle(object):'
341 349
342 350 Parameters
343 351 ----------
344
345 obj:
352 obj
346 353 object to display
347 **kwargs:
354 **kwargs
348 355 additional keyword arguments passed to update_display
349 356 """
350 357 update_display(obj, display_id=self.display_id, **kwargs)
1 NO CONTENT: modified file
1 NO CONTENT: modified file
@@ -33,7 +33,7 b' class EventManager(object):'
33 33 ----------
34 34 shell
35 35 The :class:`~IPython.core.interactiveshell.InteractiveShell` instance
36 available_callbacks
36 available_events
37 37 An iterable of names for callback events.
38 38 """
39 39 self.shell = shell
1 NO CONTENT: modified file
@@ -121,7 +121,6 b' class DisplayFormatter(Configurable):'
121 121 Returns
122 122 -------
123 123 (format_dict, metadata_dict) : tuple of two dicts
124
125 124 format_dict is a dictionary of key/value pairs, one of each format that was
126 125 generated for the object. The keys are the format types, which
127 126 will usually be MIME type strings and the values and JSON'able
@@ -133,7 +132,6 b' class DisplayFormatter(Configurable):'
133 132
134 133 Notes
135 134 -----
136
137 135 If an object implement `_repr_mimebundle_` as well as various
138 136 `_repr_*_`, the data returned by `_repr_mimebundle_` will take
139 137 precedence and the corresponding `_repr_*_` for this mimetype will
@@ -636,7 +634,6 b' class PlainTextFormatter(BaseFormatter):'
636 634
637 635 This parameter can be set via the '%precision' magic.
638 636 """
639
640 637 new = change['new']
641 638 if '%' in new:
642 639 # got explicit format string
@@ -678,6 +675,11 b' class PlainTextFormatter(BaseFormatter):'
678 675 def _type_printers_default(self):
679 676 d = pretty._type_pprinters.copy()
680 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 683 return d
682 684
683 685 @default('deferred_printers')
@@ -445,8 +445,11 b' class HistoryAccessor(HistoryAccessorBase):'
445 445 Parameters
446 446 ----------
447 447 rangestr : str
448 A string specifying ranges, e.g. "5 ~2/1-4". See
449 :func:`magic_history` for full details.
448 A string specifying ranges, e.g. "5 ~2/1-4". If empty string is used,
449 this will return everything from current session's history.
450
451 See the documentation of :func:`%history` for the full details.
452
450 453 raw, output : bool
451 454 As :meth:`get_range`
452 455
@@ -851,11 +854,18 b' $""", re.VERBOSE)'
851 854 def extract_hist_ranges(ranges_str):
852 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 860 Examples
855 861 --------
856 862 >>> list(extract_hist_ranges("~8/5-~7/4 2"))
857 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 869 for range_str in ranges_str.split():
860 870 rmatch = range_re.match(range_str)
861 871 if not rmatch:
@@ -10,7 +10,9 b' deprecated in 7.0.'
10 10 # Copyright (c) IPython Development Team.
11 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 16 import re
15 17 import tokenize
16 18 from typing import List, Tuple, Optional, Any
@@ -89,7 +91,30 b' classic_prompt = PromptStripper('
89 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 119 def cell_magic(lines):
95 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 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 550 def show_linewise_tokens(s: str):
512 551 """For investigation and debugging"""
513 552 if not s.endswith('\n'):
@@ -662,6 +701,15 b' class TransformerManager:'
662 701
663 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 713 if not tokens_by_line:
666 714 return 'incomplete', find_last_indent(lines)
667 715
@@ -727,3 +775,25 b' def find_last_indent(lines):'
727 775 if not m:
728 776 return 0
729 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 923 paths.append(p.resolve())
924 924
925 925 # In Cygwin paths like "c:\..." and '\cygdrive\c\...' are possible
926 if str(p_venv).startswith("\\cygdrive"):
927 p_venv = Path(str(p_venv)[11:])
928 elif len(str(p_venv)) >= 2 and str(p_venv)[1] == ":":
929 p_venv = Path(str(p_venv)[2:])
926 if p_venv.parts[1] == "cygdrive":
927 drive_name = p_venv.parts[2]
928 p_venv = (drive_name + ":/") / Path(*p_venv.parts[3:])
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 931 # Our exe is inside or has access to the virtualenv, don't need to do anything.
933 932 return
934 933
@@ -1896,9 +1895,14 b' class InteractiveShell(SingletonConfigurable):'
1896 1895 exception or returns an invalid result, it will be immediately
1897 1896 disabled.
1898 1897
1898 Notes
1899 -----
1900
1899 1901 WARNING: by putting in your own exception handler into IPython's main
1900 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 1906 if not isinstance(exc_tuple, tuple):
1903 1907 raise TypeError("The custom exceptions must be given as a tuple.")
1904 1908
@@ -1966,7 +1970,7 b' class InteractiveShell(SingletonConfigurable):'
1966 1970 sys.excepthook themselves. I guess this is a feature that
1967 1971 enables them to keep running after exceptions that would
1968 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 1974 except: statement.
1971 1975
1972 1976 Normally, IPython sets sys.excepthook to a CrashHandler instance, so if
@@ -2085,13 +2089,17 b' class InteractiveShell(SingletonConfigurable):'
2085 2089 except KeyboardInterrupt:
2086 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 2093 """Actually show a traceback.
2090 2094
2091 2095 Subclasses may override this method to put the traceback on a different
2092 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 2104 def showsyntaxerror(self, filename=None, running_compiled_code=False):
2097 2105 """Display the syntax error that just occurred.
@@ -2218,6 +2226,9 b' class InteractiveShell(SingletonConfigurable):'
2218 2226 matches : list
2219 2227 A sorted list with all possible completions.
2220 2228
2229
2230 Notes
2231 -----
2221 2232 The optional arguments allow the completion to take more context into
2222 2233 account, and are part of the low-level completion API.
2223 2234
@@ -2226,7 +2237,8 b' class InteractiveShell(SingletonConfigurable):'
2226 2237 exposing it as a method, it can be used by other non-readline
2227 2238 environments (such as GUIs) for text completion.
2228 2239
2229 Simple usage example:
2240 Examples
2241 --------
2230 2242
2231 2243 In [1]: x = 'hello'
2232 2244
@@ -2508,6 +2520,23 b' class InteractiveShell(SingletonConfigurable):'
2508 2520 Command to execute.
2509 2521 """
2510 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 2540 # protect os.system from UNC paths on Windows, which it can't handle:
2512 2541 if sys.platform == 'win32':
2513 2542 from IPython.utils._process_win32 import AvoidUNCPath
@@ -3109,9 +3138,7 b' class InteractiveShell(SingletonConfigurable):'
3109 3138 _run_async = False
3110 3139
3111 3140 with self.builtin_trap:
3112 cell_name = self.compile.cache(
3113 cell, self.execution_count, raw_code=raw_cell
3114 )
3141 cell_name = compiler.cache(cell, self.execution_count, raw_code=raw_cell)
3115 3142
3116 3143 with self.display_trap:
3117 3144 # Compile to bytecode
@@ -3514,6 +3541,7 b' class InteractiveShell(SingletonConfigurable):'
3514 3541 display figures inline.
3515 3542 """
3516 3543 from IPython.core import pylabtools as pt
3544 from matplotlib_inline.backend_inline import configure_inline_support
3517 3545 gui, backend = pt.find_gui_and_backend(gui, self.pylab_gui_select)
3518 3546
3519 3547 if gui != 'inline':
@@ -3527,7 +3555,7 b' class InteractiveShell(SingletonConfigurable):'
3527 3555 gui, backend = pt.find_gui_and_backend(self.pylab_gui_select)
3528 3556
3529 3557 pt.activate_matplotlib(backend)
3530 pt.configure_inline_support(self, backend)
3558 configure_inline_support(self, backend)
3531 3559
3532 3560 # Now we must activate the gui pylab wants to use, and fix %run to take
3533 3561 # plot updates into account
@@ -3667,12 +3695,15 b' class InteractiveShell(SingletonConfigurable):'
3667 3695
3668 3696 Parameters
3669 3697 ----------
3670 range_str : string
3698 range_str : str
3671 3699 The set of slices is given as a string, like "~5/6-~4/2 4:8 9",
3672 3700 since this function is for use by magic functions which get their
3673 3701 arguments as strings. The number before the / is the session
3674 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 3707 raw : bool, optional
3677 3708 By default, the processed input is used. If this is true, the raw
3678 3709 input history is used instead.
@@ -3686,7 +3717,16 b' class InteractiveShell(SingletonConfigurable):'
3686 3717 * ``N-M`` -> include items N..M (closed endpoint).
3687 3718 """
3688 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 3731 def find_user_code(self, target, raw=True, py_only=False, skip_encoding_cookie=True, search_ns=False):
3692 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 3736 Parameters
3697 3737 ----------
3698
3699 3738 target : str
3700
3701 3739 A string specifying code to retrieve. This will be tried respectively
3702 3740 as: ranges of input history (see %history for syntax), url,
3703 3741 corresponding .py file, filename, or an expression evaluating to a
3704 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 3747 raw : bool
3707 3748 If true (default), retrieve raw history. Has no effect on the other
3708 3749 retrieval mechanisms.
@@ -3771,6 +3812,22 b' class InteractiveShell(SingletonConfigurable):'
3771 3812 raise TypeError("%s is neither a string nor a macro." % target,
3772 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 3832 # Things related to IPython exiting
3776 3833 #-------------------------------------------------------------------------
@@ -3785,26 +3842,24 b' class InteractiveShell(SingletonConfigurable):'
3785 3842 code that has the appropriate information, rather than trying to
3786 3843 clutter
3787 3844 """
3788 # Close the history session (this stores the end time and line count)
3789 # this must be *before* the tempfile cleanup, in case of temporary
3790 # history db
3791 self.history_manager.end_session()
3845 self._atexit_once()
3792 3846
3793 3847 # Cleanup all tempfiles and folders left around
3794 3848 for tfile in self.tempfiles:
3795 3849 try:
3796 3850 tfile.unlink()
3851 self.tempfiles.remove(tfile)
3797 3852 except FileNotFoundError:
3798 3853 pass
3799
3854 del self.tempfiles
3800 3855 for tdir in self.tempdirs:
3801 3856 try:
3802 3857 tdir.rmdir()
3858 self.tempdirs.remove(tdir)
3803 3859 except FileNotFoundError:
3804 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 3864 # Run user hooks
3810 3865 self.hooks.shutdown_hook()
@@ -618,6 +618,9 b' class Magics(Configurable):'
618 618 posix = kw.get('posix', os.name == 'posix')
619 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 624 # Check if we have more than one argument to warrant extra processing:
622 625 odict = {} # Dictionary with options
623 626 args = arg_str.split()
@@ -629,10 +632,18 b' class Magics(Configurable):'
629 632 try:
630 633 opts,args = getopt(argv, opt_str, long_opts)
631 634 except GetoptError as e:
632 raise UsageError('%s ( allowed: "%s" %s)' % (e.msg,opt_str,
633 " ".join(long_opts))) from e
635 raise UsageError(
636 '%s ( allowed: "%s" %s)' % (e.msg, opt_str, " ".join(long_opts))
637 ) from e
634 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 647 o = o[2:]
637 648 else:
638 649 o = o[1:]
@@ -649,7 +660,10 b' class Magics(Configurable):'
649 660 # Prepare opts,args for return
650 661 opts = Struct(odict)
651 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 668 return opts,args
655 669
@@ -104,16 +104,32 b' class AutoMagics(Magics):'
104 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 120 if parameter_s:
121 if not parameter_s in map(str, valid_modes.keys()):
122 error(errorMessage())
123 return
108 124 arg = int(parameter_s)
109 125 else:
110 126 arg = 'toggle'
111 127
112 if not arg in (0, 1, 2, 'toggle'):
113 error('Valid modes: (0->Off, 1->Smart, 2->Full')
128 if not arg in (*list(valid_modes.keys()), "toggle"):
129 error(errorMessage())
114 130 return
115 131
116 if arg in (0, 1, 2):
132 if arg in (valid_modes.keys()):
117 133 self.shell.autocall = arg
118 134 else: # toggle
119 135 if self.shell.autocall:
@@ -125,4 +141,4 b' class AutoMagics(Magics):'
125 141 except AttributeError:
126 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 493 %gui qt5 # enable PyQt5 event loop integration
494 494 %gui gtk # enable PyGTK event loop integration
495 495 %gui gtk3 # enable Gtk3 event loop integration
496 %gui gtk4 # enable Gtk4 event loop integration
496 497 %gui tk # enable Tk event loop integration
497 498 %gui osx # enable Cocoa event loop integration
498 499 # (requires %matplotlib 1.1)
@@ -20,7 +20,7 b' import re'
20 20 import sys
21 21 import ast
22 22 from itertools import chain
23 from urllib.request import urlopen
23 from urllib.request import Request, urlopen
24 24 from urllib.parse import urlencode
25 25 from pathlib import Path
26 26
@@ -29,6 +29,7 b' from IPython.core.error import TryNext, StdinNotImplementedError, UsageError'
29 29 from IPython.core.macro import Macro
30 30 from IPython.core.magic import Magics, magics_class, line_magic
31 31 from IPython.core.oinspect import find_file, find_source_lines
32 from IPython.core.release import version
32 33 from IPython.testing.skipdoctest import skip_doctest
33 34 from IPython.utils.contexts import preserve_keys
34 35 from IPython.utils.path import get_py_filename
@@ -201,6 +202,9 b' class CodeMagics(Magics):'
201 202 This function uses the same syntax as %history for input ranges,
202 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 208 It adds a '.py' extension to the file if you don't do so yourself, and
205 209 it asks for confirmation before overwriting existing files.
206 210
@@ -245,20 +249,25 b' class CodeMagics(Magics):'
245 249
246 250 @line_magic
247 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 254 Usage:\\
251 %pastebin [-d "Custom description"] 1-7
255 %pastebin [-d "Custom description"][-e 24] 1-7
252 256
253 257 The argument can be an input history range, a filename, or the name of a
254 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 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 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 272 try:
264 273 code = self.shell.find_user_code(args)
@@ -266,13 +275,30 b' class CodeMagics(Magics):'
266 275 print(e.args[0])
267 276 return
268 277
269 post_data = urlencode({
270 "title": opts.get('d', "Pasted from IPython"),
271 "syntax": "python3",
272 "content": code
273 }).encode('utf-8')
278 expiry_days = 7
279 try:
280 expiry_days = int(opts.get("e", 7))
281 except ValueError as e:
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 302 return response.headers.get('Location')
277 303
278 304 @line_magic
@@ -295,6 +321,9 b' class CodeMagics(Magics):'
295 321 where source can be a filename, URL, input history range, macro, or
296 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 327 Options:
299 328
300 329 -r <lines>: Specify lines or ranges of lines to load from the source.
@@ -313,6 +342,7 b' class CodeMagics(Magics):'
313 342 confirmation before loading source with more than 200 000 characters, unless
314 343 -y flag is passed or if the frontend does not support raw_input::
315 344
345 %load
316 346 %load myscript.py
317 347 %load 7-27
318 348 %load myMacro
@@ -324,13 +354,7 b' class CodeMagics(Magics):'
324 354 %load -n my_module.wonder_function
325 355 """
326 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 357 search_ns = 'n' in opts
333
334 358 contents = self.shell.find_user_code(args, search_ns=search_ns)
335 359
336 360 if 's' in opts:
@@ -1073,8 +1073,9 b' class ExecutionMagics(Magics):'
1073 1073 does not matter as long as results from timeit.py are not mixed with
1074 1074 those from %timeit."""
1075 1075
1076 opts, stmt = self.parse_options(line,'n:r:tcp:qo',
1077 posix=False, strict=False)
1076 opts, stmt = self.parse_options(
1077 line, "n:r:tcp:qo", posix=False, strict=False, preserve_non_opts=True
1078 )
1078 1079 if stmt == "" and cell is None:
1079 1080 return
1080 1081
@@ -1234,11 +1235,15 b' class ExecutionMagics(Magics):'
1234 1235 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
1235 1236 Wall time: 0.00
1236 1237
1237 Note that the time needed by Python to compile the given expression
1238 will be reported if it is more than 0.1s. In this example, the
1239 actual exponentiation is done by Python at compilation time, so while
1240 the expression can take a noticeable amount of time to compute, that
1241 time is purely due to the compilation:
1238
1239 .. note::
1240 The time needed by Python to compile the given expression will be
1241 reported if it is more than 0.1s.
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 1248 In [5]: %time 3**9999;
1244 1249 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
@@ -1249,7 +1254,6 b' class ExecutionMagics(Magics):'
1249 1254 Wall time: 0.00 s
1250 1255 Compiler : 0.78 s
1251 1256 """
1252
1253 1257 # fail immediately if the given expression can't be compiled
1254 1258
1255 1259 if line and cell:
@@ -184,14 +184,12 b' class HistoryMagics(Magics):'
184 184 n = 10 if limit is None else limit
185 185 hist = history_manager.get_tail(n, raw=raw, output=get_output)
186 186 else:
187 if args.range: # Get history by ranges
188 187 if args.pattern:
189 188 range_pattern = "*" + " ".join(args.pattern) + "*"
190 189 print_nums = True
191 hist = history_manager.get_range_by_str(" ".join(args.range),
192 raw, get_output)
193 else: # Just get history for the current session
194 hist = history_manager.get_range(raw=raw, output=get_output)
190 hist = history_manager.get_range_by_str(
191 " ".join(args.range), raw, get_output
192 )
195 193
196 194 # We could be displaying the entire history, so let's not try to pull
197 195 # it into a list in memory. Anything that needs more space will just
@@ -282,6 +280,7 b' class HistoryMagics(Magics):'
282 280 return
283 281 else:
284 282 self.shell.set_next_input(cmd.rstrip())
283 return
285 284 print("Couldn't evaluate or find in history:", arg)
286 285
287 286 @line_magic
@@ -301,6 +300,14 b' class HistoryMagics(Magics):'
301 300 opts, args = self.parse_options(parameter_s, 'l:g:', mode='string')
302 301 if "l" in opts: # Last n lines
303 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 311 hist = self.shell.history_manager.get_tail(n)
305 312 elif "g" in opts: # Search
306 313 p = "*"+opts['g']+"*"
@@ -302,33 +302,36 b' class OSMagics(Magics):'
302 302 """Change the current working directory.
303 303
304 304 This command automatically maintains an internal list of directories
305 you visit during your IPython session, in the variable _dh. The
306 command %dhist shows this history nicely formatted. You can also
307 do 'cd -<tab>' to see directory history conveniently.
308
305 you visit during your IPython session, in the variable ``_dh``. The
306 command :magic:`%dhist` shows this history nicely formatted. You can
307 also do ``cd -<tab>`` to see directory history conveniently.
309 308 Usage:
310 309
311 cd 'dir': changes to directory 'dir'.
312
313 cd -: changes to the last visited directory.
314
315 cd -<n>: changes to the n-th directory in the directory history.
310 - ``cd 'dir'``: changes to directory 'dir'.
311 - ``cd -``: changes to the last visited directory.
312 - ``cd -<n>``: changes to the n-th directory in the directory history.
313 - ``cd --foo``: change to directory that matches 'foo' in 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 323 Options:
325 324
326 -q: quiet. Do not print the working directory after the cd command is
327 executed. By default IPython's cd command does print this directory,
328 since the default prompts do not display path information.
325 -q Be quiet. Do not print the working directory after the
326 cd command is executed. By default IPython's cd
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 336 Examples
334 337 --------
@@ -436,11 +439,11 b' class OSMagics(Magics):'
436 439
437 440 Usage:\\
438 441
439 %env: lists all environment variables/values
440 %env var: get value for var
441 %env var val: set value for var
442 %env var=val: set value for var
443 %env var=$val: set value for var, using python expansion if possible
442 :``%env``: lists all environment variables/values
443 :``%env var``: get value for var
444 :``%env var val``: set value for var
445 :``%env var=val``: set value for var
446 :``%env var=$val``: set value for var, using python expansion if possible
444 447 """
445 448 if parameter_s.strip():
446 449 split = '=' if '=' in parameter_s else ' '
@@ -803,17 +806,16 b' class OSMagics(Magics):'
803 806 to be Python source and will show it with syntax highlighting.
804 807
805 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 814 %pycat myscript.py
809 815 %pycat 7-27
810 816 %pycat myMacro
811 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 819 try :
818 820 cont = self.shell.find_user_code(parameter_s, skip_encoding_cookie=False)
819 821 except (ValueError, IOError):
@@ -66,7 +66,14 b' class PackagingMagics(Magics):'
66 66 Usage:
67 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 77 print("Note: you may need to restart the kernel to use updated packages.")
71 78
72 79 @line_magic
@@ -88,7 +88,7 b' class PylabMagics(Magics):'
88 88 You can list the available backends using the -l/--list option::
89 89
90 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 92 'gtk', 'tk', 'inline']
93 93 """
94 94 args = magic_arguments.parse_argstring(self.matplotlib, line)
@@ -199,6 +199,7 b' class ScriptMagics(Magics):'
199 199 _handle_stream(process.stderr, args.err, sys.stderr)
200 200 )
201 201 await asyncio.wait([stdout_task, stderr_task])
202 await process.wait()
202 203
203 204 if sys.platform.startswith("win"):
204 205 asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
@@ -212,7 +213,7 b' class ScriptMagics(Magics):'
212 213 *cmd,
213 214 stdout=asyncio.subprocess.PIPE,
214 215 stderr=asyncio.subprocess.PIPE,
215 stdin=asyncio.subprocess.PIPE
216 stdin=asyncio.subprocess.PIPE,
216 217 )
217 218 )
218 219 except OSError as e:
@@ -264,7 +265,11 b' class ScriptMagics(Magics):'
264 265 print("Error while terminating subprocess (pid=%i): %s" % (p.pid, e))
265 266 return
266 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 274 def _run_script(self, p, cell, to_close):
270 275 """callback for running the script in the background"""
@@ -5,29 +5,33 b''
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7 from io import BytesIO
8 import warnings
8 9
9 10 from IPython.core.display import _pngxy
10 11 from IPython.utils.decorators import flag_calls
11 12
12 13 # If user specifies a GUI, that dictates the backend, otherwise we read the
13 14 # user's mpl default from the mpl rc structure
14 backends = {'tk': 'TkAgg',
15 'gtk': 'GTKAgg',
16 'gtk3': 'GTK3Agg',
17 'wx': 'WXAgg',
18 'qt4': 'Qt4Agg',
19 'qt5': 'Qt5Agg',
20 'qt': 'Qt5Agg',
21 'osx': 'MacOSX',
22 'nbagg': 'nbAgg',
23 'notebook': 'nbAgg',
24 'agg': 'agg',
25 'svg': 'svg',
26 'pdf': 'pdf',
27 'ps': 'ps',
28 'inline': 'module://ipykernel.pylab.backend_inline',
29 'ipympl': 'module://ipympl.backend_nbagg',
30 'widget': 'module://ipympl.backend_nbagg',
15 backends = {
16 "tk": "TkAgg",
17 "gtk": "GTKAgg",
18 "gtk3": "GTK3Agg",
19 "gtk4": "GTK4Agg",
20 "wx": "WXAgg",
21 "qt4": "Qt4Agg",
22 "qt5": "Qt5Agg",
23 "qt6": "QtAgg",
24 "qt": "Qt5Agg",
25 "osx": "MacOSX",
26 "nbagg": "nbAgg",
27 "notebook": "nbAgg",
28 "agg": "agg",
29 "svg": "svg",
30 "pdf": "pdf",
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 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 43 backend2gui['Qt4Agg'] = 'qt'
40 44 # In the reverse mapping, there are a few extra valid matplotlib backends that
41 45 # map to the same GUI support
42 backend2gui['GTK'] = backend2gui['GTKCairo'] = 'gtk'
43 backend2gui['GTK3Cairo'] = 'gtk3'
44 backend2gui['WX'] = 'wx'
45 backend2gui['CocoaAgg'] = 'osx'
46 backend2gui["GTK"] = backend2gui["GTKCairo"] = "gtk"
47 backend2gui["GTK3Cairo"] = "gtk3"
48 backend2gui["GTK4Cairo"] = "gtk4"
49 backend2gui["WX"] = "wx"
50 backend2gui["CocoaAgg"] = "osx"
46 51 # And some backends that don't need GUI integration
47 del backend2gui['nbAgg']
48 del backend2gui['agg']
49 del backend2gui['svg']
50 del backend2gui['pdf']
51 del backend2gui['ps']
52 del backend2gui['module://ipykernel.pylab.backend_inline']
52 del backend2gui["nbAgg"]
53 del backend2gui["agg"]
54 del backend2gui["svg"]
55 del backend2gui["pdf"]
56 del backend2gui["ps"]
57 del backend2gui["module://matplotlib_inline.backend_inline"]
53 58
54 59 #-----------------------------------------------------------------------------
55 60 # Matplotlib utilities
@@ -274,7 +279,7 b' def find_gui_and_backend(gui=None, gui_select=None):'
274 279 Returns
275 280 -------
276 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 285 import matplotlib
@@ -361,7 +366,12 b' def import_pylab(user_ns, import_all=True):'
361 366
362 367
363 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 376 Parameters
367 377 ----------
@@ -369,51 +379,15 b' def configure_inline_support(shell, backend):'
369 379
370 380 backend : matplotlib backend
371 381 """
372 # If using our svg payload backend, register the post-execution
373 # function that will pick up the results for display. This can only be
374 # done with access to the real shell object.
382 warnings.warn(
383 "`configure_inline_support` is deprecated since IPython 7.23, directly "
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
377 # continuing (such as in terminal-only shells in environments without
378 # zeromq available).
379 try:
380 from ipykernel.pylab.backend_inline import InlineBackend
381 except ImportError:
382 return
383 import matplotlib
389 from matplotlib_inline.backend_inline import (
390 configure_inline_support as configure_inline_support_orig,
391 )
384 392
385 cfg = InlineBackend.instance(parent=shell)
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
393 configure_inline_support_orig(shell, backend)
@@ -1,6 +1,6 b''
1 1 from IPython.utils.capture import capture_output
2 2
3 import nose.tools as nt
3 import pytest
4 4
5 5 def test_alias_lifecycle():
6 6 name = 'test_alias1'
@@ -9,8 +9,8 b' def test_alias_lifecycle():'
9 9 am.clear_aliases()
10 10 am.define_alias(name, cmd)
11 11 assert am.is_alias(name)
12 nt.assert_equal(am.retrieve_alias(name), cmd)
13 nt.assert_in((name, cmd), am.aliases)
12 assert am.retrieve_alias(name) == cmd
13 assert (name, cmd) in am.aliases
14 14
15 15 # Test running the alias
16 16 orig_system = _ip.system
@@ -19,16 +19,16 b' def test_alias_lifecycle():'
19 19 try:
20 20 _ip.run_cell('%{}'.format(name))
21 21 result = [c.strip() for c in result]
22 nt.assert_equal(result, [cmd])
22 assert result == [cmd]
23 23 finally:
24 24 _ip.system = orig_system
25 25
26 26 # Test removing the alias
27 27 am.undefine_alias(name)
28 28 assert not am.is_alias(name)
29 with nt.assert_raises(ValueError):
29 with pytest.raises(ValueError):
30 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 34 def test_alias_args_error():
@@ -38,7 +38,8 b' def test_alias_args_error():'
38 38 with capture_output() as cap:
39 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 44 def test_alias_args_commented():
44 45 """Check that alias correctly ignores 'commented out' args"""
@@ -62,4 +63,4 b' def test_alias_args_commented_nargs():'
62 63 assert am.is_alias(alias_name)
63 64
64 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 18 import sys
19 19
20 20 # Third-party imports
21 import nose.tools as nt
21 import pytest
22 22
23 23 # Our own imports
24 24 from IPython.core import compilerop
@@ -30,13 +30,13 b' from IPython.core import compilerop'
30 30 def test_code_name():
31 31 code = 'x=1'
32 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 36 def test_code_name2():
37 37 code = 'x=1'
38 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 42 def test_cache():
@@ -45,18 +45,18 b' def test_cache():'
45 45 cp = compilerop.CachingCompiler()
46 46 ncache = len(linecache.cache)
47 47 cp.cache('x=1')
48 nt.assert_true(len(linecache.cache) > ncache)
48 assert len(linecache.cache) > ncache
49 49
50 50 def test_proper_default_encoding():
51 51 # Check we're in a proper Python 2 environment (some imports, such
52 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 55 def test_cache_unicode():
56 56 cp = compilerop.CachingCompiler()
57 57 ncache = len(linecache.cache)
58 58 cp.cache(u"t = 'žćčšđ'")
59 nt.assert_true(len(linecache.cache) > ncache)
59 assert len(linecache.cache) > ncache
60 60
61 61 def test_compiler_check_cache():
62 62 """Test the compiler properly manages the cache.
@@ -157,6 +157,11 b' def test_bad_module_all():'
157 157 nt.assert_in('puppies', results)
158 158 for r in results:
159 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 165 finally:
161 166 sys.path.remove(testsdir)
162 167
@@ -176,3 +181,14 b' def test_module_without_init():'
176 181 assert s == []
177 182 finally:
178 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 321 child.expect("ipdb>")
322 322
323 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 135 nt.assert_is_none(img._repr_jpeg_())
136 136
137 137 def _get_inline_config():
138 from ipykernel.pylab.config import InlineBackend
138 from matplotlib_inline.config import InlineBackend
139 139 return InlineBackend.instance()
140 140
141 141
@@ -183,7 +183,7 b' def test_set_matplotlib_formats_kwargs():'
183 183 ip = get_ipython()
184 184 cfg = _get_inline_config()
185 185 cfg.print_figure_kwargs.update(dict(foo='bar'))
186 kwargs = dict(quality=10)
186 kwargs = dict(dpi=150)
187 187 display.set_matplotlib_formats('png', **kwargs)
188 188 formatter = ip.display_formatter.formatters['image/png']
189 189 f = formatter.lookup_by_type(Figure)
@@ -457,6 +457,7 b' def test_display_handle():'
457 457 'update': True,
458 458 })
459 459
460
460 461 def test_image_alt_tag():
461 462 """Simple test for display.Image(args, alt=x,)"""
462 463 thisurl = "http://example.com/image.png"
@@ -480,3 +481,8 b' def test_image_alt_tag():'
480 481 nt.assert_equal(img.alt, "an image")
481 482 _, md = img._repr_png_()
482 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 48 def test_pretty():
49 49 f = PlainTextFormatter()
50 50 f.for_type(A, foo_printer)
51 nt.assert_equal(f(A()), 'foo')
52 nt.assert_equal(f(B()), 'B()')
53 nt.assert_equal(f(GoodPretty()), 'foo')
51 assert f(A()) == "foo"
52 assert f(B()) == "B()"
53 assert f(GoodPretty()) == "foo"
54 54 # Just don't raise an exception for the following:
55 55 f(BadPretty())
56 56
57 57 f.pprint = False
58 nt.assert_equal(f(A()), 'A()')
59 nt.assert_equal(f(B()), 'B()')
60 nt.assert_equal(f(GoodPretty()), 'GoodPretty()')
58 assert f(A()) == "A()"
59 assert f(B()) == "B()"
60 assert f(GoodPretty()) == "GoodPretty()"
61 61
62 62
63 63 def test_deferred():
@@ -66,29 +66,30 b' def test_deferred():'
66 66 def test_precision():
67 67 """test various values for float_precision."""
68 68 f = PlainTextFormatter()
69 nt.assert_equal(f(pi), repr(pi))
69 assert f(pi) == repr(pi)
70 70 f.float_precision = 0
71 71 if numpy:
72 72 po = numpy.get_printoptions()
73 nt.assert_equal(po['precision'], 0)
74 nt.assert_equal(f(pi), '3')
73 assert po["precision"] == 0
74 assert f(pi) == "3"
75 75 f.float_precision = 2
76 76 if numpy:
77 77 po = numpy.get_printoptions()
78 nt.assert_equal(po['precision'], 2)
79 nt.assert_equal(f(pi), '3.14')
80 f.float_precision = '%g'
78 assert po["precision"] == 2
79 assert f(pi) == "3.14"
80 f.float_precision = "%g"
81 81 if numpy:
82 82 po = numpy.get_printoptions()
83 nt.assert_equal(po['precision'], 2)
84 nt.assert_equal(f(pi), '3.14159')
85 f.float_precision = '%e'
86 nt.assert_equal(f(pi), '3.141593e+00')
87 f.float_precision = ''
83 assert po["precision"] == 2
84 assert f(pi) == "3.14159"
85 f.float_precision = "%e"
86 assert f(pi) == "3.141593e+00"
87 f.float_precision = ""
88 88 if numpy:
89 89 po = numpy.get_printoptions()
90 nt.assert_equal(po['precision'], 8)
91 nt.assert_equal(f(pi), repr(pi))
90 assert po["precision"] == 8
91 assert f(pi) == repr(pi)
92
92 93
93 94 def test_bad_precision():
94 95 """test various invalid values for float_precision."""
@@ -260,8 +261,9 b' def test_nowarn_notimplemented():'
260 261 with capture_output() as captured:
261 262 result = f(h)
262 263 nt.assert_is(result, None)
263 nt.assert_equal("", captured.stderr)
264 nt.assert_equal("", captured.stdout)
264 assert "" == captured.stderr
265 assert "" == captured.stdout
266
265 267
266 268 def test_warn_error_for_type():
267 269 f = HTMLFormatter()
@@ -307,7 +309,8 b' class MakePDF(object):'
307 309 def test_pdf_formatter():
308 310 pdf = MakePDF()
309 311 f = PDFFormatter()
310 nt.assert_equal(f(pdf), 'PDF')
312 assert f(pdf) == "PDF"
313
311 314
312 315 def test_print_method_bound():
313 316 f = HTMLFormatter()
@@ -321,8 +324,9 b' def test_print_method_bound():'
321 324
322 325 with capture_output() as captured:
323 326 result = f(MyHTML())
324 nt.assert_equal(result, "hello")
325 nt.assert_equal(captured.stderr, "")
327 assert result == "hello"
328 assert captured.stderr == ""
329
326 330
327 331 def test_print_method_weird():
328 332
@@ -333,7 +337,7 b' def test_print_method_weird():'
333 337 f = HTMLFormatter()
334 338
335 339 text_hat = TextMagicHat()
336 nt.assert_equal(text_hat._repr_html_, '_repr_html_')
340 assert text_hat._repr_html_ == "_repr_html_"
337 341 with capture_output() as captured:
338 342 result = f(text_hat)
339 343
@@ -348,7 +352,7 b' def test_print_method_weird():'
348 352 with capture_output() as captured:
349 353 result = f(call_hat)
350 354
351 nt.assert_equal(result, None)
355 assert result is None
352 356
353 357 class BadReprArgs(object):
354 358 def _repr_html_(self, extra, args):
@@ -369,24 +373,25 b' def test_format_config():'
369 373 with capture_output() as captured:
370 374 result = f(cfg)
371 375 nt.assert_is(result, None)
372 nt.assert_equal(captured.stderr, "")
376 assert captured.stderr == ""
373 377
374 378 with capture_output() as captured:
375 379 result = f(Config)
376 380 nt.assert_is(result, None)
377 nt.assert_equal(captured.stderr, "")
381 assert captured.stderr == ""
382
378 383
379 384 def test_pretty_max_seq_length():
380 385 f = PlainTextFormatter(max_seq_length=1)
381 386 lis = list(range(3))
382 387 text = f(lis)
383 nt.assert_equal(text, '[0, ...]')
388 assert text == "[0, ...]"
384 389 f.max_seq_length = 0
385 390 text = f(lis)
386 nt.assert_equal(text, '[0, 1, 2]')
391 assert text == "[0, 1, 2]"
387 392 text = f(list(range(1024)))
388 393 lines = text.splitlines()
389 nt.assert_equal(len(lines), 1024)
394 assert len(lines) == 1024
390 395
391 396
392 397 def test_ipython_display_formatter():
@@ -411,14 +416,14 b' def test_ipython_display_formatter():'
411 416 no = NotSelfDisplaying()
412 417
413 418 d, md = f.format(no)
414 nt.assert_equal(d, {'text/plain': repr(no)})
415 nt.assert_equal(md, {})
416 nt.assert_equal(catcher, [])
419 assert d == {"text/plain": repr(no)}
420 assert md == {}
421 assert catcher == []
417 422
418 423 d, md = f.format(yes)
419 nt.assert_equal(d, {})
420 nt.assert_equal(md, {})
421 nt.assert_equal(catcher, [yes])
424 assert d == {}
425 assert md == {}
426 assert catcher == [yes]
422 427
423 428 f.ipython_display_formatter.enabled = save_enabled
424 429
@@ -431,8 +436,8 b' def test_json_as_string_deprecated():'
431 436 f = JSONFormatter()
432 437 with warnings.catch_warnings(record=True) as w:
433 438 d = f(JSONString())
434 nt.assert_equal(d, {})
435 nt.assert_equal(len(w), 1)
439 assert d == {}
440 assert len(w) == 1
436 441
437 442
438 443 def test_repr_mime():
@@ -459,18 +464,21 b' def test_repr_mime():'
459 464 d, md = f.format(obj)
460 465 html_f.enabled = save_enabled
461 466
462 nt.assert_equal(sorted(d), ['application/json+test.v2',
463 'image/png',
464 'plain/text',
465 'text/html',
466 'text/plain'])
467 nt.assert_equal(md, {})
467 assert sorted(d) == [
468 "application/json+test.v2",
469 "image/png",
470 "plain/text",
471 "text/html",
472 "text/plain",
473 ]
474 assert md == {}
468 475
469 d, md = f.format(obj, include={'image/png'})
470 nt.assert_equal(list(d.keys()), ['image/png'],
471 'Include should filter out even things from repr_mimebundle')
472 nt.assert_equal(d['image/png'], 'i-overwrite', '_repr_mimebundle_ take precedence')
476 d, md = f.format(obj, include={"image/png"})
477 assert list(d.keys()) == [
478 "image/png"
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 484 def test_pass_correct_include_exclude():
@@ -514,13 +522,14 b' def test_repr_mime_meta():'
514 522 f = get_ipython().display_formatter
515 523 obj = HasReprMimeMeta()
516 524 d, md = f.format(obj)
517 nt.assert_equal(sorted(d), ['image/png', 'text/plain'])
518 nt.assert_equal(md, {
519 'image/png': {
520 'width': 5,
521 'height': 10,
525 assert sorted(d) == ["image/png", "text/plain"]
526 assert md == {
527 "image/png": {
528 "width": 5,
529 "height": 10,
522 530 }
523 })
531 }
532
524 533
525 534 def test_repr_mime_failure():
526 535 class BadReprMime(object):
@@ -530,4 +539,4 b' def test_repr_mime_failure():'
530 539 f = get_ipython().display_formatter
531 540 obj = BadReprMime()
532 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 160 actual = list(extract_hist_ranges(instr))
161 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 171 def test_magic_rerun():
164 172 """Simple test for %rerun (no args -> rerun last line)"""
165 173 ip = get_ipython()
@@ -255,18 +255,18 b' def test_find_assign_op_dedent():'
255 255
256 256 def test_check_complete():
257 257 cc = ipt2.TransformerManager().check_complete
258 nt.assert_equal(cc("a = 1"), ('complete', None))
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))
261 nt.assert_equal(cc("raise = 2"), ('invalid', None))
262 nt.assert_equal(cc("a = [1,\n2,"), ('incomplete', 0))
263 nt.assert_equal(cc(")"), ('incomplete', 0))
264 nt.assert_equal(cc("\\\r\n"), ('incomplete', 0))
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))
267 nt.assert_equal(cc("a \\ "), ('invalid', None)) # Nothing allowed after backslash
268 nt.assert_equal(cc("1\\\n+2"), ('complete', None))
269 nt.assert_equal(cc("exit"), ('complete', None))
258 nt.assert_equal(cc("a = 1"), ("complete", None))
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))
261 nt.assert_equal(cc("raise = 2"), ("invalid", None))
262 nt.assert_equal(cc("a = [1,\n2,"), ("incomplete", 0))
263 nt.assert_equal(cc("(\n))"), ("incomplete", 0))
264 nt.assert_equal(cc("\\\r\n"), ("incomplete", 0))
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))
267 nt.assert_equal(cc("a \\ "), ("invalid", None)) # Nothing allowed after backslash
268 nt.assert_equal(cc("1\\\n+2"), ("complete", None))
269 nt.assert_equal(cc("exit"), ("complete", None))
270 270
271 271 example = dedent("""
272 272 if True:
@@ -297,6 +297,24 b' def test_check_complete_II():'
297 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 318 def test_null_cleanup_transformer():
301 319 manager = ipt2.TransformerManager()
302 320 manager.cleanup_transforms.insert(0, null_cleanup_transformer)
@@ -3,7 +3,7 b''
3 3 Line-based transformers are the simpler ones; token-based transformers are
4 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 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 18 def test_cell_magic():
19 19 for sample, expected in [CELL_MAGIC]:
20 nt.assert_equal(ipt2.cell_magic(sample.splitlines(keepends=True)),
21 expected.splitlines(keepends=True))
20 assert ipt2.cell_magic(sample.splitlines(keepends=True)) == expected.splitlines(
21 keepends=True
22 )
22 23
23 24 CLASSIC_PROMPT = ("""\
24 25 >>> for a in range(5):
@@ -40,8 +41,9 b' for a in range(5):'
40 41
41 42 def test_classic_prompt():
42 43 for sample, expected in [CLASSIC_PROMPT, CLASSIC_PROMPT_L2]:
43 nt.assert_equal(ipt2.classic_prompt(sample.splitlines(keepends=True)),
44 expected.splitlines(keepends=True))
44 assert ipt2.classic_prompt(
45 sample.splitlines(keepends=True)
46 ) == expected.splitlines(keepends=True)
45 47
46 48 IPYTHON_PROMPT = ("""\
47 49 In [1]: for a in range(5):
@@ -61,10 +63,49 b' for a in range(5):'
61 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 98 def test_ipython_prompt():
65 for sample, expected in [IPYTHON_PROMPT, IPYTHON_PROMPT_L2]:
66 nt.assert_equal(ipt2.ipython_prompt(sample.splitlines(keepends=True)),
67 expected.splitlines(keepends=True))
99 for sample, expected in [
100 IPYTHON_PROMPT,
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 110 INDENT_SPACES = ("""\
70 111 if True:
@@ -84,8 +125,9 b' if True:'
84 125
85 126 def test_leading_indent():
86 127 for sample, expected in [INDENT_SPACES, INDENT_TABS]:
87 nt.assert_equal(ipt2.leading_indent(sample.splitlines(keepends=True)),
88 expected.splitlines(keepends=True))
128 assert ipt2.leading_indent(
129 sample.splitlines(keepends=True)
130 ) == expected.splitlines(keepends=True)
89 131
90 132 LEADING_EMPTY_LINES = ("""\
91 133 \t
@@ -111,9 +153,9 b' ONLY_EMPTY_LINES = ("""\\'
111 153
112 154 def test_leading_empty_lines():
113 155 for sample, expected in [LEADING_EMPTY_LINES, ONLY_EMPTY_LINES]:
114 nt.assert_equal(
115 ipt2.leading_empty_lines(sample.splitlines(keepends=True)),
116 expected.splitlines(keepends=True))
156 assert ipt2.leading_empty_lines(
157 sample.splitlines(keepends=True)
158 ) == expected.splitlines(keepends=True)
117 159
118 160 CRLF_MAGIC = ([
119 161 "%%ls\r\n"
@@ -123,4 +165,4 b' CRLF_MAGIC = (['
123 165
124 166 def test_crlf_magic():
125 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 449 # Reset the custom exception hook
450 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 471 def test_mktempfile(self):
453 472 filename = ip.mktempfile()
454 473 # Check that we can open the file again on Windows
@@ -581,9 +600,16 b' class TestSystemRaw(ExitCodeChecks):'
581 600 try:
582 601 self.system("sleep 1 # wont happen")
583 602 except KeyboardInterrupt:
584 self.fail("system call should intercept "
585 "keyboard interrupt from subprocess.call")
586 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGINT)
603 self.fail(
604 "system call should intercept "
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 614 # TODO: Exit codes are currently ignored on Windows.
589 615 class TestSystemPipedExitCode(ExitCodeChecks):
@@ -5,7 +5,7 b''
5 5 #-----------------------------------------------------------------------------
6 6
7 7 # third party
8 import nose.tools as nt
8 import pytest
9 9
10 10 # our own packages
11 11
@@ -31,8 +31,8 b' def test_reset():'
31 31
32 32 # Finally, check that all namespaces have only as many variables as we
33 33 # expect to find in them:
34 nt.assert_equal(len(ip.user_ns), nvars_user_ns)
35 nt.assert_equal(len(ip.user_ns_hidden), nvars_hidden)
34 assert len(ip.user_ns) == nvars_user_ns
35 assert len(ip.user_ns_hidden) == nvars_hidden
36 36
37 37
38 38 # Tests for reporting of exceptions in various modes, handling of SystemExit,
@@ -117,93 +117,103 b' ZeroDivisionError Traceback (most recent call last)'
117 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
172 ---------------------------------------------------------------------------
173 SystemExit Traceback (most recent call last)
174 <BLANKLINE>
175 ... in <module>
176 29 except IndexError:
177 30 mode = 'div'
178 ---> 32 bar(mode)
179 mode = 'exit'
180 <BLANKLINE>
181 ... in bar(mode='exit')
182 20 except:
183 21 stat = 1
184 ---> 22 sysexit(stat, mode)
185 mode = 'exit'
186 stat = 2
187 23 else:
188 24 raise ValueError('Unknown mode')
189 <BLANKLINE>
190 ... in sysexit(stat=2, mode='exit')
191 10 def sysexit(stat, mode):
192 ---> 11 raise SystemExit(stat, 'Mode = %s' % mode)
193 stat = 2
194 mode = 'exit'
195 <BLANKLINE>
196 SystemExit: (2, 'Mode = exit')
197 """
121 # TODO : Marc 2021 – this seem to fail due
122 # to upstream changes in CI for whatever reason.
123 # Commenting for now, to revive someday (maybe?)
124 # nose won't work in 3.10 anyway and we'll have to disable iptest.
125 # thus this likely need to bemigrated to pytest.
126
127
128 # def doctest_tb_sysexit():
129 # """
130 # In [17]: %xmode plain
131 # Exception reporting mode: Plain
132 #
133 # In [18]: %run simpleerr.py exit
134 # An exception has occurred, use %tb to see the full traceback.
135 # SystemExit: (1, 'Mode = exit')
136 #
137 # In [19]: %run simpleerr.py exit 2
138 # An exception has occurred, use %tb to see the full traceback.
139 # SystemExit: (2, 'Mode = exit')
140 #
141 # In [20]: %tb
142 # Traceback (most recent call last):
143 # File ... in <module>
144 # bar(mode)
145 # File ... line 22, in bar
146 # sysexit(stat, mode)
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 208 def test_run_cell():
201 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)
206 complex = textwrap.dedent("""
211 ip.run_cell("a = 10\na+=1")
212 ip.run_cell("assert a == 11\nassert 1")
213
214 assert ip.user_ns["a"] == 11
215 complex = textwrap.dedent(
216 """
207 217 if 1:
208 218 print "hello"
209 219 if 1:
@@ -225,7 +235,7 b' def test_run_cell():'
225 235
226 236 def test_db():
227 237 """Test the internal database used for variable persistence."""
228 ip.db['__unittest_'] = 12
229 nt.assert_equal(ip.db['__unittest_'], 12)
230 del ip.db['__unittest_']
231 assert '__unittest_' not in ip.db
238 ip.db["__unittest_"] = 12
239 assert ip.db["__unittest_"] == 12
240 del ip.db["__unittest_"]
241 assert "__unittest_" not in ip.db
@@ -2,8 +2,8 b''
2 2 """Test IPython.core.logger"""
3 3
4 4 import os.path
5 import pytest
5 6
6 import nose.tools as nt
7 7 from IPython.utils.tempdir import TemporaryDirectory
8 8
9 9 def test_logstart_inaccessible_file():
@@ -12,7 +12,7 b' def test_logstart_inaccessible_file():'
12 12 except IOError:
13 13 pass
14 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 17 try:
18 18 _ip.run_cell("a=1") # Check it doesn't try to log this
@@ -472,6 +472,23 b' def test_parse_options():'
472 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 492 def test_dirops():
476 493 """Test various directory handling operations."""
477 494 # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/')
@@ -1072,6 +1089,29 b' def test_save():'
1072 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 1115 def test_store():
1076 1116 """Test %store."""
1077 1117 ip = get_ipython()
@@ -7,7 +7,7 b''
7 7 #-----------------------------------------------------------------------------
8 8
9 9 import argparse
10 from nose.tools import assert_equal
10 import pytest
11 11
12 12 from IPython.core.magic_arguments import (argument, argument_group, kwds,
13 13 magic_arguments, parse_argstring, real_name)
@@ -74,45 +74,62 b' def foo(self, args):'
74 74
75 75
76 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')
78 assert_equal(getattr(magic_foo1, 'argcmd_name', None), None)
79 assert_equal(real_name(magic_foo1), 'foo1')
80 assert_equal(magic_foo1(None, ''), argparse.Namespace(foo=None))
81 assert hasattr(magic_foo1, 'has_arguments')
82
83 assert_equal(magic_foo2.__doc__, '::\n\n %foo2\n\n A docstring.\n')
84 assert_equal(getattr(magic_foo2, 'argcmd_name', None), None)
85 assert_equal(real_name(magic_foo2), 'foo2')
86 assert_equal(magic_foo2(None, ''), argparse.Namespace())
87 assert hasattr(magic_foo2, 'has_arguments')
88
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')
90 assert_equal(getattr(magic_foo3, 'argcmd_name', None), None)
91 assert_equal(real_name(magic_foo3), 'foo3')
92 assert_equal(magic_foo3(None, ''),
93 argparse.Namespace(bar=None, baz=None, foo=None))
94 assert hasattr(magic_foo3, 'has_arguments')
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')
97 assert_equal(getattr(magic_foo4, 'argcmd_name', None), None)
98 assert_equal(real_name(magic_foo4), 'foo4')
99 assert_equal(magic_foo4(None, ''), argparse.Namespace())
100 assert hasattr(magic_foo4, 'has_arguments')
101
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')
103 assert_equal(getattr(magic_foo5, 'argcmd_name', None), 'frobnicate')
104 assert_equal(real_name(magic_foo5), 'frobnicate')
105 assert_equal(magic_foo5(None, ''), argparse.Namespace(foo=None))
106 assert hasattr(magic_foo5, 'has_arguments')
107
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')
109 assert_equal(getattr(magic_magic_foo, 'argcmd_name', None), None)
110 assert_equal(real_name(magic_magic_foo), 'magic_foo')
111 assert_equal(magic_magic_foo(None, ''), argparse.Namespace(foo=None))
112 assert hasattr(magic_magic_foo, 'has_arguments')
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')
115 assert_equal(getattr(foo, 'argcmd_name', None), None)
116 assert_equal(real_name(foo), 'foo')
117 assert_equal(foo(None, ''), argparse.Namespace(foo=None))
118 assert hasattr(foo, 'has_arguments')
77 assert (
78 magic_foo1.__doc__
79 == "::\n\n %foo1 [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n"
80 )
81 assert getattr(magic_foo1, "argcmd_name", None) == None
82 assert real_name(magic_foo1) == "foo1"
83 assert magic_foo1(None, "") == argparse.Namespace(foo=None)
84 assert hasattr(magic_foo1, "has_arguments")
85
86 assert magic_foo2.__doc__ == "::\n\n %foo2\n\n A docstring.\n"
87 assert getattr(magic_foo2, "argcmd_name", None) == None
88 assert real_name(magic_foo2) == "foo2"
89 assert magic_foo2(None, "") == argparse.Namespace()
90 assert hasattr(magic_foo2, "has_arguments")
91
92 assert (
93 magic_foo3.__doc__
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 )
96 assert getattr(magic_foo3, "argcmd_name", None) == None
97 assert real_name(magic_foo3) == "foo3"
98 assert magic_foo3(None, "") == argparse.Namespace(bar=None, baz=None, foo=None)
99 assert hasattr(magic_foo3, "has_arguments")
100
101 assert (
102 magic_foo4.__doc__
103 == "::\n\n %foo4 [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n"
104 )
105 assert getattr(magic_foo4, "argcmd_name", None) == None
106 assert real_name(magic_foo4) == "foo4"
107 assert magic_foo4(None, "") == argparse.Namespace()
108 assert hasattr(magic_foo4, "has_arguments")
109
110 assert (
111 magic_foo5.__doc__
112 == "::\n\n %frobnicate [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n"
113 )
114 assert getattr(magic_foo5, "argcmd_name", None) == "frobnicate"
115 assert real_name(magic_foo5) == "frobnicate"
116 assert magic_foo5(None, "") == argparse.Namespace(foo=None)
117 assert hasattr(magic_foo5, "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 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 62 def test_find_file():
@@ -76,7 +76,7 b' def test_find_file_decorated1():'
76 76 "My docstring"
77 77
78 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 82 def test_find_file_decorated2():
@@ -92,7 +92,7 b' def test_find_file_decorated2():'
92 92 "My docstring 2"
93 93
94 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 98 def test_find_file_magic():
@@ -167,41 +167,46 b' class SerialLiar(object):'
167 167
168 168 def test_info():
169 169 "Check that Inspector.info fills out various fields as expected."
170 i = inspector.info(Call, oname='Call')
171 nt.assert_equal(i['type_name'], 'type')
170 i = inspector.info(Call, oname="Call")
171 assert i["type_name"] == "type"
172 172 expted_class = str(type(type)) # <class 'type'> (Python 3) or <type 'type'>
173 nt.assert_equal(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})?>")
173 assert i["base_class"] == expted_class
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 178 fname = __file__
176 179 if fname.endswith(".pyc"):
177 180 fname = fname[:-1]
178 181 # case-insensitive comparison needed on some filesystems
179 182 # e.g. Windows:
180 nt.assert_equal(i['file'].lower(), compress_user(fname).lower())
181 nt.assert_equal(i['definition'], None)
182 nt.assert_equal(i['docstring'], Call.__doc__)
183 nt.assert_equal(i['source'], None)
184 nt.assert_true(i['isclass'])
185 nt.assert_equal(i['init_definition'], "Call(x, y=1)")
186 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
183 assert i["file"].lower() == compress_user(fname).lower()
184 assert i["definition"] == None
185 assert i["docstring"] == Call.__doc__
186 assert i["source"] == None
187 nt.assert_true(i["isclass"])
188 assert i["init_definition"] == "Call(x, y=1)"
189 assert i["init_docstring"] == Call.__init__.__doc__
187 190
188 191 i = inspector.info(Call, detail_level=1)
189 nt.assert_not_equal(i['source'], None)
190 nt.assert_equal(i['docstring'], None)
192 nt.assert_not_equal(i["source"], None)
193 assert i["docstring"] == None
191 194
192 195 c = Call(1)
193 196 c.__doc__ = "Modified instance docstring"
194 197 i = inspector.info(c)
195 nt.assert_equal(i['type_name'], 'Call')
196 nt.assert_equal(i['docstring'], "Modified instance docstring")
197 nt.assert_equal(i['class_docstring'], Call.__doc__)
198 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
199 nt.assert_equal(i['call_docstring'], Call.__call__.__doc__)
198 assert i["type_name"] == "Call"
199 assert i["docstring"] == "Modified instance docstring"
200 assert i["class_docstring"] == Call.__doc__
201 assert i["init_docstring"] == Call.__init__.__doc__
202 assert i["call_docstring"] == Call.__call__.__doc__
203
200 204
201 205 def test_class_signature():
202 info = inspector.info(HasSignature, 'HasSignature')
203 nt.assert_equal(info['init_definition'], "HasSignature(test)")
204 nt.assert_equal(info['init_docstring'], HasSignature.__init__.__doc__)
206 info = inspector.info(HasSignature, "HasSignature")
207 assert info["init_definition"] == "HasSignature(test)"
208 assert info["init_docstring"] == HasSignature.__init__.__doc__
209
205 210
206 211 def test_info_awkward():
207 212 # Just test that this doesn't throw an error.
@@ -231,8 +236,9 b' def f_kwarg(pos, *, kwonly):'
231 236 pass
232 237
233 238 def test_definition_kwonlyargs():
234 i = inspector.info(f_kwarg, oname='f_kwarg') # analysis:ignore
235 nt.assert_equal(i['definition'], "f_kwarg(pos, *, kwonly)")
239 i = inspector.info(f_kwarg, oname="f_kwarg") # analysis:ignore
240 assert i["definition"] == "f_kwarg(pos, *, kwonly)"
241
236 242
237 243 def test_getdoc():
238 244 class A(object):
@@ -253,9 +259,9 b' def test_getdoc():'
253 259 b = B()
254 260 c = C()
255 261
256 nt.assert_equal(oinspect.getdoc(a), "standard docstring")
257 nt.assert_equal(oinspect.getdoc(b), "custom docstring")
258 nt.assert_equal(oinspect.getdoc(c), "standard docstring")
262 assert oinspect.getdoc(a) == "standard docstring"
263 assert oinspect.getdoc(b) == "custom docstring"
264 assert oinspect.getdoc(c) == "standard docstring"
259 265
260 266
261 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 305 """This is `foobar` property."""
300 306 pass
301 307
302 ip.user_ns['a_obj'] = A()
303 nt.assert_equal(
304 'This is `foobar` property.',
305 ip.object_inspect('a_obj.foobar', detail_level=0)['docstring'])
308 ip.user_ns["a_obj"] = A()
309 assert (
310 "This is `foobar` property."
311 == ip.object_inspect("a_obj.foobar", detail_level=0)["docstring"]
312 )
306 313
307 ip.user_ns['a_cls'] = A
308 nt.assert_equal(
309 'This is `foobar` property.',
310 ip.object_inspect('a_cls.foobar', detail_level=0)['docstring'])
314 ip.user_ns["a_cls"] = A
315 assert (
316 "This is `foobar` property."
317 == ip.object_inspect("a_cls.foobar", detail_level=0)["docstring"]
318 )
311 319
312 320
313 321 def test_pdef():
@@ -404,7 +412,7 b' def test_render_signature_short():'
404 412 signature(short_fun),
405 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 418 def test_render_signature_long():
@@ -3,7 +3,7 b''
3 3 #-----------------------------------------------------------------------------
4 4 # Imports
5 5 #-----------------------------------------------------------------------------
6 import nose.tools as nt
6 import pytest
7 7
8 8 from IPython.core.prefilter import AutocallChecker
9 9
@@ -19,7 +19,7 b' def test_prefilter():'
19 19 ]
20 20
21 21 for raw, correct in pairs:
22 nt.assert_equal(ip.prefilter(raw), correct)
22 assert ip.prefilter(raw) == correct
23 23
24 24 def test_prefilter_shadowed():
25 25 def dummy_magic(line): pass
@@ -32,16 +32,16 b' def test_prefilter_shadowed():'
32 32 # These should not be transformed - they are shadowed by other names
33 33 for name in ['if', 'zip', 'get_ipython']: # keyword, builtin, global
34 34 ip.register_magic_function(dummy_magic, magic_name=name)
35 res = ip.prefilter(name+' foo')
36 nt.assert_equal(res, name+' foo')
37 del ip.magics_manager.magics['line'][name]
35 res = ip.prefilter(name + " foo")
36 assert res == name + " foo"
37 del ip.magics_manager.magics["line"][name]
38 38
39 39 # These should be transformed
40 40 for name in ['fi', 'piz', 'nohtypi_teg']:
41 41 ip.register_magic_function(dummy_magic, magic_name=name)
42 res = ip.prefilter(name+' foo')
43 nt.assert_not_equal(res, name+' foo')
44 del ip.magics_manager.magics['line'][name]
42 res = ip.prefilter(name + " foo")
43 assert res != name + " foo"
44 del ip.magics_manager.magics["line"][name]
45 45
46 46 finally:
47 47 ip.automagic = prev_automagic_state
@@ -52,9 +52,9 b' def test_autocall_binops():'
52 52 f = lambda x: x
53 53 ip.user_ns['f'] = f
54 54 try:
55 nt.assert_equal(ip.prefilter('f 1'),'f(1)')
56 for t in ['f +1', 'f -1']:
57 nt.assert_equal(ip.prefilter(t), t)
55 assert ip.prefilter("f 1") == "f(1)"
56 for t in ["f +1", "f -1"]:
57 assert ip.prefilter(t) == t
58 58
59 59 # Run tests again with a more permissive exclude_regexp, which will
60 60 # allow transformation of binary operations ('f -1' -> 'f(-1)').
@@ -66,8 +66,8 b' def test_autocall_binops():'
66 66 ac.exclude_regexp = r'^[,&^\|\*/]|^is |^not |^in |^and |^or '
67 67 pm.sort_checkers()
68 68
69 nt.assert_equal(ip.prefilter('f -1'), 'f(-1)')
70 nt.assert_equal(ip.prefilter('f +1'), 'f(+1)')
69 assert ip.prefilter("f -1") == "f(-1)"
70 assert ip.prefilter("f +1") == "f(+1)"
71 71 finally:
72 72 pm.unregister_checker(ac)
73 73 finally:
@@ -88,7 +88,7 b' def test_issue_114():'
88 88 try:
89 89 for mgk in ip.magics_manager.lsmagic()['line']:
90 90 raw = template % mgk
91 nt.assert_equal(ip.prefilter(raw), raw)
91 assert ip.prefilter(raw) == raw
92 92 finally:
93 93 ip.prefilter_manager.multi_line_specials = msp
94 94
@@ -121,7 +121,7 b' def test_autocall_should_support_unicode():'
121 121 ip.magic('autocall 2')
122 122 ip.user_ns['π'] = lambda x: x
123 123 try:
124 nt.assert_equal(ip.prefilter('π 3'),'π(3)')
124 assert ip.prefilter("π 3") == "π(3)"
125 125 finally:
126 126 ip.magic('autocall 0')
127 127 del ip.user_ns['π']
@@ -15,6 +15,7 b' from nose import SkipTest'
15 15 import nose.tools as nt
16 16
17 17 from matplotlib import pyplot as plt
18 import matplotlib_inline
18 19 import numpy as np
19 20
20 21 from IPython.core.getipython import get_ipython
@@ -146,8 +147,16 b' def test_import_pylab():'
146 147 nt.assert_true('plt' in ns)
147 148 nt.assert_equal(ns['np'], np)
148 149
150 from traitlets.config import Config
151
152
149 153 class TestPylabSwitch(object):
150 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 160 def enable_gui(self, gui):
152 161 pass
153 162
@@ -167,18 +176,21 b' class TestPylabSwitch(object):'
167 176 pt.activate_matplotlib = act_mpl
168 177 self._save_ip = pt.import_pylab
169 178 pt.import_pylab = lambda *a,**kw:None
170 self._save_cis = pt.configure_inline_support
171 pt.configure_inline_support = lambda *a,**kw:None
179 self._save_cis = matplotlib_inline.backend_inline.configure_inline_support
180 matplotlib_inline.backend_inline.configure_inline_support = (
181 lambda *a, **kw: None
182 )
172 183
173 184 def teardown(self):
174 185 pt.activate_matplotlib = self._save_am
175 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 188 import matplotlib
178 189 matplotlib.rcParams = self._saved_rcParams
179 190 matplotlib.rcParamsOrig = self._saved_rcParamsOrig
180 191
181 192 def test_qt(self):
193
182 194 s = self.Shell()
183 195 gui, backend = s.enable_matplotlib(None)
184 196 nt.assert_equal(gui, 'qt')
@@ -169,6 +169,31 b' def _format_traceback_lines(lines, Colors, has_colors, lvals):'
169 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 198 # Module classes
174 199 class TBTools(colorable.Colorable):
@@ -414,21 +439,31 b' class ListTB(TBTools):'
414 439 Colors = self.Colors
415 440 list = []
416 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' % \
418 (Colors.filename, filename, Colors.Normal,
419 Colors.lineno, lineno, Colors.Normal,
420 Colors.name, name, Colors.Normal)
442 item = " %s, line %s%d%s, in %s%s%s\n" % (
443 _format_filename(filename, Colors.filename, Colors.Normal),
444 Colors.lineno,
445 lineno,
446 Colors.Normal,
447 Colors.name,
448 name,
449 Colors.Normal,
450 )
421 451 if line:
422 452 item += ' %s\n' % line.strip()
423 453 list.append(item)
424 454 # Emphasize the last entry
425 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' % \
427 (Colors.normalEm,
428 Colors.filenameEm, filename, Colors.normalEm,
429 Colors.linenoEm, lineno, Colors.normalEm,
430 Colors.nameEm, name, Colors.normalEm,
431 Colors.Normal)
456 item = "%s %s, line %s%d%s, in %s%s%s%s\n" % (
457 Colors.normalEm,
458 _format_filename(filename, Colors.filenameEm, Colors.normalEm),
459 Colors.linenoEm,
460 lineno,
461 Colors.normalEm,
462 Colors.nameEm,
463 name,
464 Colors.normalEm,
465 Colors.Normal,
466 )
432 467 if line:
433 468 item += '%s %s%s\n' % (Colors.line, line.strip(),
434 469 Colors.Normal)
@@ -463,13 +498,21 b' class ListTB(TBTools):'
463 498 lineno = value.lineno
464 499 textline = linecache.getline(value.filename, value.lineno)
465 500 else:
466 lineno = 'unknown'
467 textline = ''
468 list.append('%s File %s"%s"%s, line %s%s%s\n' % \
469 (Colors.normalEm,
470 Colors.filenameEm, py3compat.cast_unicode(value.filename), Colors.normalEm,
471 Colors.linenoEm, lineno, Colors.Normal ))
472 if textline == '':
501 lineno = "unknown"
502 textline = ""
503 list.append(
504 "%s %s, line %s%s%s\n"
505 % (
506 Colors.normalEm,
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 516 textline = py3compat.cast_unicode(value.text, "utf-8")
474 517
475 518 if textline is not None:
@@ -581,25 +624,19 b' class VerboseTB(TBTools):'
581 624 Colors = self.Colors # just a shorthand + quicker name lookup
582 625 ColorsNormal = Colors.Normal # used a lot
583 626
584
585
586 627 if isinstance(frame_info, stack_data.RepeatedFrames):
587 628 return ' %s[... skipping similar frames: %s]%s\n' % (
588 629 Colors.excName, frame_info.description, ColorsNormal)
589 630
590 631 indent = ' ' * INDENT_SIZE
591 632 em_normal = '%s\n%s%s' % (Colors.valEm, indent, ColorsNormal)
592 tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
593 633 tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm,
594 634 ColorsNormal)
595 635 tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \
596 636 (Colors.vName, Colors.valEm, ColorsNormal)
597 tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal)
598 637 tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal)
599 638
600 file = frame_info.filename
601 file = py3compat.cast_unicode(file, util_path.fs_encoding)
602 link = tpl_link % util_path.compress_user(file)
639 link = _format_filename(frame_info.filename, Colors.filenameEm, ColorsNormal)
603 640 args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame)
604 641
605 642 func = frame_info.executing.code_qualname()
@@ -634,12 +671,20 b' class VerboseTB(TBTools):'
634 671 lvals = ''
635 672 lvals_list = []
636 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 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 684 if lvals_list:
640 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 689 result += ''.join(_format_traceback_lines(frame_info.lines, Colors, self.has_colors, lvals))
645 690 return result
@@ -48,6 +48,11 b' The following magic commands are provided:'
48 48 Reload all modules (except those excluded by ``%aimport``) every
49 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 56 ``%aimport``
52 57
53 58 List modules which are to be automatically imported or not to be imported.
@@ -124,14 +129,18 b' from imp import reload'
124 129 # Autoreload functionality
125 130 #------------------------------------------------------------------------------
126 131
127 class ModuleReloader(object):
132
133 class ModuleReloader:
128 134 enabled = False
129 135 """Whether this reloader is enabled"""
130 136
131 137 check_all = True
132 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 144 # Modules that failed to reload: {module: mtime-on-failed-reload, ...}
136 145 self.failed = {}
137 146 # Modules specially marked as autoreloadable.
@@ -142,6 +151,7 b' class ModuleReloader(object):'
142 151 self.old_objects = {}
143 152 # Module modification timestamps
144 153 self.modules_mtimes = {}
154 self.shell = shell
145 155
146 156 # Cache module modification times
147 157 self.check(check_all=True, do_reload=False)
@@ -176,22 +186,22 b' class ModuleReloader(object):'
176 186 self.mark_module_reloadable(module_name)
177 187
178 188 import_module(module_name)
179 top_name = module_name.split('.')[0]
189 top_name = module_name.split(".")[0]
180 190 top_module = sys.modules[top_name]
181 191 return top_module, top_name
182 192
183 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 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 198 # we cannot reload(__main__) or reload(__mp_main__)
189 199 return None, None
190 200
191 201 filename = module.__file__
192 202 path, ext = os.path.splitext(filename)
193 203
194 if ext.lower() == '.py':
204 if ext.lower() == ".py":
195 205 py_filename = filename
196 206 else:
197 207 try:
@@ -242,21 +252,35 b' class ModuleReloader(object):'
242 252 # If we've reached this point, we should try to reload the module
243 253 if do_reload:
244 254 try:
255 if self.autoload_obj:
256 superreload(m, reload, self.old_objects, self.shell)
257 else:
245 258 superreload(m, reload, self.old_objects)
246 259 if py_filename in self.failed:
247 260 del self.failed[py_filename]
248 261 except:
249 print("[autoreload of %s failed: %s]" % (
250 modname, traceback.format_exc(10)), file=sys.stderr)
262 print(
263 "[autoreload of {} failed: {}]".format(
264 modname, traceback.format_exc(10)
265 ),
266 file=sys.stderr,
267 )
251 268 self.failed[py_filename] = pymtime
252 269
270
253 271 #------------------------------------------------------------------------------
254 272 # superreload
255 273 #------------------------------------------------------------------------------
256 274
257 275
258 func_attrs = ['__code__', '__defaults__', '__doc__',
259 '__closure__', '__globals__', '__dict__']
276 func_attrs = [
277 "__code__",
278 "__defaults__",
279 "__doc__",
280 "__closure__",
281 "__globals__",
282 "__dict__",
283 ]
260 284
261 285
262 286 def update_function(old, new):
@@ -299,7 +323,8 b' def update_class(old, new):'
299 323 pass
300 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 329 try:
305 330 setattr(old, key, getattr(new, key))
@@ -329,16 +354,18 b' def isinstance2(a, b, typ):'
329 354
330 355
331 356 UPDATE_RULES = [
332 (lambda a, b: isinstance2(a, b, type),
333 update_class),
334 (lambda a, b: isinstance2(a, b, types.FunctionType),
335 update_function),
336 (lambda a, b: isinstance2(a, b, property),
337 update_property),
357 (lambda a, b: isinstance2(a, b, type), update_class),
358 (lambda a, b: isinstance2(a, b, types.FunctionType), update_function),
359 (lambda a, b: isinstance2(a, b, property), update_property),
338 360 ]
339 UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.MethodType),
340 lambda a, b: update_function(a.__func__, b.__func__)),
341 ])
361 UPDATE_RULES.extend(
362 [
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 371 def update_generic(a, b):
@@ -349,14 +376,45 b' def update_generic(a, b):'
349 376 return False
350 377
351 378
352 class StrongRef(object):
379 class StrongRef:
353 380 def __init__(self, obj):
354 381 self.obj = obj
382
355 383 def __call__(self):
356 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 418 """Enhanced version of the builtin reload function.
361 419
362 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 430 # collect old objects in the module
373 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 433 continue
376 434 key = (module.__name__, name)
377 435 try:
@@ -385,8 +443,8 b' def superreload(module, reload=reload, old_objects=None):'
385 443 old_dict = module.__dict__.copy()
386 444 old_name = module.__name__
387 445 module.__dict__.clear()
388 module.__dict__['__name__'] = old_name
389 module.__dict__['__loader__'] = old_dict['__loader__']
446 module.__dict__["__name__"] = old_name
447 module.__dict__["__loader__"] = old_dict["__loader__"]
390 448 except (TypeError, AttributeError, KeyError):
391 449 pass
392 450
@@ -400,12 +458,21 b' def superreload(module, reload=reload, old_objects=None):'
400 458 # iterate over all objects and update functions & classes
401 459 for name, new_obj in list(module.__dict__.items()):
402 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 471 new_refs = []
406 472 for old_ref in old_objects[key]:
407 473 old_obj = old_ref()
408 if old_obj is None: continue
474 if old_obj is None:
475 continue
409 476 new_refs.append(old_ref)
410 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 484 return module
418 485
486
419 487 #------------------------------------------------------------------------------
420 488 # IPython connectivity
421 489 #------------------------------------------------------------------------------
422 490
423 491 from IPython.core.magic import Magics, magics_class, line_magic
424 492
493
425 494 @magics_class
426 495 class AutoreloadMagics(Magics):
427 496 def __init__(self, *a, **kw):
428 super(AutoreloadMagics, self).__init__(*a, **kw)
429 self._reloader = ModuleReloader()
497 super().__init__(*a, **kw)
498 self._reloader = ModuleReloader(self.shell)
430 499 self._reloader.check_all = False
500 self._reloader.autoload_obj = False
431 501 self.loaded_modules = set(sys.modules)
432 502
433 503 @line_magic
434 def autoreload(self, parameter_s=''):
504 def autoreload(self, parameter_s=""):
435 505 r"""%autoreload => Reload modules automatically
436 506
437 507 %autoreload
@@ -475,19 +545,24 b' class AutoreloadMagics(Magics):'
475 545 autoreloaded.
476 546
477 547 """
478 if parameter_s == '':
548 if parameter_s == "":
479 549 self._reloader.check(True)
480 elif parameter_s == '0':
550 elif parameter_s == "0":
481 551 self._reloader.enabled = False
482 elif parameter_s == '1':
552 elif parameter_s == "1":
483 553 self._reloader.check_all = False
484 554 self._reloader.enabled = True
485 elif parameter_s == '2':
555 elif parameter_s == "2":
486 556 self._reloader.check_all = True
487 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 564 @line_magic
490 def aimport(self, parameter_s='', stream=None):
565 def aimport(self, parameter_s="", stream=None):
491 566 """%aimport => Import modules for automatic reloading.
492 567
493 568 %aimport
@@ -511,13 +586,13 b' class AutoreloadMagics(Magics):'
511 586 if self._reloader.check_all:
512 587 stream.write("Modules to reload:\nall-except-skipped\n")
513 588 else:
514 stream.write("Modules to reload:\n%s\n" % ' '.join(to_reload))
515 stream.write("\nModules to skip:\n%s\n" % ' '.join(to_skip))
516 elif modname.startswith('-'):
589 stream.write("Modules to reload:\n%s\n" % " ".join(to_reload))
590 stream.write("\nModules to skip:\n%s\n" % " ".join(to_skip))
591 elif modname.startswith("-"):
517 592 modname = modname[1:]
518 593 self._reloader.mark_module_skipped(modname)
519 594 else:
520 for _module in ([_.strip() for _ in modname.split(',')]):
595 for _module in [_.strip() for _ in modname.split(",")]:
521 596 top_module, top_name = self._reloader.aimport_module(_module)
522 597
523 598 # Inject module to user namespace
@@ -531,8 +606,7 b' class AutoreloadMagics(Magics):'
531 606 pass
532 607
533 608 def post_execute_hook(self):
534 """Cache the modification times of any modules imported in this execution
535 """
609 """Cache the modification times of any modules imported in this execution"""
536 610 newly_loaded_modules = set(sys.modules) - self.loaded_modules
537 611 for modname in newly_loaded_modules:
538 612 _, pymtime = self._reloader.filename_and_mtime(sys.modules[modname])
@@ -546,5 +620,5 b' def load_ipython_extension(ip):'
546 620 """Load the extension in IPython."""
547 621 auto_reload = AutoreloadMagics(ip)
548 622 ip.register_magics(auto_reload)
549 ip.events.register('pre_run_cell', auto_reload.pre_run_cell)
550 ip.events.register('post_execute', auto_reload.post_execute_hook)
623 ip.events.register("pre_run_cell", auto_reload.pre_run_cell)
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 36 noop = lambda *a, **kw: None
37 37
38 class FakeShell:
39 38
39 class FakeShell:
40 40 def __init__(self):
41 41 self.ns = {}
42 42 self.user_ns = self.ns
43 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 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 48 register_magics = set_hook = noop
49 49
50 50 def run_code(self, code):
51 self.events.trigger('pre_run_cell')
51 self.events.trigger("pre_run_cell")
52 52 exec(code, self.user_ns)
53 53 self.auto_magics.post_execute_hook()
54 54
@@ -111,20 +111,22 b' class Fixture(TestCase):'
111 111 time.sleep(1.05)
112 112
113 113 # Write
114 with open(filename, 'w') as f:
114 with open(filename, "w") as f:
115 115 f.write(content)
116 116
117 117 def new_module(self, code):
118 118 code = textwrap.dedent(code)
119 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 121 f.write(code)
122 122 return mod_name, mod_fn
123 123
124
124 125 #-----------------------------------------------------------------------------
125 126 # Test automatic reloading
126 127 #-----------------------------------------------------------------------------
127 128
129
128 130 def pickle_get_current_class(obj):
129 131 """
130 132 Original issue comes from pickle; hence the name.
@@ -136,25 +138,36 b' def pickle_get_current_class(obj):'
136 138 obj2 = getattr(obj2, subpath)
137 139 return obj2
138 140
139 class TestAutoreload(Fixture):
140 141
142 class TestAutoreload(Fixture):
141 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 147 from enum import Enum
144 148 class MyEnum(Enum):
145 149 A = 'A'
146 150 B = 'B'
147 """))
151 """
152 )
153 )
148 154 self.shell.magic_autoreload("2")
149 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 160 from enum import Enum
152 161 class MyEnum(Enum):
153 162 A = 'A'
154 163 B = 'B'
155 164 C = 'C'
156 """))
157 with tt.AssertNotPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
165 """
166 ),
167 )
168 with tt.AssertNotPrints(
169 ("[autoreload of %s failed:" % mod_name), channel="stderr"
170 ):
158 171 self.shell.run_code("pass") # trigger another reload
159 172
160 173 def test_reload_class_type(self):
@@ -195,7 +208,9 b' class TestAutoreload(Fixture):'
195 208
196 209 def test_reload_class_attributes(self):
197 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 214 class MyClass:
200 215
201 216 def __init__(self, a=10):
@@ -241,16 +256,99 b' class TestAutoreload(Fixture):'
241 256
242 257 self.shell.run_code("second = MyClass(5)")
243 258
244 for object_name in {'first', 'second'}:
245 self.shell.run_code("{object_name}.power(5)".format(object_name=object_name))
259 for object_name in {"first", "second"}:
260 self.shell.run_code(f"{object_name}.power(5)")
246 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 263 with nt.assert_raises(AttributeError):
249 self.shell.run_code("{object_name}.square()".format(object_name=object_name))
250 self.shell.run_code("{object_name}.b".format(object_name=object_name))
251 self.shell.run_code("{object_name}.a".format(object_name=object_name))
264 self.shell.run_code(f"{object_name}.square()")
265 self.shell.run_code(f"{object_name}.b")
266 self.shell.run_code(f"{object_name}.a")
252 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 353 def _check_smoketest(self, use_aimport=True):
256 354 """
@@ -258,7 +356,8 b' class TestAutoreload(Fixture):'
258 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 361 x = 9
263 362
264 363 z = 123 # this item will be deleted
@@ -281,7 +380,8 b' class Baz(object):'
281 380 class Bar: # old-style class: weakref doesn't work for it on Python < 2.7
282 381 def foo(self):
283 382 return 1
284 """)
383 """
384 )
285 385
286 386 #
287 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 400 self.shell.run_code("import %s" % mod_name)
301 401 stream = StringIO()
302 402 self.shell.magic_aimport("", stream=stream)
303 nt.assert_true("Modules to reload:\nall-except-skipped" in
304 stream.getvalue())
403 nt.assert_true(
404 "Modules to reload:\nall-except-skipped" in stream.getvalue()
405 )
305 406 nt.assert_in(mod_name, self.shell.ns)
306 407
307 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 437 # Simulate a failed reload: no reload should occur and exactly
337 438 # one error message should be printed
338 439 #
339 self.write_file(mod_fn, """
440 self.write_file(
441 mod_fn,
442 """
340 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 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 454 self.shell.run_code("pass") # trigger another reload
347 455 check_module_contents()
348 456
349 457 #
350 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 463 x = 10
354 464
355 465 def foo(y):
@@ -367,11 +477,12 b' class Baz(object):'
367 477 class Bar: # old-style class
368 478 def foo(self):
369 479 return 2
370 """)
480 """,
481 )
371 482
372 483 def check_module_contents():
373 484 nt.assert_equal(mod.x, 10)
374 nt.assert_false(hasattr(mod, 'z'))
485 nt.assert_false(hasattr(mod, "z"))
375 486
376 487 nt.assert_equal(old_foo(0), 4) # superreload magic!
377 488 nt.assert_equal(mod.foo(0), 4)
@@ -383,8 +494,8 b' class Bar: # old-style class'
383 494 nt.assert_equal(old_obj.quux, 43)
384 495 nt.assert_equal(obj.quux, 43)
385 496
386 nt.assert_false(hasattr(old_obj, 'zzz'))
387 nt.assert_false(hasattr(obj, 'zzz'))
497 nt.assert_false(hasattr(old_obj, "zzz"))
498 nt.assert_false(hasattr(obj, "zzz"))
388 499
389 500 obj2 = mod.Bar()
390 501 nt.assert_equal(old_obj2.foo(), 2)
@@ -408,17 +519,19 b' class Bar: # old-style class'
408 519 self.shell.magic_aimport("-" + mod_name)
409 520 stream = StringIO()
410 521 self.shell.magic_aimport("", stream=stream)
411 nt.assert_true(("Modules to skip:\n%s" % mod_name) in
412 stream.getvalue())
522 nt.assert_true(("Modules to skip:\n%s" % mod_name) in stream.getvalue())
413 523
414 524 # This should succeed, although no such module exists
415 525 self.shell.magic_aimport("-tmpmod_as318989e89ds")
416 526 else:
417 527 self.shell.magic_autoreload("0")
418 528
419 self.write_file(mod_fn, """
529 self.write_file(
530 mod_fn,
531 """
420 532 x = -99
421 """)
533 """,
534 )
422 535
423 536 self.shell.run_code("pass") # trigger reload
424 537 self.shell.run_code("pass")
@@ -440,8 +553,3 b' x = -99'
440 553
441 554 def test_smoketest_autoreload(self):
442 555 self._check_smoketest(use_aimport=False)
443
444
445
446
447
@@ -32,15 +32,42 b' import os'
32 32 import sys
33 33
34 34 from IPython.utils.version import check_version
35 from IPython.external.qt_loaders import (load_qt, loaded_api, QT_API_PYSIDE,
36 QT_API_PYSIDE2, QT_API_PYQT, QT_API_PYQT5,
37 QT_API_PYQTv1, QT_API_PYQT_DEFAULT)
35 from IPython.external.qt_loaders import (
36 load_qt,
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 69 def matplotlib_options(mpl):
70 """Constraints placed on an imported matplotlib."""
44 71 if mpl is None:
45 72 return
46 73 backend = mpl.rcParams.get('backend', None)
@@ -66,9 +93,7 b' def matplotlib_options(mpl):'
66 93 mpqt)
67 94
68 95 def get_options():
69 """Return a list of acceptable QT APIs, in decreasing order of
70 preference
71 """
96 """Return a list of acceptable QT APIs, in decreasing order of preference."""
72 97 #already imported Qt somewhere. Use that
73 98 loaded = loaded_api()
74 99 if loaded is not None:
@@ -83,13 +108,22 b' def get_options():'
83 108 qt_api = os.environ.get('QT_API', None)
84 109 if qt_api is None:
85 110 #no ETS variable. Ask mpl, then use default fallback path
86 return matplotlib_options(mpl) or [QT_API_PYQT_DEFAULT, QT_API_PYSIDE,
87 QT_API_PYQT5, QT_API_PYSIDE2]
111 return matplotlib_options(mpl) or [
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 120 elif qt_api not in _qt_apis:
89 121 raise RuntimeError("Invalid Qt API %r, valid values are: %r" %
90 122 (qt_api, ', '.join(_qt_apis)))
91 123 else:
92 124 return [qt_api]
93 125
126
94 127 api_opts = get_options()
95 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 11 import sys
12 12 import types
13 from functools import partial
14 from importlib import import_module
13 from functools import partial, lru_cache
14 import operator
15 15
16 16 from IPython.utils.version import check_version
17 17
18 # Available APIs.
19 QT_API_PYQT = 'pyqt' # Force version 2
18 # ### Available APIs.
19 # Qt6
20 QT_API_PYQT6 = "pyqt6"
21 QT_API_PYSIDE6 = "pyside6"
22
23 # Qt5
20 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 25 QT_API_PYSIDE2 = 'pyside2'
25 26
26 api_to_module = {QT_API_PYSIDE2: 'PySide2',
27 QT_API_PYSIDE: 'PySide',
28 QT_API_PYQT: 'PyQt4',
29 QT_API_PYQTv1: 'PyQt4',
30 QT_API_PYQT5: 'PyQt5',
31 QT_API_PYQT_DEFAULT: 'PyQt4',
27 # Qt4
28 QT_API_PYQT = "pyqt" # Force version 2
29 QT_API_PYQTv1 = "pyqtv1" # Force version 2
30 QT_API_PYSIDE = "pyside"
31
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 71 already imported an Incompatible QT Binding: %s
57 72 """ % (fullname, loaded_api()))
58 73
74
59 75 ID = ImportDenier()
60 76 sys.meta_path.insert(0, ID)
61 77
@@ -63,23 +79,11 b' sys.meta_path.insert(0, ID)'
63 79 def commit_api(api):
64 80 """Commit to a particular API, and trigger ImportErrors on subsequent
65 81 dangerous imports"""
82 modules = set(api_to_module.values())
66 83
67 if api == QT_API_PYSIDE2:
68 ID.forbid('PySide')
69 ID.forbid('PyQt4')
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')
84 modules.remove(api_to_module[api])
85 for mod in modules:
86 ID.forbid(mod)
83 87
84 88
85 89 def loaded_api():
@@ -90,19 +94,24 b' def loaded_api():'
90 94
91 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 108 if qtapi_version() == 2:
97 109 return QT_API_PYQT
98 110 else:
99 111 return QT_API_PYQTv1
100 elif 'PySide.QtCore' in sys.modules:
112 elif sys.modules.get("PySide.QtCore"):
101 113 return QT_API_PYSIDE
102 elif 'PySide2.QtCore' in sys.modules:
103 return QT_API_PYSIDE2
104 elif 'PyQt5.QtCore' in sys.modules:
105 return QT_API_PYQT5
114
106 115 return None
107 116
108 117
@@ -122,7 +131,7 b' def has_binding(api):'
122 131 from importlib.util import find_spec
123 132
124 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 135 # QT5 requires QtWidgets too
127 136 required.append('QtWidgets')
128 137
@@ -174,7 +183,7 b' def can_import(api):'
174 183
175 184 current = loaded_api()
176 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 187 else:
179 188 return current in [api, None]
180 189
@@ -238,6 +247,28 b' def import_pyqt5():'
238 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 272 def import_pyside():
242 273 """
243 274 Import PySide
@@ -264,6 +295,23 b' def import_pyside2():'
264 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 315 def load_qt(api_options):
268 316 """
269 317 Attempt to import Qt, given a preference list
@@ -291,12 +339,18 b' def load_qt(api_options):'
291 339 an incompatible library has already been installed)
292 340 """
293 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 347 QT_API_PYSIDE2: import_pyside2,
348 # Qt4
295 349 QT_API_PYSIDE: import_pyside,
296 350 QT_API_PYQT: import_pyqt4,
297 QT_API_PYQT5: import_pyqt5,
298 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 356 for api in api_options:
@@ -332,3 +386,16 b' def load_qt(api_options):'
332 386 has_binding(QT_API_PYSIDE),
333 387 has_binding(QT_API_PYSIDE2),
334 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 9 from IPython.core.display import DisplayObject, TextDisplayObject
10 10
11 from typing import Tuple, Iterable
12
11 13 __all__ = ['Audio', 'IFrame', 'YouTubeVideo', 'VimeoVideo', 'ScribdDocument',
12 14 'FileLink', 'FileLinks', 'Code']
13 15
@@ -159,7 +161,7 b' class Audio(DisplayObject):'
159 161 return val
160 162
161 163 @staticmethod
162 def _validate_and_normalize_with_numpy(data, normalize):
164 def _validate_and_normalize_with_numpy(data, normalize) -> Tuple[bytes, int]:
163 165 import numpy as np
164 166
165 167 data = np.array(data, dtype=float)
@@ -178,8 +180,7 b' class Audio(DisplayObject):'
178 180 max_abs_value = np.max(np.abs(data))
179 181 normalization_factor = Audio._get_normalization_factor(max_abs_value, normalize)
180 182 scaled = data / normalization_factor * 32767
181 return scaled.astype('<h').tostring(), nchan
182
183 return scaled.astype("<h").tobytes(), nchan
183 184
184 185 @staticmethod
185 186 def _validate_and_normalize_without_numpy(data, normalize):
@@ -262,13 +263,18 b' class IFrame(object):'
262 263 src="{src}{params}"
263 264 frameborder="0"
264 265 allowfullscreen
266 {extras}
265 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 274 self.src = src
270 275 self.width = width
271 276 self.height = height
277 self.extras = extras
272 278 self.params = kwargs
273 279
274 280 def _repr_html_(self):
@@ -278,10 +284,14 b' class IFrame(object):'
278 284 params = "?" + urlencode(self.params)
279 285 else:
280 286 params = ""
281 return self.iframe.format(src=self.src,
287 return self.iframe.format(
288 src=self.src,
282 289 width=self.width,
283 290 height=self.height,
284 params=params)
291 params=params,
292 extras=" ".join(self.extras),
293 )
294
285 295
286 296 class YouTubeVideo(IFrame):
287 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 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 323 self.id=id
314 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 328 super(YouTubeVideo, self).__init__(src, width, height, **kwargs)
316 329
317 330 def _repr_jpeg_(self):
@@ -6,7 +6,6 b' Contributions are *very* welcome.'
6 6 """
7 7
8 8 import os
9 import pipes
10 9 import shlex
11 10 import subprocess
12 11 import sys
@@ -47,9 +46,9 b' def install_editor(template, wait=False):'
47 46 def call_editor(self, filename, line=0):
48 47 if line is None:
49 48 line = 0
50 cmd = template.format(filename=pipes.quote(filename), line=line)
49 cmd = template.format(filename=shlex.quote(filename), line=line)
51 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 52 if sys.platform.startswith('win'):
54 53 cmd = shlex.split(cmd)
55 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 112 def latex_to_png_mpl(s, wrap, color='Black', scale=1.0):
113 113 try:
114 from matplotlib import mathtext
114 from matplotlib import figure, font_manager, mathtext
115 from matplotlib.backends import backend_agg
115 116 from pyparsing import ParseFatalException
116 117 except ImportError:
117 118 return None
@@ -122,11 +123,18 b" def latex_to_png_mpl(s, wrap, color='Black', scale=1.0):"
122 123 s = u'${0}$'.format(s)
123 124
124 125 try:
125 mt = mathtext.MathTextParser('bitmap')
126 f = BytesIO()
126 prop = font_manager.FontProperties(size=12)
127 127 dpi = 120*scale
128 mt.to_png(f, s, fontsize=12, dpi=dpi, color=color)
129 return f.getvalue()
128 buffer = BytesIO()
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 138 except (ValueError, RuntimeError, ParseFatalException):
131 139 return None
132 140
@@ -626,7 +626,7 b' def _default_pprint(obj, p, cycle):'
626 626 def _seq_pprinter_factory(start, end):
627 627 """
628 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 631 def inner(obj, p, cycle):
632 632 if cycle:
@@ -15,9 +15,6 b''
15 15 # Stdlib imports
16 16 import time
17 17
18 # Third-party imports
19 import nose.tools as nt
20
21 18 # Our own imports
22 19 from IPython.lib import backgroundjobs as bg
23 20
@@ -49,7 +46,7 b' def test_result():'
49 46 jobs = bg.BackgroundJobManager()
50 47 j = jobs.new(sleeper)
51 48 j.join()
52 nt.assert_equal(j.result['interval'], t_short)
49 assert j.result["interval"] == t_short
53 50
54 51
55 52 def test_flush():
@@ -57,10 +54,10 b' def test_flush():'
57 54 jobs = bg.BackgroundJobManager()
58 55 j = jobs.new(sleeper)
59 56 j.join()
60 nt.assert_equal(len(jobs.completed), 1)
61 nt.assert_equal(len(jobs.dead), 0)
57 assert len(jobs.completed) == 1
58 assert len(jobs.dead) == 0
62 59 jobs.flush()
63 nt.assert_equal(len(jobs.completed), 0)
60 assert len(jobs.completed) == 0
64 61
65 62
66 63 def test_dead():
@@ -68,10 +65,10 b' def test_dead():'
68 65 jobs = bg.BackgroundJobManager()
69 66 j = jobs.new(crasher)
70 67 j.join()
71 nt.assert_equal(len(jobs.completed), 0)
72 nt.assert_equal(len(jobs.dead), 1)
68 assert len(jobs.completed) == 0
69 assert len(jobs.dead) == 1
73 70 jobs.flush()
74 nt.assert_equal(len(jobs.dead), 0)
71 assert len(jobs.dead) == 0
75 72
76 73
77 74 def test_longer():
@@ -81,8 +78,8 b' def test_longer():'
81 78 # job as running, but not so long that it makes the test suite noticeably
82 79 # slower.
83 80 j = jobs.new(sleeper, 0.1)
84 nt.assert_equal(len(jobs.running), 1)
85 nt.assert_equal(len(jobs.completed), 0)
81 assert len(jobs.running) == 1
82 assert len(jobs.completed) == 0
86 83 j.join()
87 nt.assert_equal(len(jobs.running), 0)
88 nt.assert_equal(len(jobs.completed), 1)
84 assert len(jobs.running) == 0
85 assert len(jobs.completed) == 1
@@ -60,7 +60,8 b' def test_existing_path_FileLink():'
60 60 fl = display.FileLink(tf.name)
61 61 actual = fl._repr_html_()
62 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 66 def test_existing_path_FileLink_repr():
66 67 """FileLink: Calling repr() functions as expected on existing filepath
@@ -69,7 +70,8 b' def test_existing_path_FileLink_repr():'
69 70 fl = display.FileLink(tf.name)
70 71 actual = repr(fl)
71 72 expected = tf.name
72 nt.assert_equal(actual,expected)
73 assert actual == expected
74
73 75
74 76 def test_error_on_directory_to_FileLink():
75 77 """FileLink: Raises error when passed directory
@@ -111,7 +113,8 b' def test_existing_path_FileLinks():'
111 113 (tf1.name.replace("\\","/"),split(tf1.name)[1])]
112 114 expected.sort()
113 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 119 def test_existing_path_FileLinks_alt_formatter():
117 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 131 expected = ["hello","world"]
129 132 expected.sort()
130 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 137 def test_existing_path_FileLinks_repr():
134 138 """FileLinks: Calling repr() functions as expected on existing directory """
@@ -142,7 +146,8 b' def test_existing_path_FileLinks_repr():'
142 146 expected = ['%s/' % td, ' %s' % split(tf1.name)[1],' %s' % split(tf2.name)[1]]
143 147 expected.sort()
144 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 152 def test_existing_path_FileLinks_repr_alt_formatter():
148 153 """FileLinks: Calling repr() functions as expected w/ alt formatter
@@ -159,7 +164,8 b' def test_existing_path_FileLinks_repr_alt_formatter():'
159 164 expected = ["hello","world"]
160 165 expected.sort()
161 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 170 def test_error_on_file_to_FileLinks():
165 171 """FileLinks: Raises error when passed file
@@ -178,11 +184,11 b' def test_recursive_FileLinks():'
178 184 fl = display.FileLinks(td)
179 185 actual = str(fl)
180 186 actual = actual.split('\n')
181 nt.assert_equal(len(actual), 4, actual)
187 assert len(actual) == 4, actual
182 188 fl = display.FileLinks(td, recursive=False)
183 189 actual = str(fl)
184 190 actual = actual.split('\n')
185 nt.assert_equal(len(actual), 2, actual)
191 assert len(actual) == 2, actual
186 192
187 193 def test_audio_from_file():
188 194 path = pjoin(dirname(__file__), 'test.wav')
@@ -194,13 +200,13 b' class TestAudioDataWithNumpy(TestCase):'
194 200 def test_audio_from_numpy_array(self):
195 201 test_tone = get_test_tone()
196 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 205 @skipif_not_numpy
200 206 def test_audio_from_list(self):
201 207 test_tone = get_test_tone()
202 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 211 @skipif_not_numpy
206 212 def test_audio_from_numpy_array_without_rate_raises(self):
@@ -212,7 +218,7 b' class TestAudioDataWithNumpy(TestCase):'
212 218 for scale in [1, 0.5, 2]:
213 219 audio = display.Audio(get_test_tone(scale), rate=44100)
214 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 223 @skipif_not_numpy
218 224 def test_audio_data_without_normalization(self):
@@ -223,7 +229,7 b' class TestAudioDataWithNumpy(TestCase):'
223 229 expected_max_value = int(max_int16 * test_tone_max_abs)
224 230 audio = display.Audio(test_tone, rate=44100, normalize=False)
225 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 234 def test_audio_data_without_normalization_raises_for_invalid_data(self):
229 235 nt.assert_raises(
@@ -2,8 +2,6 b''
2 2 import sys
3 3 from unittest import mock
4 4
5 import nose.tools as nt
6
7 5 from IPython import get_ipython
8 6 from IPython.lib import editorhooks
9 7
@@ -20,15 +18,15 b' def test_install_editor():'
20 18 with mock.patch('subprocess.Popen', fake_popen):
21 19 get_ipython().hooks.editor('the file', 64)
22 20
23 nt.assert_equal(len(called), 1)
24 args = called[0]['args']
25 kwargs = called[0]['kwargs']
21 assert len(called) == 1
22 args = called[0]["args"]
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'):
30 expected = ['foo', '-l', '64', '-f', 'the file']
27 if sys.platform.startswith("win"):
28 expected = ["foo", "-l", "64", "-f", "the file"]
31 29 else:
32 30 expected = "foo -l 64 -f 'the file'"
33 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 25 raise FindCmdError
26 26
27 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 31 @contextmanager
@@ -81,7 +81,7 b' def test_indentation():'
81 81 gotoutput = pretty.pretty(MyList(range(count)))
82 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 87 def test_dispatch():
@@ -92,7 +92,7 b' def test_dispatch():'
92 92 gotoutput = pretty.pretty(MyDict())
93 93 expectedoutput = "MyDict(...)"
94 94
95 nt.assert_equal(gotoutput, expectedoutput)
95 assert gotoutput == expectedoutput
96 96
97 97
98 98 def test_callability_checking():
@@ -103,7 +103,7 b' def test_callability_checking():'
103 103 gotoutput = pretty.pretty(Dummy2())
104 104 expectedoutput = "Dummy1(...)"
105 105
106 nt.assert_equal(gotoutput, expectedoutput)
106 assert gotoutput == expectedoutput
107 107
108 108
109 109 @pytest.mark.parametrize(
@@ -135,7 +135,7 b' def test_sets(obj, expected_output):'
135 135 Test that set and frozenset use Python 3 formatting.
136 136 """
137 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 141 @skip_without('xxlimited')
@@ -145,14 +145,16 b' def test_pprint_heap_allocated_type():'
145 145 """
146 146 import xxlimited
147 147 output = pretty.pretty(xxlimited.Null)
148 nt.assert_equal(output, 'xxlimited.Null')
148 assert output == "xxlimited.Null"
149
149 150
150 151 def test_pprint_nomod():
151 152 """
152 153 Test that pprint works for classes with no __module__.
153 154 """
154 155 output = pretty.pretty(NoModule)
155 nt.assert_equal(output, 'NoModule')
156 assert output == "NoModule"
157
156 158
157 159 def test_pprint_break():
158 160 """
@@ -160,7 +162,7 b' def test_pprint_break():'
160 162 """
161 163 output = pretty.pretty(Breaking())
162 164 expected = "TG: Breaking(\n ):"
163 nt.assert_equal(output, expected)
165 assert output == expected
164 166
165 167 def test_pprint_break_repr():
166 168 """
@@ -168,11 +170,11 b' def test_pprint_break_repr():'
168 170 """
169 171 output = pretty.pretty([[BreakingRepr()]])
170 172 expected = "[[Breaking(\n )]]"
171 nt.assert_equal(output, expected)
173 assert output == expected
172 174
173 175 output = pretty.pretty([[BreakingRepr()]*2])
174 176 expected = "[[Breaking(\n ),\n Breaking(\n )]]"
175 nt.assert_equal(output, expected)
177 assert output == expected
176 178
177 179 def test_bad_repr():
178 180 """Don't catch bad repr errors"""
@@ -258,7 +260,7 b" ClassWithMeta = MetaClass('ClassWithMeta')"
258 260
259 261 def test_metaclass_repr():
260 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 266 def test_unicode_repr():
@@ -271,9 +273,9 b' def test_unicode_repr():'
271 273
272 274 c = C()
273 275 p = pretty.pretty(c)
274 nt.assert_equal(p, u)
276 assert p == u
275 277 p = pretty.pretty([c])
276 nt.assert_equal(p, u'[%s]' % u)
278 assert p == u"[%s]" % u
277 279
278 280
279 281 def test_basic_class():
@@ -290,10 +292,11 b' def test_basic_class():'
290 292 printer.flush()
291 293 output = stream.getvalue()
292 294
293 nt.assert_equal(output, '%s.MyObj' % __name__)
295 assert output == "%s.MyObj" % __name__
294 296 nt.assert_true(type_pprint_wrapper.called)
295 297
296 298
299 # TODO : pytest.mark.parametrise once nose is gone.
297 300 def test_collections_defaultdict():
298 301 # Create defaultdicts with cycles
299 302 a = defaultdict()
@@ -311,9 +314,10 b' def test_collections_defaultdict():'
311 314 (b, "defaultdict(list, {'key': defaultdict(...)})"),
312 315 ]
313 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 321 def test_collections_ordereddict():
318 322 # Create OrderedDict with cycle
319 323 a = OrderedDict()
@@ -335,9 +339,10 b' def test_collections_ordereddict():'
335 339 (a, "OrderedDict([('key', OrderedDict(...))])"),
336 340 ]
337 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 346 def test_collections_deque():
342 347 # Create deque with cycle
343 348 a = deque()
@@ -369,8 +374,10 b' def test_collections_deque():'
369 374 (a, 'deque([deque(...)])'),
370 375 ]
371 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 381 def test_collections_counter():
375 382 class MyCounter(Counter):
376 383 pass
@@ -380,8 +387,9 b' def test_collections_counter():'
380 387 (MyCounter(a=1), "MyCounter({'a': 1})"),
381 388 ]
382 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 393 def test_mappingproxy():
386 394 MP = types.MappingProxyType
387 395 underlying_dict = {}
@@ -424,9 +432,10 b' def test_mappingproxy():'
424 432 "{2: mappingproxy({2: {...}, 3: {...}}), 3: {...}}"),
425 433 ]
426 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 439 def test_simplenamespace():
431 440 SN = types.SimpleNamespace
432 441
@@ -444,7 +453,7 b' def test_simplenamespace():'
444 453 (sn_recursive, "namespace(first=namespace(...), second=namespace(...))"),
445 454 ]
446 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 459 def test_pretty_environ():
@@ -452,7 +461,7 b' def test_pretty_environ():'
452 461 # reindent to align with 'environ' prefix
453 462 dict_indented = dict_repr.replace('\n', '\n' + (' ' * len('environ')))
454 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 467 def test_function_pretty():
@@ -460,7 +469,8 b' def test_function_pretty():'
460 469 # posixpath is a pure python module, its interface is consistent
461 470 # across Python distributions
462 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 475 # custom function
466 476 def meaning_of_life(question=None):
@@ -489,4 +499,4 b' def test_custom_repr():'
489 499 oc = OrderedCounter("abracadabra")
490 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 1 # coding: utf-8
2 2 from IPython.lib import passwd
3 3 from IPython.lib.security import passwd_check, salt_len
4 import nose.tools as nt
5 4
6 5 def test_passwd_structure():
7 p = passwd('passphrase')
8 algorithm, salt, hashed = p.split(':')
9 nt.assert_equal(algorithm, 'sha1')
10 nt.assert_equal(len(salt), salt_len)
11 nt.assert_equal(len(hashed), 40)
6 p = passwd("passphrase")
7 algorithm, salt, hashed = p.split(":")
8 assert algorithm == "sha1"
9 assert len(salt) == salt_len
10 assert len(hashed) == 40
12 11
13 12 def test_roundtrip():
14 p = passwd('passphrase')
15 nt.assert_equal(passwd_check(p, 'passphrase'), True)
13 p = passwd("passphrase")
14 assert passwd_check(p, "passphrase") is True
15
16 16
17 17 def test_bad():
18 18 p = passwd('passphrase')
19 nt.assert_equal(passwd_check(p, p), False)
20 nt.assert_equal(passwd_check(p, 'a:b:c:d'), False)
21 nt.assert_equal(passwd_check(p, 'a:b'), False)
19 assert passwd_check(p, p) is False
20 assert passwd_check(p, "a:b:c:d") is False
21 assert passwd_check(p, "a:b") is False
22
22 23
23 24 def test_passwd_check_unicode():
24 25 # GH issue #4524
@@ -200,6 +200,7 b' from io import StringIO'
200 200 # Third-party
201 201 from docutils.parsers.rst import directives
202 202 from docutils.parsers.rst import Directive
203 from sphinx.util import logging
203 204
204 205 # Our own
205 206 from traitlets.config import Config
@@ -557,15 +558,20 b' class EmbeddedSphinxShell(object):'
557 558 filename = self.directive.state.document.current_source
558 559 lineno = self.directive.state.document.current_line
559 560
561 # Use sphinx logger for warnings
562 logger = logging.getLogger(__name__)
563
560 564 # output any exceptions raised during execution to stdout
561 565 # unless :okexcept: has been specified.
562 if not is_okexcept and (("Traceback" in processed_output) or ("SyntaxError" in processed_output)):
563 s = "\nException in %s at block ending on line %s\n" % (filename, lineno)
566 if not is_okexcept and (
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 571 s += "Specify :okexcept: as an option in the ipython:: block to suppress this message\n"
565 sys.stdout.write('\n\n>>>' + ('-' * 73))
566 sys.stdout.write(s)
567 sys.stdout.write(processed_output)
568 sys.stdout.write('<<<' + ('-' * 73) + '\n\n')
572 s += processed_output + "\n"
573 s += "<<<" + ("-" * 73)
574 logger.warning(s)
569 575 if self.warning_is_error:
570 576 raise RuntimeError('Non Expected exception in `{}` line {}'.format(filename, lineno))
571 577
@@ -573,15 +579,15 b' class EmbeddedSphinxShell(object):'
573 579 # unless :okwarning: has been specified.
574 580 if not is_okwarning:
575 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 584 s += "Specify :okwarning: as an option in the ipython:: block to suppress this message\n"
578 sys.stdout.write('\n\n>>>' + ('-' * 73))
579 sys.stdout.write(s)
580 sys.stdout.write(('-' * 76) + '\n')
581 s=warnings.formatwarning(w.message, w.category,
582 w.filename, w.lineno, w.line)
583 sys.stdout.write(s)
584 sys.stdout.write('<<<' + ('-' * 73) + '\n')
585 s += ("-" * 76) + "\n"
586 s += warnings.formatwarning(
587 w.message, w.category, w.filename, w.lineno, w.line
588 )
589 s += "<<<" + ("-" * 73)
590 logger.warning(s)
585 591 if self.warning_is_error:
586 592 raise RuntimeError('Non Expected warning in `{}` line {}'.format(filename, lineno))
587 593
@@ -1002,6 +1008,9 b' class IPythonDirective(Directive):'
1002 1008 lines = ['.. code-block:: ipython', '']
1003 1009 figures = []
1004 1010
1011 # Use sphinx logger for warnings
1012 logger = logging.getLogger(__name__)
1013
1005 1014 for part in parts:
1006 1015 block = block_parser(part, rgxin, rgxout, promptin, promptout)
1007 1016 if len(block):
@@ -1020,7 +1029,7 b' class IPythonDirective(Directive):'
1020 1029 if self.shell.warning_is_error:
1021 1030 raise RuntimeError(message)
1022 1031 else:
1023 warnings.warn(message)
1032 logger.warning(message)
1024 1033
1025 1034 for figure in figures:
1026 1035 lines.append('')
@@ -7,6 +7,7 b' from IPython.core.debugger import Pdb'
7 7 from IPython.core.completer import IPCompleter
8 8 from .ptutils import IPythonPTCompleter
9 9 from .shortcuts import create_ipython_shortcuts
10 from . import embed
10 11
11 12 from pygments.token import Token
12 13 from prompt_toolkit.shortcuts.prompt import PromptSession
@@ -131,6 +132,18 b' class TerminalPdb(Pdb):'
131 132 except Exception:
132 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 148 def set_trace(frame=None):
136 149 """
@@ -148,6 +161,6 b" if __name__ == '__main__':"
148 161 # happened after hitting "c", this is needed in order to
149 162 # be able to quit the debugging session (see #9950).
150 163 old_trace_dispatch = pdb.Pdb.trace_dispatch
151 pdb.Pdb = TerminalPdb
152 pdb.Pdb.trace_dispatch = old_trace_dispatch
164 pdb.Pdb = TerminalPdb # type: ignore
165 pdb.Pdb.trace_dispatch = old_trace_dispatch # type: ignore
153 166 pdb.main()
@@ -19,6 +19,8 b' from IPython.terminal.ipapp import load_default_config'
19 19 from traitlets import Bool, CBool, Unicode
20 20 from IPython.utils.io import ask_yes_no
21 21
22 from typing import Set
23
22 24 class KillEmbedded(Exception):pass
23 25
24 26 # kept for backward compatibility as IPython 6 was released with
@@ -123,17 +125,17 b' class InteractiveShellEmbed(TerminalInteractiveShell):'
123 125 help="Automatically set the terminal title"
124 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 134 @property
129 135 def embedded_active(self):
130 136 return (self._call_location_id not in InteractiveShellEmbed._inactive_locations)\
131 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 139 @embedded_active.setter
138 140 def embedded_active(self, value):
139 141 if value:
@@ -334,7 +336,7 b' class InteractiveShellEmbed(TerminalInteractiveShell):'
334 336 self.compile.flags = orig_compile_flags
335 337
336 338
337 def embed(**kwargs):
339 def embed(*, header="", compile_flags=None, **kwargs):
338 340 """Call this to embed IPython at the current point in your program.
339 341
340 342 The first invocation of this will create an :class:`InteractiveShellEmbed`
@@ -360,8 +362,6 b' def embed(**kwargs):'
360 362 config argument.
361 363 """
362 364 config = kwargs.get('config')
363 header = kwargs.pop('header', u'')
364 compile_flags = kwargs.pop('compile_flags', None)
365 365 if config is None:
366 366 config = load_default_config()
367 367 config.InteractiveShellEmbed = config.TerminalInteractiveShell
@@ -204,9 +204,8 b' class TerminalInteractiveShell(InteractiveShell):'
204 204
205 205 @observe('editing_mode')
206 206 def _editing_mode(self, change):
207 u_mode = change.new.upper()
208 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 210 @observe('autoformatter')
212 211 def _autoformatter_changed(self, change):
@@ -615,6 +614,13 b' class TerminalInteractiveShell(InteractiveShell):'
615 614
616 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 625 _inputhook = None
620 626 def inputhook(self, context):
@@ -7,13 +7,20 b' aliases = {'
7 7 }
8 8
9 9 backends = [
10 'qt', 'qt4', 'qt5',
11 'gtk', 'gtk2', 'gtk3',
12 'tk',
13 'wx',
14 'pyglet', 'glut',
15 'osx',
16 'asyncio'
10 "qt",
11 "qt4",
12 "qt5",
13 "qt6",
14 "gtk",
15 "gtk2",
16 "gtk3",
17 "gtk4",
18 "tk",
19 "wx",
20 "pyglet",
21 "glut",
22 "osx",
23 "asyncio",
17 24 ]
18 25
19 26 registered = {}
@@ -22,6 +29,7 b' def register(name, inputhook):'
22 29 """Register the function *inputhook* as an event loop integration."""
23 30 registered[name] = inputhook
24 31
32
25 33 class UnknownBackend(KeyError):
26 34 def __init__(self, name):
27 35 self.name = name
@@ -31,6 +39,7 b' class UnknownBackend(KeyError):'
31 39 "Supported event loops are: {}").format(self.name,
32 40 ', '.join(backends + sorted(registered)))
33 41
42
34 43 def get_inputhook_name_and_func(gui):
35 44 if gui in registered:
36 45 return gui, registered[gui]
@@ -42,9 +51,12 b' def get_inputhook_name_and_func(gui):'
42 51 return get_inputhook_name_and_func(aliases[gui])
43 52
44 53 gui_mod = gui
45 if gui == 'qt5':
46 os.environ['QT_API'] = 'pyqt5'
47 gui_mod = 'qt'
54 if gui == "qt5":
55 os.environ["QT_API"] = "pyqt5"
56 gui_mod = "qt"
57 elif gui == "qt6":
58 os.environ["QT_API"] = "pyqt6"
59 gui_mod = "qt"
48 60
49 61 mod = importlib.import_module('IPython.terminal.pt_inputhooks.'+gui_mod)
50 62 return gui, mod.inputhook
@@ -9,7 +9,7 b' import ctypes'
9 9 import ctypes.util
10 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 14 void_p = ctypes.c_void_p
15 15
@@ -37,7 +37,7 b' def C(classname):'
37 37 # end obj-c boilerplate from appnope
38 38
39 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 42 CFFileDescriptorCreate = CoreFoundation.CFFileDescriptorCreate
43 43 CFFileDescriptorCreate.restype = void_p
@@ -1,6 +1,6 b''
1 1 import sys
2 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 4 from IPython import get_ipython
5 5
6 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 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 17 def _reclaim_excepthook():
13 18 shell = get_ipython()
14 19 if shell is not None:
@@ -32,7 +37,16 b' def inputhook(context):'
32 37 'variable. Deactivate Qt5 code.'
33 38 )
34 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 50 _appref = app = QtGui.QApplication([" "])
37 51
38 52 # "reclaim" IPython sys.excepthook after event loop starts
@@ -55,8 +69,9 b' def inputhook(context):'
55 69 else:
56 70 # On POSIX platforms, we can use a file descriptor to quit the event
57 71 # loop when there is input ready to read.
58 notifier = QtCore.QSocketNotifier(context.fileno(),
59 QtCore.QSocketNotifier.Read)
72 notifier = QtCore.QSocketNotifier(
73 context.fileno(), enum_helper("QtCore.QSocketNotifier.Type").Read
74 )
60 75 try:
61 76 # connect the callback we care about before we turn it on
62 77 # lambda is necessary as PyQT inspect the function signature to know
@@ -65,6 +80,6 b' def inputhook(context):'
65 80 notifier.setEnabled(True)
66 81 # only start the event loop we are not already flipped
67 82 if not context.input_is_ready():
68 event_loop.exec_()
83 _exec(event_loop)
69 84 finally:
70 85 notifier.setEnabled(False)
@@ -52,10 +52,8 b' def process_handler(cmd, callback, stderr=subprocess.PIPE):'
52 52 A command to be executed by the system, using :class:`subprocess.Popen`.
53 53 If a string is passed, it will be run in the system shell. If a list is
54 54 passed, it will be used directly as arguments.
55
56 55 callback : callable
57 56 A one-argument function that will be called with the Popen object.
58
59 57 stderr : file descriptor number, optional
60 58 By default this is set to ``subprocess.PIPE``, but you can also pass the
61 59 value ``subprocess.STDOUT`` to force the subprocess' stderr to go into
1 NO CONTENT: modified file
1 NO CONTENT: modified file
1 NO CONTENT: modified file
1 NO CONTENT: modified file
1 NO CONTENT: modified file
@@ -28,12 +28,10 b' def extract_vars(*names,**kw):'
28 28 *names : str
29 29 One or more variable names which will be extracted from the caller's
30 30 frame.
31
32 depth : integer, optional
31 **kw : integer, optional
33 32 How many frames in the stack to walk when looking for your variables.
34 33 The default is 0, which will use the frame where the call was made.
35 34
36
37 35 Examples
38 36 --------
39 37 ::
@@ -22,7 +22,6 b' def complete_object(obj, prev_completions):'
22 22 The object to complete.
23 23 prev_completions : list
24 24 List of attributes discovered so far.
25
26 25 This should return the list of attributes in obj. If you only wish to
27 26 add to the attributes already discovered normally, return
28 27 own_attrs + prev_completions.
1 NO CONTENT: modified file
@@ -114,10 +114,8 b' class Tee(object):'
114 114 ----------
115 115 file_or_name : filename or open filehandle (writable)
116 116 File that will be duplicated
117
118 117 mode : optional, valid mode for open().
119 118 If a filename was give, open with this mode.
120
121 119 channel : str, one of ['stdout', 'stderr']
122 120 """
123 121 if channel not in ['stdout', 'stderr']:
@@ -196,7 +194,6 b" def temp_pyfile(src, ext='.py'):"
196 194 ----------
197 195 src : string or list of strings (no need for ending newlines if list)
198 196 Source code to be written to the file.
199
200 197 ext : optional, string
201 198 Extension for the generated file.
202 199
@@ -43,14 +43,13 b' class Struct(dict):'
43 43
44 44 Parameters
45 45 ----------
46 args : dict, Struct
46 *args : dict, Struct
47 47 Initialize with one dict or Struct
48 kw : dict
48 **kw : dict
49 49 Initialize with key, value pairs.
50 50
51 51 Examples
52 52 --------
53
54 53 >>> s = Struct(a=10,b=30)
55 54 >>> s.a
56 55 10
@@ -68,7 +67,6 b' class Struct(dict):'
68 67
69 68 Examples
70 69 --------
71
72 70 >>> s = Struct()
73 71 >>> s['a'] = 10
74 72 >>> s.allow_new_attr(False)
@@ -95,7 +93,6 b' class Struct(dict):'
95 93
96 94 Examples
97 95 --------
98
99 96 >>> s = Struct()
100 97 >>> s.a = 10
101 98 >>> s.a
@@ -130,7 +127,6 b' class Struct(dict):'
130 127
131 128 Examples
132 129 --------
133
134 130 >>> s = Struct(a=10)
135 131 >>> s.a
136 132 10
@@ -155,7 +151,6 b' class Struct(dict):'
155 151
156 152 Examples
157 153 --------
158
159 154 >>> s = Struct(a=10,b=30)
160 155 >>> s2 = Struct(a=20,c=40)
161 156 >>> s += s2
@@ -170,7 +165,6 b' class Struct(dict):'
170 165
171 166 Examples
172 167 --------
173
174 168 >>> s1 = Struct(a=10,b=30)
175 169 >>> s2 = Struct(a=20,c=40)
176 170 >>> s = s1 + s2
@@ -186,7 +180,6 b' class Struct(dict):'
186 180
187 181 Examples
188 182 --------
189
190 183 >>> s1 = Struct(a=10,b=30)
191 184 >>> s2 = Struct(a=40)
192 185 >>> s = s1 - s2
@@ -202,7 +195,6 b' class Struct(dict):'
202 195
203 196 Examples
204 197 --------
205
206 198 >>> s1 = Struct(a=10,b=30)
207 199 >>> s2 = Struct(a=40)
208 200 >>> s1 -= s2
@@ -236,7 +228,6 b' class Struct(dict):'
236 228
237 229 Examples
238 230 --------
239
240 231 >>> s = Struct(a=10,b=30)
241 232 >>> s2 = s.copy()
242 233 >>> type(s2) is Struct
@@ -251,7 +242,6 b' class Struct(dict):'
251 242
252 243 Examples
253 244 --------
254
255 245 >>> s = Struct(a=10)
256 246 >>> s.hasattr('a')
257 247 True
@@ -284,7 +274,7 b' class Struct(dict):'
284 274
285 275 Parameters
286 276 ----------
287 __loc_data : dict, Struct
277 __loc_data__ : dict, Struct
288 278 The data to merge into self
289 279 __conflict_solve : dict
290 280 The conflict policy dict. The keys are binary functions used to
@@ -292,12 +282,11 b' class Struct(dict):'
292 282 the keys the conflict resolution function applies to. Instead of
293 283 a list of strings a space separated string can be used, like
294 284 'a b c'.
295 kw : dict
285 **kw : dict
296 286 Additional key, value pairs to merge in
297 287
298 288 Notes
299 289 -----
300
301 290 The `__conflict_solve` dict is a dictionary of binary functions which will be used to
302 291 solve key conflicts. Here is an example::
303 292
@@ -338,7 +327,6 b' class Struct(dict):'
338 327
339 328 Examples
340 329 --------
341
342 330 This show the default policy:
343 331
344 332 >>> s = Struct(a=10,b=30)
1 NO CONTENT: modified file
1 NO CONTENT: modified file
@@ -109,7 +109,7 b' def get_py_filename(name, force_win32=None):'
109 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 113 """Find a file by looking through a sequence of paths.
114 114
115 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 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 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 184 Parameters
180 185 ----------
181
182 186 require_writable : bool [default: False]
183 187 if True:
184 188 guarantees the return value is a writable directory, otherwise
1 NO CONTENT: modified file
1 NO CONTENT: modified file
@@ -15,7 +15,6 b''
15 15
16 16 import sys
17 17
18 import nose.tools as nt
19 18 import pytest
20 19
21 20 from IPython.testing.decorators import skip_iptest_but_not_pytest
@@ -75,18 +74,18 b' def test_rich_output_empty(method_mime):'
75 74 """RichOutput with no args"""
76 75 rich = capture.RichOutput()
77 76 method, mime = method_mime
78 nt.assert_equal(getattr(rich, method)(), None)
77 assert getattr(rich, method)() is None
79 78
80 79 def test_rich_output():
81 80 """test RichOutput basics"""
82 81 data = basic_data
83 82 metadata = basic_metadata
84 83 rich = capture.RichOutput(data=data, metadata=metadata)
85 nt.assert_equal(rich._repr_html_(), data["text/html"])
86 nt.assert_equal(rich._repr_png_(), (data["image/png"], metadata["image/png"]))
87 nt.assert_equal(rich._repr_latex_(), None)
88 nt.assert_equal(rich._repr_javascript_(), None)
89 nt.assert_equal(rich._repr_svg_(), None)
84 assert rich._repr_html_() == data["text/html"]
85 assert rich._repr_png_() == (data["image/png"], metadata["image/png"])
86 assert rich._repr_latex_() is None
87 assert rich._repr_javascript_() is None
88 assert rich._repr_svg_() is None
90 89
91 90
92 91 @skip_iptest_but_not_pytest
@@ -96,7 +95,7 b' def test_rich_output_no_metadata(method_mime):'
96 95 data = full_data
97 96 rich = capture.RichOutput(data=data)
98 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 101 @skip_iptest_but_not_pytest
@@ -107,7 +106,7 b' def test_rich_output_metadata(method_mime):'
107 106 metadata = full_metadata
108 107 rich = capture.RichOutput(data=data, metadata=metadata)
109 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 111 def test_rich_output_display():
113 112 """test RichOutput.display
@@ -119,10 +118,10 b' def test_rich_output_display():'
119 118 rich = capture.RichOutput(data=data)
120 119 with capture.capture_output() as cap:
121 120 rich.display()
122 nt.assert_equal(len(cap.outputs), 1)
121 assert len(cap.outputs) == 1
123 122 rich2 = cap.outputs[0]
124 nt.assert_equal(rich2.data, rich.data)
125 nt.assert_equal(rich2.metadata, rich.metadata)
123 assert rich2.data == rich.data
124 assert rich2.metadata == rich.metadata
126 125
127 126 def test_capture_output():
128 127 """capture_output works"""
@@ -131,8 +130,8 b' def test_capture_output():'
131 130 print(hello_stdout, end="")
132 131 print(hello_stderr, end="", file=sys.stderr)
133 132 rich.display()
134 nt.assert_equal(hello_stdout, cap.stdout)
135 nt.assert_equal(hello_stderr, cap.stderr)
133 assert hello_stdout == cap.stdout
134 assert hello_stderr == cap.stderr
136 135
137 136
138 137 def test_capture_output_no_stdout():
@@ -142,9 +141,9 b' def test_capture_output_no_stdout():'
142 141 print(hello_stdout, end="")
143 142 print(hello_stderr, end="", file=sys.stderr)
144 143 rich.display()
145 nt.assert_equal("", cap.stdout)
146 nt.assert_equal(hello_stderr, cap.stderr)
147 nt.assert_equal(len(cap.outputs), 1)
144 assert "" == cap.stdout
145 assert hello_stderr == cap.stderr
146 assert len(cap.outputs) == 1
148 147
149 148
150 149 def test_capture_output_no_stderr():
@@ -155,9 +154,9 b' def test_capture_output_no_stderr():'
155 154 print(hello_stdout, end="")
156 155 print(hello_stderr, end="", file=sys.stderr)
157 156 rich.display()
158 nt.assert_equal(hello_stdout, cap.stdout)
159 nt.assert_equal("", cap.stderr)
160 nt.assert_equal(len(cap.outputs), 1)
157 assert hello_stdout == cap.stdout
158 assert "" == cap.stderr
159 assert len(cap.outputs) == 1
161 160
162 161
163 162 def test_capture_output_no_display():
@@ -167,6 +166,6 b' def test_capture_output_no_display():'
167 166 print(hello_stdout, end="")
168 167 print(hello_stderr, end="", file=sys.stderr)
169 168 rich.display()
170 nt.assert_equal(hello_stdout, cap.stdout)
171 nt.assert_equal(hello_stderr, cap.stderr)
172 nt.assert_equal(cap.outputs, [])
169 assert hello_stdout == cap.stdout
170 assert hello_stderr == cap.stderr
171 assert cap.outputs == []
1 NO CONTENT: modified file
@@ -113,7 +113,7 b' def test_get_home_dir_1():'
113 113 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py"))
114 114
115 115 home_dir = path.get_home_dir()
116 nt.assert_equal(home_dir, unfrozen)
116 assert home_dir == unfrozen
117 117
118 118
119 119 @skip_if_not_win32
@@ -127,7 +127,7 b' def test_get_home_dir_2():'
127 127 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower()
128 128
129 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 133 @skip_win32_py38
@@ -137,7 +137,7 b' def test_get_home_dir_3():'
137 137 env["HOME"] = HOME_TEST_DIR
138 138 home_dir = path.get_home_dir(True)
139 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 143 @with_environment
@@ -181,7 +181,7 b' def test_get_home_dir_8():'
181 181 with patch.object(wreg, 'OpenKey', return_value=key()), \
182 182 patch.object(wreg, 'QueryValueEx', return_value=[abspath(HOME_TEST_DIR)]):
183 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 186 @with_environment
187 187 def test_get_xdg_dir_0():
@@ -195,7 +195,7 b' def test_get_xdg_dir_0():'
195 195 env.pop('IPYTHONDIR', None)
196 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 201 @with_environment
@@ -208,7 +208,7 b' def test_get_xdg_dir_1():'
208 208 env.pop('IPYTHON_DIR', None)
209 209 env.pop('IPYTHONDIR', None)
210 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 213 @with_environment
214 214 def test_get_xdg_dir_2():
@@ -224,7 +224,7 b' def test_get_xdg_dir_2():'
224 224 if not os.path.exists(cfgdir):
225 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 229 @with_environment
230 230 def test_get_xdg_dir_3():
@@ -240,7 +240,7 b' def test_get_xdg_dir_3():'
240 240 if not os.path.exists(cfgdir):
241 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 245 def test_filefind():
246 246 """Various tests for filefind"""
@@ -263,13 +263,13 b' def test_get_long_path_name_win32():'
263 263 # Test to see if the short path evaluates correctly.
264 264 short_path = os.path.join(tmpdir, 'THISIS~1')
265 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 269 @dec.skip_win32
270 270 def test_get_long_path_name():
271 p = path.get_long_path_name('/usr/local')
272 nt.assert_equal(p,'/usr/local')
271 p = path.get_long_path_name("/usr/local")
272 assert p == "/usr/local"
273 273
274 274
275 275 class TestRaiseDeprecation(unittest.TestCase):
@@ -300,18 +300,18 b' class TestRaiseDeprecation(unittest.TestCase):'
300 300 @with_environment
301 301 def test_get_py_filename():
302 302 os.chdir(TMP_TEST_DIR)
303 with make_tempfile('foo.py'):
304 nt.assert_equal(path.get_py_filename('foo.py'), 'foo.py')
305 nt.assert_equal(path.get_py_filename('foo'), 'foo.py')
306 with make_tempfile('foo'):
307 nt.assert_equal(path.get_py_filename('foo'), 'foo')
308 nt.assert_raises(IOError, path.get_py_filename, 'foo.py')
309 nt.assert_raises(IOError, path.get_py_filename, 'foo')
310 nt.assert_raises(IOError, path.get_py_filename, 'foo.py')
311 true_fn = 'foo with spaces.py'
303 with make_tempfile("foo.py"):
304 assert path.get_py_filename("foo.py") == "foo.py"
305 assert path.get_py_filename("foo") == "foo.py"
306 with make_tempfile("foo"):
307 assert path.get_py_filename("foo") == "foo"
308 nt.assert_raises(IOError, path.get_py_filename, "foo.py")
309 nt.assert_raises(IOError, path.get_py_filename, "foo")
310 nt.assert_raises(IOError, path.get_py_filename, "foo.py")
311 true_fn = "foo with spaces.py"
312 312 with make_tempfile(true_fn):
313 nt.assert_equal(path.get_py_filename('foo with spaces'), true_fn)
314 nt.assert_equal(path.get_py_filename('foo with spaces.py'), true_fn)
313 assert path.get_py_filename("foo with spaces") == true_fn
314 assert path.get_py_filename("foo with spaces.py") == true_fn
315 315 nt.assert_raises(IOError, path.get_py_filename, '"foo with spaces.py"')
316 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 361 def check_match(self, patterns, matches):
362 362 with self.in_tempdir():
363 363 # glob returns unordered list. that's why sorted is required.
364 nt.assert_equal(sorted(path.shellglob(patterns)),
365 sorted(matches))
364 assert sorted(path.shellglob(patterns)) == sorted(matches)
366 365
367 366 def common_cases(self):
368 367 return [
@@ -397,12 +396,13 b' class TestShellGlob(unittest.TestCase):'
397 396 yield (self.check_match, patterns, matches)
398 397
399 398
399 # TODO : pytest.mark.parametrise once nose is gone.
400 400 def test_unescape_glob():
401 nt.assert_equal(path.unescape_glob(r'\*\[\!\]\?'), '*[!]?')
402 nt.assert_equal(path.unescape_glob(r'\\*'), r'\*')
403 nt.assert_equal(path.unescape_glob(r'\\\*'), r'\*')
404 nt.assert_equal(path.unescape_glob(r'\\a'), r'\a')
405 nt.assert_equal(path.unescape_glob(r'\a'), r'\a')
401 assert path.unescape_glob(r"\*\[\!\]\?") == "*[!]?"
402 assert path.unescape_glob(r"\\*") == r"\*"
403 assert path.unescape_glob(r"\\\*") == r"\*"
404 assert path.unescape_glob(r"\\a") == r"\a"
405 assert path.unescape_glob(r"\a") == r"\a"
406 406
407 407
408 408 @onlyif_unicode_paths
@@ -431,17 +431,19 b' class TestLinkOrCopy(unittest.TestCase):'
431 431 return os.path.join(self.tempdir.name, *args)
432 432
433 433 def assert_inode_not_equal(self, a, b):
434 nt.assert_not_equal(os.stat(a).st_ino, os.stat(b).st_ino,
435 "%r and %r do reference the same indoes" %(a, b))
434 assert (
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 438 def assert_inode_equal(self, a, b):
438 nt.assert_equal(os.stat(a).st_ino, os.stat(b).st_ino,
439 "%r and %r do not reference the same indoes" %(a, b))
439 assert (
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 443 def assert_content_equal(self, a, b):
442 444 with open(a) as a_f:
443 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 448 @skip_win32
447 449 def test_link_successful(self):
@@ -489,4 +491,4 b' class TestLinkOrCopy(unittest.TestCase):'
489 491 path.link_or_copy(self.src, dst)
490 492 path.link_or_copy(self.src, dst)
491 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 67 nt.assert_raises(FindCmdError,find_cmd,'asdfasdf')
68 68
69 69
70 # TODO: move to pytest.mark.parametrize once nose gone
70 71 @dec.skip_win32
71 72 def test_arg_split():
72 73 """Ensure that argument lines are correctly split like in a shell."""
@@ -80,8 +81,10 b' def test_arg_split():'
80 81 ['something "with quotes"', ['something', '"with quotes"']],
81 82 ]
82 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 88 @dec.skip_if_not_win32
86 89 def test_arg_split_win32():
87 90 """Ensure that argument lines are correctly split like in a shell."""
@@ -92,7 +95,7 b' def test_arg_split_win32():'
92 95 ['something "with quotes"', ['something', 'with quotes']],
93 96 ]
94 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 101 class SubProcessTestCase(tt.TempFileMixin):
1 NO CONTENT: modified file
@@ -32,31 +32,31 b' def test_columnize():'
32 32 items = [l*size for l in 'abcd']
33 33
34 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 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 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 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 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 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 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 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 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 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 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 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 62 def test_columnize_random():
@@ -77,38 +77,43 b' def test_columnize_random():'
77 77 print("size of each element :\n %s" % rand_len)
78 78 assert False, "row_first={0}".format(row_first)
79 79
80
81 # TODO: pytest mark.parametrize once nose removed.
80 82 def test_columnize_medium():
81 83 """Test with inputs than shouldn't be wider than 80"""
82 84 size = 40
83 85 items = [l*size for l in 'abc']
84 86 for row_first in [True, False]:
85 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 92 def test_columnize_long():
89 93 """Test columnize with inputs longer than the display window"""
90 94 size = 11
91 95 items = [l*size for l in 'abc']
92 96 for row_first in [True, False]:
93 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 101 def eval_formatter_check(f):
97 102 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os, u=u"café", b="café")
98 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 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 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 109 s = f.format("{stuff!s}", **ns)
105 nt.assert_equal(s, ns['stuff'])
110 assert s == ns["stuff"]
106 111 s = f.format("{stuff!r}", **ns)
107 nt.assert_equal(s, repr(ns['stuff']))
112 assert s == repr(ns["stuff"])
108 113
109 114 # Check with unicode:
110 115 s = f.format("{u}", **ns)
111 nt.assert_equal(s, ns['u'])
116 assert s == ns["u"]
112 117 # This decodes in a platform dependent manner, but it shouldn't error out
113 118 s = f.format("{b}", **ns)
114 119
@@ -117,11 +122,11 b' def eval_formatter_check(f):'
117 122 def eval_formatter_slicing_check(f):
118 123 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
119 124 s = f.format(" {stuff.split()[:]} ", **ns)
120 nt.assert_equal(s, " ['hello', 'there'] ")
125 assert s == " ['hello', 'there'] "
121 126 s = f.format(" {stuff.split()[::-1]} ", **ns)
122 nt.assert_equal(s, " ['there', 'hello'] ")
127 assert s == " ['there', 'hello'] "
123 128 s = f.format("{stuff[::2]}", **ns)
124 nt.assert_equal(s, ns['stuff'][::2])
129 assert s == ns["stuff"][::2]
125 130
126 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 134 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
130 135
131 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)
135 nt.assert_equal(s, 'ell')
139 s = f.format("{stuff[slice(1,4)]}", **ns)
140 assert s == "ell"
136 141
137 142 s = f.format("{a[:]}", a=[1, 2])
138 nt.assert_equal(s, "[1, 2]")
143 assert s == "[1, 2]"
139 144
140 145 def test_eval_formatter():
141 146 f = text.EvalFormatter()
@@ -154,29 +159,16 b' def test_dollar_formatter():'
154 159
155 160 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
156 161 s = f.format("$n", **ns)
157 nt.assert_equal(s, "12")
162 assert s == "12"
158 163 s = f.format("$n.real", **ns)
159 nt.assert_equal(s, "12")
164 assert s == "12"
160 165 s = f.format("$n/{stuff[:5]}", **ns)
161 nt.assert_equal(s, "12/hello")
166 assert s == "12/hello"
162 167 s = f.format("$n $$HOME", **ns)
163 nt.assert_equal(s, "12 $HOME")
168 assert s == "12 $HOME"
164 169 s = f.format("${foo}", foo="HOME")
165 nt.assert_equal(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')
170 assert s == "$HOME"
176 171
177 def test_long_substr_empty():
178 data = []
179 nt.assert_equal(text.long_substr(data), '')
180 172
181 173 def test_strip_email():
182 174 src = """\
@@ -189,25 +181,25 b' def test_strip_email():'
189 181 ... return x+1
190 182 ...
191 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 187 def test_strip_email2():
196 188 src = '> > > list()'
197 189 cln = 'list()'
198 nt.assert_equal(text.strip_email_quotes(src), cln)
190 assert text.strip_email_quotes(src) == cln
199 191
200 192 def test_LSString():
201 193 lss = text.LSString("abc\ndef")
202 nt.assert_equal(lss.l, ['abc', 'def'])
203 nt.assert_equal(lss.s, 'abc def')
194 assert lss.l == ["abc", "def"]
195 assert lss.s == "abc def"
204 196 lss = text.LSString(os.getcwd())
205 197 nt.assert_is_instance(lss.p[0], Path)
206 198
207 199 def test_SList():
208 sl = text.SList(['a 11', 'b 1', 'a 2'])
209 nt.assert_equal(sl.n, 'a 11\nb 1\na 2')
210 nt.assert_equal(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']))
212 nt.assert_equal(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']))
200 sl = text.SList(["a 11", "b 1", "a 2"])
201 assert sl.n == "a 11\nb 1\na 2"
202 assert sl.s == "a 11 b 1 a 2"
203 assert sl.grep(lambda x: x.startswith("a")) == text.SList(["a 11", "a 2"])
204 assert sl.fields(0) == text.SList(["a", "b", "a"])
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 11 import os
12 12 import re
13 import string
13 14 import sys
14 15 import textwrap
15 16 from string import Formatter
@@ -252,7 +253,6 b' def indent(instr,nspaces=4, ntabs=0, flatten=False):'
252 253
253 254 Parameters
254 255 ----------
255
256 256 instr : basestring
257 257 The string to be indented.
258 258 nspaces : int (default: 4)
@@ -266,7 +266,6 b' def indent(instr,nspaces=4, ntabs=0, flatten=False):'
266 266
267 267 Returns
268 268 -------
269
270 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 390 Returns
392 391 -------
393
394 392 list of complete paragraphs, wrapped to fill `ncols` columns.
395 393 """
396 394 paragraph_re = re.compile(r'\n(\s*\n)+', re.MULTILINE)
@@ -408,22 +406,6 b' def wrap_paragraphs(text, ncols=80):'
408 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 409 def strip_email_quotes(text):
428 410 """Strip leading email quotation characters ('>').
429 411
@@ -457,20 +439,23 b' def strip_email_quotes(text):'
457 439 Out[5]: '> > text\\n> > more\\nlast different'
458 440 """
459 441 lines = text.splitlines()
460 matches = set()
461 for line in lines:
462 prefix = re.match(r'^(\s*>[ >]*)', line)
463 if prefix:
464 matches.add(prefix.group(1))
465 else:
442 strip_len = 0
443
444 for characters in zip(*lines):
445 # Check if all characters in this position are the same
446 if len(set(characters)) > 1:
466 447 break
448 prefix_char = characters[0]
449
450 if prefix_char in string.whitespace or prefix_char == ">":
451 strip_len += 1
467 452 else:
468 prefix = long_substr(list(matches))
469 if prefix:
470 strip = len(prefix)
471 text = '\n'.join([ ln[strip:] for ln in lines])
453 break
454
455 text = "\n".join([ln[strip_len:] for ln in lines])
472 456 return text
473 457
458
474 459 def strip_ansi(source):
475 460 """
476 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 637 Parameters
653 638 ----------
654
655 639 items
656 640 list of strings to columize
657 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 651 Returns
668 652 -------
669
670 653 strings_matrix
671
672 654 nested list of string, the outer most list contains as many list as
673 655 rows, the innermost lists have each as many element as columns. If the
674 656 total number of elements in `items` does not equal the product of
675 657 rows*columns, the last element of some lists are filled with `None`.
676
677 658 dict_info
678 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 695 items : sequence of strings
715 696 The strings to process.
716
717 697 row_first : (default False)
718 698 Whether to compute columns for a row-first matrix instead of
719 699 column-first (default).
720
721 700 separator : str, optional [default is two spaces]
722 701 The string that separates columns.
723
724 702 displaywidth : int, optional [default is 80]
725 703 Width of the display in number of characters.
726 704
@@ -23,6 +23,11 b' import time'
23 23 # If possible (Unix), use the resource module instead of time.clock()
24 24 try:
25 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 31 def clocku():
27 32 """clocku() -> floating point number
28 33
@@ -56,7 +61,9 b' try:'
56 61
57 62 Similar to clock(), but return a tuple of user/system times."""
58 63 return resource.getrusage(resource.RUSAGE_SELF)[:2]
59 except ImportError:
64
65
66 else:
60 67 # There is no distinction of user/system time under windows, so we just use
61 68 # time.perff_counter() for everything...
62 69 clocku = clocks = clock = time.perf_counter
@@ -28,7 +28,6 b' def line_at_cursor(cell, cursor_pos=0):'
28 28
29 29 Parameters
30 30 ----------
31
32 31 cell: str
33 32 multiline block of text
34 33 cursor_pos: integer
@@ -36,7 +35,6 b' def line_at_cursor(cell, cursor_pos=0):'
36 35
37 36 Returns
38 37 -------
39
40 38 (line, offset): (string, integer)
41 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 65 Parameters
68 66 ----------
69
70 67 cell : unicode
71 68 A block of Python code
72 69 cursor_pos : int
@@ -4,8 +4,8 b''
4 4 .. image:: https://img.shields.io/pypi/v/IPython.svg
5 5 :target: https://pypi.python.org/pypi/ipython
6 6
7 .. image:: https://img.shields.io/travis/ipython/ipython.svg
8 :target: https://travis-ci.org/ipython/ipython
7 .. image:: https://github.com/ipython/ipython/actions/workflows/test.yml/badge.svg
8 :target: https://github.com/ipython/ipython/actions/workflows/test.yml)
9 9
10 10 .. image:: https://www.codetriage.com/ipython/ipython/badges/users.svg
11 11 :target: https://www.codetriage.com/ipython/ipython/
@@ -1,4 +1,4 b''
1 -e .
1 -e .[test]
2 2 ipykernel
3 3 setuptools>=18.5
4 4 sphinx
@@ -6,3 +6,4 b' sphinx-rtd-theme'
6 6 docrepr
7 7 matplotlib
8 8 stack_data
9 pytest
@@ -18,7 +18,7 b''
18 18 import sys, os
19 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 22 ON_RTD = os.environ.get('READTHEDOCS', None) == 'True'
23 23
24 24 if ON_RTD:
@@ -151,7 +151,7 b' Complete Example'
151 151
152 152 Here is a full example of a magic package. You can distribute magics using
153 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 156 When distributing magics as part of a package, recommended best practice is to
157 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 7 supports a number of common GUI toolkits, but from IPython 3.0, it is possible
8 8 to integrate other event loops without modifying IPython itself.
9 9
10 Supported event loops include ``qt4``, ``qt5``, ``gtk2``, ``gtk3``, ``wx``,
11 ``osx`` and ``tk``. Make sure the event loop you specify matches the GUI
12 toolkit used by your own code.
10 Supported event loops include ``qt4``, ``qt5``, ``gtk2``, ``gtk3``, ``gtk4``,
11 ``wx``, ``osx`` and ``tk``. Make sure the event loop you specify matches the
12 GUI toolkit used by your own code.
13 13
14 14 To make IPython GUI event loop integration occur automatically at every
15 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 24 Rich display
25 25 ============
26 26
27 The notebook and the Qt console can display richer representations of objects.
28 To use this, you can define any of a number of ``_repr_*_()`` methods. Note that
29 these are surrounded by single, not double underscores.
30
31 Both the notebook and the Qt console can display ``svg``, ``png`` and ``jpeg``
32 representations. The notebook can also display ``html``, ``javascript``,
33 ``markdown`` and ``latex``. If the methods don't exist, or return ``None``, it
34 falls back to a standard ``repr()``.
27 Custom methods
28 ----------------------
29 IPython can display richer representations of objects.
30 To do this, you can define ``_ipython_display_()``, or any of a number of
31 ``_repr_*_()`` methods.
32 Note that these are surrounded by single, not double underscores.
33
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 81 For example::
37 82
@@ -42,43 +87,61 b' For example::'
42 87 def _repr_html_(self):
43 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 117 .. class:: MyObject
55 118
56 119 .. method:: _repr_mimebundle_(include=None, exclude=None)
57 120
58 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
60 ``_repr_*_`` methods are ignored. The method should take keyword arguments
61 ``include`` and ``exclude``, though it is not required to respect them.
122 of two dictionaries: *data, metadata* (see :ref:`Metadata`).
123 If this returns something, other ``_repr_*_`` methods are ignored.
124 The method should take keyword arguments ``include`` and ``exclude``, though
125 it is not required to respect them.
62 126
63 127 .. method:: _ipython_display_()
64 128
65 129 Displays the object as a side effect; the return value is ignored. If this
66 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 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 166 exception. To do this, define the custom traceback using
104 167 `_render_traceback_(self)` method which returns a list of strings, one string
105 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 170 IPython, does this to display errors from multiple engines.
108 171
109 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 119 you can load this profile by adding ``--profile=<name>`` to your command line
120 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 122 IPython extends the config loader for Python files so that you can inherit
128 123 config from another profile. To do this, use a line like this in your Python
129 124 config file:
@@ -5,4 +5,4 b' Connection Diagrams of The IPython ZMQ Cluster'
5 5 ==============================================
6 6
7 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 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 7 This is useful for languages that have Python bindings, such as `Octave
8 8 <http://www.gnu.org/software/octave/>`_ (via
9 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 11 such as bash.
12 12
13 13 .. seealso::
@@ -57,7 +57,7 b' features:'
57 57
58 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 62 * syntax highlighting as you type.
63 63
@@ -69,7 +69,7 b' it allows:'
69 69 * the object to create a rich display of Html, Images, Latex, Sound and
70 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 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 103 .. seealso::
104 104
105 `Jupyter documentation <http://jupyter.readthedocs.io/en/latest/>`__
105 `Jupyter documentation <https://jupyter.readthedocs.io/en/latest/>`__
106 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 108 Formerly ``IPython.parallel``.
109 109
110 110
@@ -51,7 +51,7 b' for more help see'
51 51
52 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 55 The Notebook, nbconvert, and many other former pieces of IPython are now
56 56 part of Project Jupyter.
57 57
@@ -29,4 +29,4 b' done some work in the classic Python REPL.'
29 29 .. seealso::
30 30
31 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 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 9 ``SyntaxError`` if run in a pure Python shell, or if executing in a Python
10 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 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 21 .. code-block:: ipython
22 22
@@ -46,9 +46,9 b' All the following construct are valid IPython syntax:'
46 46 .. code-block:: ipython
47 47
48 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 50 ...: raw = !echo $file
51 ...: !echo {files[0].upper()} $raw
51 ...: !echo {file[0].upper()} $raw
52 52
53 53
54 54 .. code-block:: ipython
@@ -58,8 +58,8 b' All the following construct are valid IPython syntax:'
58 58 ...: print $months[0];
59 59
60 60
61 Each of these construct 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
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's see each of these examples
63 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 93 namespace will show help relative to this object:
94 94
95 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 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 131 matching names:
132 132
133 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 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 150 .. code-block:: ipython
151 151
@@ -167,7 +167,7 b' Or edit file:'
167 167
168 168 The line after the bang can call any program installed in the underlying
169 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 172 .. code-block:: ipython
173 173
@@ -176,25 +176,24 b' The later form of expansion supports arbitrary python expression:'
176 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
180 after the equal sign, or separated from it by a white space. In which case the
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.
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 this case the
181 standard output of the command after the bang will be split out into lines
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 186 .. code-block:: ipython
187 187
188 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 193 .. code-block:: ipython
194 194
195 195 my_files = !ls ~/
196 b = "backup file"
197 for i,file in enumerate(my_file):
196 for i, file in enumerate(my_files):
198 197 raw = !echo $backup $file
199 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 201 Magics
203 202 ------
204 203
205 Magics function are often present in the form of shell-like syntax, but are
206 under the hood python function. The syntax and assignment possibility are
204 Magic functions (magics) are often present in the form of shell-like syntax, but they are
205 python functions under the hood. The syntax and assignment possibilities are
207 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 211 .. code-block:: ipython
213 212
214 213 In[1]: %xmode
215 214 Exception reporting mode: Verbose
216 215
217 And support assignment:
216 Magics support assignment:
218 217
219 218 .. code-block:: ipython
220 219
@@ -224,7 +223,7 b' And support assignment:'
224 223 In [2]: results
225 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 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 238 devfs 190Ki 190Ki 0Bi 100% 656 0 100% /dev
240 239 map -hosts 0Bi 0Bi 0Bi 100% 0 0 100% /net
241 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 12 $ ipython [options] files
13 13
14 If invoked with no options, it executes all the files listed in sequence and
15 exits. If you add the ``-i`` flag, it drops you into the interpreter while still
14 If invoked with no options, it executes the file and exits, passing the
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 19 acknowledging any options you may have set in your ``ipython_config.py``. This
17 20 behavior is different from standard Python, which when called as python ``-i``
18 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 44 <...snip...>
42 45 --matplotlib=<CaselessStrEnum> (InteractiveShellApp.matplotlib)
43 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 48 Configure matplotlib for interactive use with the default matplotlib
46 49 backend.
47 50 <...snip...>
@@ -899,7 +902,8 b' For users, enabling GUI event loop integration is simple. You simple use the'
899 902 %gui [GUINAME]
900 903
901 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 908 Thus, to use wxPython interactively and create a running :class:`wx.App`
905 909 object, do::
@@ -1,5 +1,11 b''
1 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 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 50 See ``%alias?`` and ``%unalias?`` for details on the alias facilities. See also
45 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 58 Directory management
49 59 ====================
@@ -196,3 +206,9 b' provide a convenient ways to use contained text in different formats:'
196 206 * ``.s`` returns string with lines separated by single space (for
197 207 convenient passing to system commands)
198 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 90 Starting with IPython 6.0, if ``jedi`` is installed, IPython will try to pull
91 91 completions from Jedi as well. This allows to not only inspect currently
92 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 94 completion on more complex expressions like the following::
95 95
96 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 220 You can read more about using `jupyter qtconsole
221 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 223 is also a :ref:`message spec <messaging>` which documents the protocol for
224 224 communication between kernels
225 225 and clients.
@@ -234,7 +234,7 b' Interactive parallel computing'
234 234
235 235
236 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 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 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 173 .. DO NOT EDIT THIS LINE BEFORE RELEASE. FEATURE INSERTION POINT.
156 174
157 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 1099 * :ghpull:`2274`: CLN: Use name to id mapping of notebooks instead of searching.
1100 1100 * :ghpull:`2270`: SSHLauncher tweaks
1101 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 1103 * :ghpull:`2256`: Adding data publication example notebook.
1104 1104 * :ghpull:`2255`: better flush iopub with AsyncResults
1105 1105 * :ghpull:`2261`: Fix: longest_substr([]) -> ''
@@ -309,7 +309,7 b' be started by calling ``ipython qtconsole``. The protocol is :ref:`documented'
309 309 <messaging>`.
310 310
311 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 313 new :mod:`IPython.parallel` module.
314 314
315 315 .. _python3_011:
@@ -307,7 +307,7 b" and tab completions that don't clutter up your history."
307 307 :target: ../_images/ptshell_features.png
308 308
309 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 311 ``readline`` throughout our terminal interface.
312 312
313 313 Relying on this pure-Python, cross platform module also makes it simpler to
@@ -2,6 +2,309 b''
2 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 308 .. _version 721:
6 309
7 310 IPython 7.21
@@ -33,7 +336,7 b' Thanks'
33 336 ------
34 337
35 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 342 .. _version 720:
@@ -82,14 +385,14 b' was exceptionally no release last month.'
82 385 - Docs docs formatting that make the install commands work on zsh
83 386 :ghpull:`12587`
84 387 - Always display the last frame in tracebacks even if hidden with
85 ``__traceback_hide__`` :ghpull:`12601`
388 ``__tracebackhide__`` :ghpull:`12601`
86 389 - Avoid an issue where a callback can be registered multiple times.
87 390 :ghpull:`12625`
88 391 - Avoid an issue in debugger mode where frames changes could be lost.
89 392 :ghpull:`12627`
90 393
91 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 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 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 548 + IPython.core.debugger.Pdb.do_down(self, arg)
246 549 + IPython.core.debugger.Pdb.do_skip_hidden(self, arg)
@@ -150,6 +150,7 b''
150 150 "&nbsp;&nbsp;<a href='gui/gui-glut.py' target='_blank'>gui-glut.py</a><br>\n",
151 151 "&nbsp;&nbsp;<a href='gui/gui-gtk.py' target='_blank'>gui-gtk.py</a><br>\n",
152 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 154 "&nbsp;&nbsp;<a href='gui/gui-pyglet.py' target='_blank'>gui-pyglet.py</a><br>\n",
154 155 "&nbsp;&nbsp;<a href='gui/gui-qt.py' target='_blank'>gui-qt.py</a><br>\n",
155 156 "&nbsp;&nbsp;<a href='gui/gui-tk.py' target='_blank'>gui-tk.py</a><br>\n",
@@ -160,6 +161,7 b''
160 161 " gui-glut.py\n",
161 162 " gui-gtk.py\n",
162 163 " gui-gtk3.py\n",
164 " gui-gtk4.py\n",
163 165 " gui-pyglet.py\n",
164 166 " gui-qt.py\n",
165 167 " gui-tk.py\n",
@@ -3180,6 +3180,7 b''
3180 3180 "&nbsp;&nbsp;<a href='./gui/gui-glut.py' target='_blank'>gui-glut.py</a><br>\n",
3181 3181 "&nbsp;&nbsp;<a href='./gui/gui-gtk.py' target='_blank'>gui-gtk.py</a><br>\n",
3182 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 3184 "&nbsp;&nbsp;<a href='./gui/gui-pyglet.py' target='_blank'>gui-pyglet.py</a><br>\n",
3184 3185 "&nbsp;&nbsp;<a href='./gui/gui-qt.py' target='_blank'>gui-qt.py</a><br>\n",
3185 3186 "&nbsp;&nbsp;<a href='./gui/gui-tk.py' target='_blank'>gui-tk.py</a><br>\n",
@@ -3230,6 +3231,7 b''
3230 3231 " gui-glut.py\n",
3231 3232 " gui-gtk.py\n",
3232 3233 " gui-gtk3.py\n",
3234 " gui-gtk4.py\n",
3233 3235 " gui-pyglet.py\n",
3234 3236 " gui-qt.py\n",
3235 3237 " gui-tk.py\n",
@@ -1,2 +1,7 b''
1 1 [metadata]
2 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 171 # setuptools requirements
172 172
173 173 extras_require = dict(
174 parallel = ['ipyparallel'],
175 qtconsole = ['qtconsole'],
176 doc = ['Sphinx>=1.3'],
177 test = ['nose>=0.10.1', 'requests', 'testpath', 'pygments', 'nbformat', 'ipykernel', 'numpy>=1.14'],
174 parallel=["ipyparallel"],
175 qtconsole=["qtconsole"],
176 doc=["Sphinx>=1.3"],
177 test=[
178 "nose>=0.10.1",
179 "requests",
180 "testpath",
181 "pygments",
182 "nbformat",
183 "ipykernel",
184 "numpy>=1.17",
185 ],
178 186 terminal = [],
179 kernel = ['ipykernel'],
180 nbformat = ['nbformat'],
181 notebook = ['notebook', 'ipywidgets'],
182 nbconvert = ['nbconvert'],
187 kernel=["ipykernel"],
188 nbformat=["nbformat"],
189 notebook=["notebook", "ipywidgets"],
190 nbconvert=["nbconvert"],
183 191 )
184 192
185 193 install_requires = [
186 'setuptools>=18.5',
187 'jedi>=0.16',
188 'decorator',
189 'pickleshare',
190 'traitlets>=4.2',
191 'prompt_toolkit>=2.0.0,<3.1.0,!=3.0.0,!=3.0.1',
192 'pygments',
193 'backcall',
194 'stack_data',
194 "setuptools>=18.5",
195 "jedi>=0.16",
196 "decorator",
197 "pickleshare",
198 "traitlets>=4.2",
199 "prompt_toolkit>=2.0.0,<3.1.0,!=3.0.0,!=3.0.1",
200 "pygments",
201 "backcall",
202 "stack_data",
203 "matplotlib-inline",
195 204 ]
196 205
197 206 # Platform-specific dependencies:
@@ -21,6 +21,7 b' WHITE=$(tput setaf 7)'
21 21 NOR=$(tput sgr0)
22 22
23 23
24 echo "Will use $EDITOR to edit files when necessary"
24 25 echo -n "PREV_RELEASE (X.y.z) [$PREV_RELEASE]: "
25 26 read input
26 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 64 echo
52 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 117 sed -i bkp -e '/Uncomment/s/^# //g' IPython/core/release.py
105 118 rm IPython/core/release.pybkp
106 119 git diff
120 maybe_edit IPython/core/release.py
107 121
108 122 echo $GREEN"Press enter to continue"$NOR
109 123 read
@@ -151,6 +165,7 b' then'
151 165 rm IPython/core/release.pybkp
152 166 git diff
153 167 echo $GREEN"Please bump ${RED}the minor version number${NOR}"
168 maybe_edit IPython/core/release.py
154 169 echo ${BLUE}"Do not commit yet – we'll do it later."$NOR
155 170
156 171
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now