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 |
|
|
30 |
mypy IPython |
|
|
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/ |
@@ -68,20 +68,19 b' version_info = release.version_info' | |||
|
68 | 68 | |
|
69 | 69 | def embed_kernel(module=None, local_ns=None, **kwargs): |
|
70 | 70 | """Embed and start an IPython kernel in a given scope. |
|
71 | ||
|
71 | ||
|
72 | 72 | If you don't want the kernel to initialize the namespace |
|
73 | 73 | from the scope of the surrounding function, |
|
74 | 74 | and/or you want to load full IPython configuration, |
|
75 | 75 | you probably want `IPython.start_kernel()` instead. |
|
76 | ||
|
76 | ||
|
77 | 77 | Parameters |
|
78 | 78 | ---------- |
|
79 | 79 | module : types.ModuleType, optional |
|
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. |
@@ -99,26 +98,25 b' def embed_kernel(module=None, local_ns=None, **kwargs):' | |||
|
99 | 98 | |
|
100 | 99 | def start_ipython(argv=None, **kwargs): |
|
101 | 100 | """Launch a normal IPython instance (as opposed to embedded) |
|
102 | ||
|
101 | ||
|
103 | 102 | `IPython.embed()` puts a shell in a particular calling scope, |
|
104 | 103 | such as a function or method for debugging purposes, |
|
105 | 104 | which is often not desirable. |
|
106 | ||
|
105 | ||
|
107 | 106 | `start_ipython()` does full, regular IPython initialization, |
|
108 | 107 | including loading startup files, configuration, etc. |
|
109 | 108 | much of which is skipped by `embed()`. |
|
110 | ||
|
109 | ||
|
111 | 110 | This is a public API method, and will survive implementation changes. |
|
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 | """ |
@@ -127,24 +125,23 b' def start_ipython(argv=None, **kwargs):' | |||
|
127 | 125 | |
|
128 | 126 | def start_kernel(argv=None, **kwargs): |
|
129 | 127 | """Launch a normal IPython kernel instance (as opposed to embedded) |
|
130 | ||
|
128 | ||
|
131 | 129 | `IPython.embed_kernel()` puts a shell in a particular calling scope, |
|
132 | 130 | such as a function or method for debugging purposes, |
|
133 | 131 | which is often not desirable. |
|
134 | ||
|
132 | ||
|
135 | 133 | `start_kernel()` does full, regular IPython initialization, |
|
136 | 134 | including loading startup files, configuration, etc. |
|
137 | 135 | much of which is skipped by `embed()`. |
|
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,27 +66,49 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', | |
|
75 | } | |
|
76 | ||
|
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 | |
|
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", | |
|
80 | } | |
|
81 | ) | |
|
82 | ||
|
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 |
|
|
87 |
|
|
|
106 | """, | |
|
107 | ), | |
|
108 | ) | |
|
88 | 109 | ) |
|
89 | 110 | |
|
111 | ||
|
90 | 112 | class ProfileAwareConfigLoader(PyFileConfigLoader): |
|
91 | 113 | """A Python file config loader that is aware of IPython profiles.""" |
|
92 | 114 | def load_subconfig(self, fname, path=None, profile=None): |
@@ -254,7 +276,7 b' class BaseIPythonApplication(Application):' | |||
|
254 | 276 | |
|
255 | 277 | def excepthook(self, etype, evalue, tb): |
|
256 | 278 | """this is sys.excepthook after init_crashhandler |
|
257 | ||
|
279 | ||
|
258 | 280 | set self.verbose_crash=True to use our full crashhandler, instead of |
|
259 | 281 | a regular traceback with a short message (crash_handler_lite) |
|
260 | 282 | """ |
@@ -41,9 +41,9 b' class IPyAutocall(object):' | |||
|
41 | 41 | |
|
42 | 42 | def set_ip(self, ip): |
|
43 | 43 | """ Will be used to set _ip point to current ipython instance b/f call |
|
44 | ||
|
44 | ||
|
45 | 45 | Override this method if you don't want this to happen. |
|
46 | ||
|
46 | ||
|
47 | 47 | """ |
|
48 | 48 | self._ip = ip |
|
49 | 49 |
@@ -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. |
@@ -118,12 +122,12 b' class CachingCompiler(codeop.Compile):' | |||
|
118 | 122 | Parameters |
|
119 | 123 | ---------- |
|
120 | 124 | raw_code : str |
|
121 | The raw cell code. | |
|
125 | The raw cell code. | |
|
122 | 126 | transformed_code : str |
|
123 | The executable Python source code to cache and compile. | |
|
127 | The executable Python source code to cache and compile. | |
|
124 | 128 | number : int |
|
125 | A number which forms part of the code's name. Used for the execution | |
|
126 | counter. | |
|
129 | A number which forms part of the code's name. Used for the execution | |
|
130 | counter. | |
|
127 | 131 | |
|
128 | 132 | Returns |
|
129 | 133 | ------- |
@@ -137,12 +141,12 b' class CachingCompiler(codeop.Compile):' | |||
|
137 | 141 | Parameters |
|
138 | 142 | ---------- |
|
139 | 143 | transformed_code : str |
|
140 | The executable Python source code to cache and compile. | |
|
144 | The executable Python source code to cache and compile. | |
|
141 | 145 | number : int |
|
142 | A number which forms part of the code's name. Used for the execution | |
|
143 | counter. | |
|
146 | A number which forms part of the code's name. Used for the execution | |
|
147 | counter. | |
|
144 | 148 | raw_code : str |
|
145 | The raw code before transformation, if None, set to `transformed_code`. | |
|
149 | The raw code before transformation, if None, set to `transformed_code`. | |
|
146 | 150 | |
|
147 | 151 | Returns |
|
148 | 152 | ------- |
@@ -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(), |
@@ -431,9 +431,9 b' def _deduplicate_completions(text: str, completions: _IC)-> _IC:' | |||
|
431 | 431 | |
|
432 | 432 | Parameters |
|
433 | 433 | ---------- |
|
434 | text: str | |
|
434 | text : str | |
|
435 | 435 | text that should be completed. |
|
436 | completions: Iterator[Completion] | |
|
436 | completions : Iterator[Completion] | |
|
437 | 437 | iterator over the completions to deduplicate |
|
438 | 438 | |
|
439 | 439 | Yields |
@@ -474,9 +474,9 b' def rectify_completions(text: str, completions: _IC, *, _debug=False)->_IC:' | |||
|
474 | 474 | |
|
475 | 475 | Parameters |
|
476 | 476 | ---------- |
|
477 | text: str | |
|
477 | text : str | |
|
478 | 478 | text that should be completed. |
|
479 | completions: Iterator[Completion] | |
|
479 | completions : Iterator[Completion] | |
|
480 | 480 | iterator over the completions to rectify |
|
481 | 481 | |
|
482 | 482 | Notes |
@@ -763,13 +763,13 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 | extra_prefix: optional | |
|
772 | extra_prefix : optional | |
|
773 | 773 | Part of the text already typed in multi-key index cases. E.g. for |
|
774 | 774 | `mydict['foo', "bar", 'b`, this would be `('foo', 'bar')`. |
|
775 | 775 | |
@@ -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 |
@@ -1013,7 +1013,7 b' def _make_signature(completion)-> str:' | |||
|
1013 | 1013 | |
|
1014 | 1014 | Parameters |
|
1015 | 1015 | ---------- |
|
1016 | completion: jedi.Completion | |
|
1016 | completion : jedi.Completion | |
|
1017 | 1017 | object does not complete a function type |
|
1018 | 1018 | |
|
1019 | 1019 | Returns |
@@ -1854,9 +1854,9 b' class IPCompleter(Completer):' | |||
|
1854 | 1854 | |
|
1855 | 1855 | Parameters |
|
1856 | 1856 | ---------- |
|
1857 | text:str | |
|
1857 | text : str | |
|
1858 | 1858 | Full text of the current input, multi line string. |
|
1859 | offset:int | |
|
1859 | offset : int | |
|
1860 | 1860 | Integer representing the position of the cursor in ``text``. Offset |
|
1861 | 1861 | is 0-based indexed. |
|
1862 | 1862 |
@@ -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 |
|
|
|
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)} |
@@ -101,25 +101,19 b' class CrashHandler(object):' | |||
|
101 | 101 | |
|
102 | 102 | Parameters |
|
103 | 103 | ---------- |
|
104 |
app : |
|
|
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,21 +199,39 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. | |
|
215 | The possibilities are python version dependent, see the python | |
|
216 | docs for more info. | |
|
228 | **kwargs | |
|
229 | Passed to pdb.Pdb. | |
|
230 | ||
|
231 | Notes | |
|
232 | ----- | |
|
233 | The possibilities are python version dependent, see the python | |
|
234 | docs for more info. | |
|
217 | 235 | """ |
|
218 | 236 | |
|
219 | 237 | # Parent constructor: |
@@ -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__'] | |
|
427 | #return_value += '->' | |
|
428 |
return_value += |
|
|
484 | loc_frame = self._get_frame_locals(frame) | |
|
485 | if "__return__" in loc_frame: | |
|
486 | rv = loc_frame["__return__"] | |
|
487 | # return_value += '->' | |
|
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 |
|
|
443 |
args = reprlib.repr(frame |
|
|
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 |
|
|
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: |
|
710 | Colors = self.color_scheme_table.active_colors | |
|
711 | ColorsNormal = Colors.Normal | |
|
712 | print(f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n") | |
|
713 | ||
|
823 | if self.report_skipped: | |
|
824 | Colors = self.color_scheme_table.active_colors | |
|
825 | ColorsNormal = Colors.Normal | |
|
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) |
@@ -62,7 +62,7 b' def publish_display_data(data, metadata=None, source=None, *, transient=None, **' | |||
|
62 | 62 | Unused. |
|
63 | 63 | transient : dict, keyword-only |
|
64 | 64 | A dictionary of transient data, such as display_id. |
|
65 |
|
|
|
65 | """ | |
|
66 | 66 | from IPython.core.interactiveshell import InteractiveShell |
|
67 | 67 | |
|
68 | 68 | display_pub = InteractiveShell.instance().display_pub |
@@ -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 | display_id: keyword-only | |
|
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) |
@@ -146,7 +146,7 b' class DisplayHook(Configurable):' | |||
|
146 | 146 | MIME type representation of the object. |
|
147 | 147 | md_dict is a :class:`dict` with the same MIME type keys |
|
148 | 148 | of metadata associated with each output. |
|
149 | ||
|
149 | ||
|
150 | 150 | """ |
|
151 | 151 | return self.shell.display_formatter.format(result) |
|
152 | 152 |
@@ -94,11 +94,11 b' class DisplayPublisher(Configurable):' | |||
|
94 | 94 | the data itself. |
|
95 | 95 | source : str, deprecated |
|
96 | 96 | Unused. |
|
97 | transient: dict, keyword-only | |
|
97 | transient : dict, keyword-only | |
|
98 | 98 | A dictionary for transient data. |
|
99 | 99 | Data in this dictionary should not be persisted as part of saving this output. |
|
100 | 100 | Examples include 'display_id'. |
|
101 | update: bool, keyword-only, default: False | |
|
101 | update : bool, keyword-only, default: False | |
|
102 | 102 | If True, only update existing outputs with the same display_id, |
|
103 | 103 | rather than creating a new output. |
|
104 | 104 | """ |
@@ -28,34 +28,34 b' class EventManager(object):' | |||
|
28 | 28 | """ |
|
29 | 29 | def __init__(self, shell, available_events): |
|
30 | 30 | """Initialise the :class:`CallbackManager`. |
|
31 | ||
|
31 | ||
|
32 | 32 | Parameters |
|
33 | 33 | ---------- |
|
34 | 34 | shell |
|
35 | The :class:`~IPython.core.interactiveshell.InteractiveShell` instance | |
|
36 |
available_ |
|
|
37 | An iterable of names for callback events. | |
|
35 | The :class:`~IPython.core.interactiveshell.InteractiveShell` instance | |
|
36 | available_events | |
|
37 | An iterable of names for callback events. | |
|
38 | 38 | """ |
|
39 | 39 | self.shell = shell |
|
40 | 40 | self.callbacks = {n:[] for n in available_events} |
|
41 | 41 | |
|
42 | 42 | def register(self, event, function): |
|
43 | 43 | """Register a new event callback. |
|
44 | ||
|
44 | ||
|
45 | 45 | Parameters |
|
46 | 46 | ---------- |
|
47 | 47 | event : str |
|
48 | The event for which to register this callback. | |
|
48 | The event for which to register this callback. | |
|
49 | 49 | function : callable |
|
50 | A function to be called on the given event. It should take the same | |
|
51 | parameters as the appropriate callback prototype. | |
|
52 | ||
|
50 | A function to be called on the given event. It should take the same | |
|
51 | parameters as the appropriate callback prototype. | |
|
52 | ||
|
53 | 53 | Raises |
|
54 | 54 | ------ |
|
55 | 55 | TypeError |
|
56 | If ``function`` is not callable. | |
|
56 | If ``function`` is not callable. | |
|
57 | 57 | KeyError |
|
58 | If ``event`` is not one of the known events. | |
|
58 | If ``event`` is not one of the known events. | |
|
59 | 59 | """ |
|
60 | 60 | if not callable(function): |
|
61 | 61 | raise TypeError('Need a callable, got %r' % function) |
@@ -80,7 +80,7 b' class EventManager(object):' | |||
|
80 | 80 | |
|
81 | 81 | def trigger(self, event, *args, **kwargs): |
|
82 | 82 | """Call callbacks for ``event``. |
|
83 | ||
|
83 | ||
|
84 | 84 | Any additional arguments are passed to all callbacks registered for this |
|
85 | 85 | event. Exceptions raised by callbacks are caught, and a message printed. |
|
86 | 86 | """ |
@@ -109,7 +109,7 b' def _define_event(callback_function):' | |||
|
109 | 109 | @_define_event |
|
110 | 110 | def pre_execute(): |
|
111 | 111 | """Fires before code is executed in response to user/frontend action. |
|
112 | ||
|
112 | ||
|
113 | 113 | This includes comm and widget messages and silent execution, as well as user |
|
114 | 114 | code cells. |
|
115 | 115 | """ |
@@ -122,14 +122,14 b' def pre_run_cell(info):' | |||
|
122 | 122 | Parameters |
|
123 | 123 | ---------- |
|
124 | 124 | info : :class:`~IPython.core.interactiveshell.ExecutionInfo` |
|
125 | An object containing information used for the code execution. | |
|
125 | An object containing information used for the code execution. | |
|
126 | 126 | """ |
|
127 | 127 | pass |
|
128 | 128 | |
|
129 | 129 | @_define_event |
|
130 | 130 | def post_execute(): |
|
131 | 131 | """Fires after code is executed in response to user/frontend action. |
|
132 | ||
|
132 | ||
|
133 | 133 | This includes comm and widget messages and silent execution, as well as user |
|
134 | 134 | code cells. |
|
135 | 135 | """ |
@@ -142,20 +142,20 b' def post_run_cell(result):' | |||
|
142 | 142 | Parameters |
|
143 | 143 | ---------- |
|
144 | 144 | result : :class:`~IPython.core.interactiveshell.ExecutionResult` |
|
145 | The object which will be returned as the execution result. | |
|
145 | The object which will be returned as the execution result. | |
|
146 | 146 | """ |
|
147 | 147 | pass |
|
148 | 148 | |
|
149 | 149 | @_define_event |
|
150 | 150 | def shell_initialized(ip): |
|
151 | 151 | """Fires after initialisation of :class:`~IPython.core.interactiveshell.InteractiveShell`. |
|
152 | ||
|
152 | ||
|
153 | 153 | This is before extensions and startup scripts are loaded, so it can only be |
|
154 | 154 | set by subclassing. |
|
155 | ||
|
155 | ||
|
156 | 156 | Parameters |
|
157 | 157 | ---------- |
|
158 | 158 | ip : :class:`~IPython.core.interactiveshell.InteractiveShell` |
|
159 | The newly initialised shell. | |
|
159 | The newly initialised shell. | |
|
160 | 160 | """ |
|
161 | 161 | pass |
@@ -94,7 +94,7 b' class ExtensionManager(Configurable):' | |||
|
94 | 94 | |
|
95 | 95 | This function looks up the extension's name in ``sys.modules`` and |
|
96 | 96 | simply calls ``mod.unload_ipython_extension(self)``. |
|
97 | ||
|
97 | ||
|
98 | 98 | Returns the string "no unload function" if the extension doesn't define |
|
99 | 99 | a function to unload itself, "not loaded" if the extension isn't loaded, |
|
100 | 100 | otherwise None. |
@@ -121,19 +121,17 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 |
|
128 | 127 | data structure containing the raw data for the representation in |
|
129 | 128 | that format. |
|
130 | ||
|
129 | ||
|
131 | 130 | metadata_dict is a dictionary of metadata about each mime-type output. |
|
132 | 131 | Its keys will be a strict subset of the keys in format_dict. |
|
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 |
@@ -263,7 +261,7 b' class FormatterABC(metaclass=abc.ABCMeta):' | |||
|
263 | 261 | |
|
264 | 262 | def _mod_name_key(typ): |
|
265 | 263 | """Return a (__module__, __name__) tuple for a type. |
|
266 | ||
|
264 | ||
|
267 | 265 | Used as key in Formatter.deferred_printers. |
|
268 | 266 | """ |
|
269 | 267 | module = getattr(typ, '__module__', None) |
@@ -358,7 +356,7 b' class BaseFormatter(Configurable):' | |||
|
358 | 356 | |
|
359 | 357 | def _check_return(self, r, obj): |
|
360 | 358 | """Check that a return value is appropriate |
|
361 | ||
|
359 | ||
|
362 | 360 | Return the value if so, None otherwise, warning if invalid. |
|
363 | 361 | """ |
|
364 | 362 | if r is None or isinstance(r, self._return_type) or \ |
@@ -373,10 +371,10 b' class BaseFormatter(Configurable):' | |||
|
373 | 371 | |
|
374 | 372 | def lookup(self, obj): |
|
375 | 373 | """Look up the formatter for a given instance. |
|
376 | ||
|
374 | ||
|
377 | 375 | Parameters |
|
378 | 376 | ---------- |
|
379 |
obj |
|
|
377 | obj : object instance | |
|
380 | 378 | |
|
381 | 379 | Returns |
|
382 | 380 | ------- |
@@ -399,7 +397,7 b' class BaseFormatter(Configurable):' | |||
|
399 | 397 | |
|
400 | 398 | Parameters |
|
401 | 399 | ---------- |
|
402 |
typ |
|
|
400 | typ : type or '__module__.__name__' string for a type | |
|
403 | 401 | |
|
404 | 402 | Returns |
|
405 | 403 | ------- |
@@ -430,7 +428,7 b' class BaseFormatter(Configurable):' | |||
|
430 | 428 | |
|
431 | 429 | def for_type(self, typ, func=None): |
|
432 | 430 | """Add a format function for a given type. |
|
433 | ||
|
431 | ||
|
434 | 432 | Parameters |
|
435 | 433 | ---------- |
|
436 | 434 | typ : type or '__module__.__name__' string for a type |
@@ -441,10 +439,10 b' class BaseFormatter(Configurable):' | |||
|
441 | 439 | and will return the raw data in this formatter's format. |
|
442 | 440 | Subclasses may use a different call signature for the |
|
443 | 441 | `func` argument. |
|
444 | ||
|
442 | ||
|
445 | 443 | If `func` is None or not specified, there will be no change, |
|
446 | 444 | only returning the current value. |
|
447 | ||
|
445 | ||
|
448 | 446 | Returns |
|
449 | 447 | ------- |
|
450 | 448 | oldfunc : callable |
@@ -484,10 +482,10 b' class BaseFormatter(Configurable):' | |||
|
484 | 482 | and will return the raw data in this formatter's format. |
|
485 | 483 | Subclasses may use a different call signature for the |
|
486 | 484 | `func` argument. |
|
487 | ||
|
485 | ||
|
488 | 486 | If `func` is None or unspecified, there will be no change, |
|
489 | 487 | only returning the current value. |
|
490 | ||
|
488 | ||
|
491 | 489 | Returns |
|
492 | 490 | ------- |
|
493 | 491 | oldfunc : callable |
@@ -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') |
@@ -823,7 +825,7 b' class JSONFormatter(BaseFormatter):' | |||
|
823 | 825 | |
|
824 | 826 | def _check_return(self, r, obj): |
|
825 | 827 | """Check that a return value is appropriate |
|
826 | ||
|
828 | ||
|
827 | 829 | Return the value if so, None otherwise, warning if invalid. |
|
828 | 830 | """ |
|
829 | 831 | if r is None: |
@@ -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". |
|
|
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) |
@@ -921,14 +921,13 b' class InteractiveShell(SingletonConfigurable):' | |||
|
921 | 921 | while p.is_symlink(): |
|
922 | 922 | p = Path(os.readlink(p)) |
|
923 | 923 | paths.append(p.resolve()) |
|
924 | ||
|
924 | ||
|
925 | 925 | # In Cygwin paths like "c:\..." and '\cygdrive\c\...' are possible |
|
926 |
if |
|
|
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( |
|
|
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 ex |
|
|
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 |
|
|
|
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. |
@@ -2212,12 +2220,15 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2212 | 2220 | |
|
2213 | 2221 | Returns |
|
2214 | 2222 | ------- |
|
2215 |
|
|
|
2216 |
|
|
|
2223 | text : string | |
|
2224 | The actual text that was completed. | |
|
2225 | ||
|
2226 | matches : list | |
|
2227 | A sorted list with all possible completions. | |
|
2217 | 2228 | |
|
2218 | matches : list | |
|
2219 | A sorted list with all possible completions. | |
|
2220 | 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 |
|
|
|
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 = |
|
|
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 |
|
|
|
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 : str |
|
|
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 |
|
|
|
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( |
|
|
633 |
|
|
|
634 | for o,a in opts: | |
|
635 |
|
|
|
635 | raise UsageError( | |
|
636 | '%s ( allowed: "%s" %s)' % (e.msg, opt_str, " ".join(long_opts)) | |
|
637 | ) from e | |
|
638 | for o, a in opts: | |
|
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 ( |
|
|
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 ( |
|
|
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:", |
|
|
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 |
|
|
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 |
|
|
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, |
|
|
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( |
|
|
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,22 +1235,25 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: | |
|
1242 | 1238 | |
|
1243 | In [5]: %time 3**9999; | |
|
1244 | CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s | |
|
1245 | Wall time: 0.00 s | |
|
1239 | .. note:: | |
|
1240 | The time needed by Python to compile the given expression will be | |
|
1241 | reported if it is more than 0.1s. | |
|
1246 | 1242 | |
|
1247 | In [6]: %time 3**999999; | |
|
1248 | CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s | |
|
1249 | Wall time: 0.00 s | |
|
1250 |
|
|
|
1251 | """ | |
|
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:: | |
|
1252 | 1247 |
|
|
1248 | In [5]: %time 3**9999; | |
|
1249 | CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s | |
|
1250 | Wall time: 0.00 s | |
|
1251 | ||
|
1252 | In [6]: %time 3**999999; | |
|
1253 | CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s | |
|
1254 | Wall time: 0.00 s | |
|
1255 | Compiler : 0.78 s | |
|
1256 | """ | |
|
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 | if args.pattern: | |
|
189 | range_pattern = "*" + " ".join(args.pattern) + "*" | |
|
190 | 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) | |
|
187 | if args.pattern: | |
|
188 | range_pattern = "*" + " ".join(args.pattern) + "*" | |
|
189 | print_nums = True | |
|
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 |
|
|
307 |
|
|
|
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 |
|
|
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. | |
|
314 | 317 | |
|
315 | cd -<n>: changes to the n-th directory in the directory history. | |
|
318 | .. note:: | |
|
319 | ``cd <bookmark_name>`` is enough if there is no directory | |
|
320 | ``<bookmark_name>``, but a bookmark with the name exists. | |
|
316 | 321 | |
|
317 | cd --foo: change to directory that matches 'foo' in history | |
|
318 | ||
|
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 |
|
|
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,18 +806,17 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 | try : | |
|
819 | try: | |
|
818 | 820 | cont = self.shell.find_user_code(parameter_s, skip_encoding_cookie=False) |
|
819 | 821 | except (ValueError, IOError): |
|
820 | 822 | print("Error: no such file, variable, URL, history range or macro") |
@@ -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,30 +5,34 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 = { |
|
|
15 | 'gtk': 'GTKAgg', | |
|
16 |
|
|
|
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', | |
|
31 | } | |
|
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", | |
|
35 | } | |
|
32 | 36 | |
|
33 | 37 | # We also need a reverse backends2guis mapping that will properly choose which |
|
34 | 38 | # GUI support to activate based on the desired matplotlib backend. For the |
@@ -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[ |
|
|
43 |
backend2gui[ |
|
|
44 | backend2gui['WX'] = 'wx' | |
|
45 |
backend2gui[ |
|
|
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[ |
|
|
48 |
del backend2gui[ |
|
|
49 |
del backend2gui[ |
|
|
50 |
del backend2gui[ |
|
|
51 |
del backend2gui[ |
|
|
52 |
del backend2gui[ |
|
|
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 |
@@ -96,10 +101,10 b' def figsize(sizex, sizey):' | |||
|
96 | 101 | |
|
97 | 102 | def print_figure(fig, fmt='png', bbox_inches='tight', **kwargs): |
|
98 | 103 | """Print a figure to an image, and return the resulting file data |
|
99 | ||
|
104 | ||
|
100 | 105 | Returned data will be bytes unless ``fmt='svg'``, |
|
101 | 106 | in which case it will be unicode. |
|
102 | ||
|
107 | ||
|
103 | 108 | Any keyword args are passed to fig.canvas.print_figure, |
|
104 | 109 | such as ``quality`` or ``bbox_inches``. |
|
105 | 110 | """ |
@@ -112,7 +117,7 b" def print_figure(fig, fmt='png', bbox_inches='tight', **kwargs):" | |||
|
112 | 117 | if fmt == 'retina': |
|
113 | 118 | dpi = dpi * 2 |
|
114 | 119 | fmt = 'png' |
|
115 | ||
|
120 | ||
|
116 | 121 | # build keyword args |
|
117 | 122 | kw = { |
|
118 | 123 | "format":fmt, |
@@ -162,7 +167,7 b' def mpl_runner(safe_execfile):' | |||
|
162 | 167 | A function suitable for use as the ``runner`` argument of the %run magic |
|
163 | 168 | function. |
|
164 | 169 | """ |
|
165 | ||
|
170 | ||
|
166 | 171 | def mpl_execfile(fname,*where,**kw): |
|
167 | 172 | """matplotlib-aware wrapper around safe_execfile. |
|
168 | 173 | |
@@ -243,7 +248,7 b' def select_figure_formats(shell, formats, **kwargs):' | |||
|
243 | 248 | bs = "%s" % ','.join([repr(f) for f in bad]) |
|
244 | 249 | gs = "%s" % ','.join([repr(f) for f in supported]) |
|
245 | 250 | raise ValueError("supported formats are: %s not %s" % (gs, bs)) |
|
246 | ||
|
251 | ||
|
247 | 252 | if 'png' in formats: |
|
248 | 253 | png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png', **kwargs)) |
|
249 | 254 | if 'retina' in formats or 'png2x' in formats: |
@@ -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:// |
|
|
282 | 'WXAgg','Qt4Agg','module://matplotlib_inline.backend_inline','agg'). | |
|
278 | 283 | """ |
|
279 | 284 | |
|
280 | 285 | import matplotlib |
@@ -308,7 +313,7 b' def activate_matplotlib(backend):' | |||
|
308 | 313 | |
|
309 | 314 | import matplotlib |
|
310 | 315 | matplotlib.interactive(True) |
|
311 | ||
|
316 | ||
|
312 | 317 | # Matplotlib had a bug where even switch_backend could not force |
|
313 | 318 | # the rcParam to update. This needs to be set *before* the module |
|
314 | 319 | # magic of switch_backend(). |
@@ -329,11 +334,11 b' def activate_matplotlib(backend):' | |||
|
329 | 334 | |
|
330 | 335 | def import_pylab(user_ns, import_all=True): |
|
331 | 336 | """Populate the namespace with pylab-related values. |
|
332 | ||
|
337 | ||
|
333 | 338 | Imports matplotlib, pylab, numpy, and everything from pylab and numpy. |
|
334 | ||
|
339 | ||
|
335 | 340 | Also imports a few names from IPython (figsize, display, getfigs) |
|
336 | ||
|
341 | ||
|
337 | 342 | """ |
|
338 | 343 | |
|
339 | 344 | # Import numpy as np/pyplot as plt are conventions we're trying to |
@@ -346,12 +351,12 b' def import_pylab(user_ns, import_all=True):' | |||
|
346 | 351 | "plt = pyplot\n" |
|
347 | 352 | ) |
|
348 | 353 | exec(s, user_ns) |
|
349 | ||
|
354 | ||
|
350 | 355 | if import_all: |
|
351 | 356 | s = ("from matplotlib.pylab import *\n" |
|
352 | 357 | "from numpy import *\n") |
|
353 | 358 | exec(s, user_ns) |
|
354 | ||
|
359 | ||
|
355 | 360 | # IPython symbols to add |
|
356 | 361 | user_ns['figsize'] = figsize |
|
357 | 362 | from IPython.display import display |
@@ -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. | |
|
375 | ||
|
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 | |
|
384 | ||
|
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 | |
|
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 | ) | |
|
388 | ||
|
389 | from matplotlib_inline.backend_inline import ( | |
|
390 | configure_inline_support as configure_inline_support_orig, | |
|
391 | ) | |
|
392 | ||
|
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 |
|
|
|
13 |
|
|
|
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 |
|
|
|
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 |
|
|
29 | with pytest.raises(ValueError): | |
|
30 | 30 | am.retrieve_alias(name) |
|
31 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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) |
@@ -277,14 +277,14 b' def test_xmode_skip():' | |||
|
277 | 277 | |
|
278 | 278 | block = dedent( |
|
279 | 279 | """ |
|
280 | def f(): | |
|
281 | __tracebackhide__ = True | |
|
282 | g() | |
|
280 | def f(): | |
|
281 | __tracebackhide__ = True | |
|
282 | g() | |
|
283 | 283 | |
|
284 | def g(): | |
|
285 | raise ValueError | |
|
284 | def g(): | |
|
285 | raise ValueError | |
|
286 | 286 | |
|
287 | f() | |
|
287 | f() | |
|
288 | 288 | """ |
|
289 | 289 | ) |
|
290 | 290 | |
@@ -295,15 +295,15 b' f()' | |||
|
295 | 295 | |
|
296 | 296 | block = dedent( |
|
297 | 297 | """ |
|
298 | def f(): | |
|
299 | __tracebackhide__ = True | |
|
300 | g() | |
|
298 | def f(): | |
|
299 | __tracebackhide__ = True | |
|
300 | g() | |
|
301 | 301 | |
|
302 | def g(): | |
|
303 | from IPython.core.debugger import set_trace | |
|
304 | set_trace() | |
|
302 | def g(): | |
|
303 | from IPython.core.debugger import set_trace | |
|
304 | set_trace() | |
|
305 | 305 | |
|
306 | f() | |
|
306 | f() | |
|
307 | 307 | """ |
|
308 | 308 | ) |
|
309 | 309 | |
@@ -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 |
|
|
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( |
|
|
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 |
|
|
|
52 |
|
|
|
53 |
|
|
|
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 |
|
|
|
59 |
|
|
|
60 |
|
|
|
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 |
|
|
|
69 | assert f(pi) == repr(pi) | |
|
70 | 70 | f.float_precision = 0 |
|
71 | 71 | if numpy: |
|
72 | 72 | po = numpy.get_printoptions() |
|
73 |
|
|
|
74 |
|
|
|
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 |
|
|
|
79 |
|
|
|
80 |
f.float_precision = |
|
|
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 |
|
|
|
84 |
|
|
|
85 |
f.float_precision = |
|
|
86 |
|
|
|
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 |
|
|
|
91 |
|
|
|
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 |
|
|
|
264 |
|
|
|
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 |
|
|
|
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 |
|
|
|
325 |
|
|
|
327 | assert result == "hello" | |
|
328 | assert captured.stderr == "" | |
|
329 | ||
|
326 | 330 | |
|
327 | 331 | def test_print_method_weird(): |
|
328 | 332 | |
@@ -331,9 +335,9 b' def test_print_method_weird():' | |||
|
331 | 335 | return key |
|
332 | 336 | |
|
333 | 337 | f = HTMLFormatter() |
|
334 | ||
|
338 | ||
|
335 | 339 | text_hat = TextMagicHat() |
|
336 |
|
|
|
340 | assert text_hat._repr_html_ == "_repr_html_" | |
|
337 | 341 | with capture_output() as captured: |
|
338 | 342 | result = f(text_hat) |
|
339 | 343 | |
@@ -347,8 +351,8 b' def test_print_method_weird():' | |||
|
347 | 351 | call_hat = CallableMagicHat() |
|
348 | 352 | with capture_output() as captured: |
|
349 | 353 | result = f(call_hat) |
|
350 | ||
|
351 |
|
|
|
354 | ||
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
388 | assert text == "[0, ...]" | |
|
384 | 389 | f.max_seq_length = 0 |
|
385 | 390 | text = f(lis) |
|
386 |
|
|
|
391 | assert text == "[0, 1, 2]" | |
|
387 | 392 | text = f(list(range(1024))) |
|
388 | 393 | lines = text.splitlines() |
|
389 |
|
|
|
394 | assert len(lines) == 1024 | |
|
390 | 395 | |
|
391 | 396 | |
|
392 | 397 | def test_ipython_display_formatter(): |
@@ -409,16 +414,16 b' def test_ipython_display_formatter():' | |||
|
409 | 414 | |
|
410 | 415 | yes = SelfDisplaying() |
|
411 | 416 | no = NotSelfDisplaying() |
|
412 | ||
|
417 | ||
|
413 | 418 | d, md = f.format(no) |
|
414 |
|
|
|
415 |
|
|
|
416 |
|
|
|
417 | ||
|
419 | assert d == {"text/plain": repr(no)} | |
|
420 | assert md == {} | |
|
421 | assert catcher == [] | |
|
422 | ||
|
418 | 423 | d, md = f.format(yes) |
|
419 |
|
|
|
420 |
|
|
|
421 |
|
|
|
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 |
|
|
|
435 |
|
|
|
439 | assert d == {} | |
|
440 | assert len(w) == 1 | |
|
436 | 441 | |
|
437 | 442 | |
|
438 | 443 | def test_repr_mime(): |
@@ -458,19 +463,22 b' def test_repr_mime():' | |||
|
458 | 463 | obj = HasReprMime() |
|
459 | 464 | d, md = f.format(obj) |
|
460 | 465 | html_f.enabled = save_enabled |
|
461 | ||
|
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, {}) | |
|
468 | 466 | |
|
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') | |
|
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 == {} | |
|
473 | 475 | |
|
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" | |
|
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 |
|
|
|
518 |
|
|
|
519 |
|
|
|
520 |
|
|
|
521 |
|
|
|
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 |
|
|
|
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"), ( |
|
|
259 |
nt.assert_equal(cc("for a in range(5):"), ( |
|
|
260 |
nt.assert_equal(cc("for a in range(5):\n if a > 0:"), ( |
|
|
261 |
nt.assert_equal(cc("raise = 2"), ( |
|
|
262 |
nt.assert_equal(cc("a = [1,\n2,"), ( |
|
|
263 |
nt.assert_equal(cc(")"), ( |
|
|
264 |
nt.assert_equal(cc("\\\r\n"), ( |
|
|
265 |
nt.assert_equal(cc("a = '''\n hi"), ( |
|
|
266 |
nt.assert_equal(cc("def a():\n x=1\n global x"), ( |
|
|
267 |
nt.assert_equal(cc("a \\ "), ( |
|
|
268 |
nt.assert_equal(cc("1\\\n+2"), ( |
|
|
269 |
nt.assert_equal(cc("exit"), ( |
|
|
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 |
|
|
|
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 |
|
|
|
44 |
|
|
|
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 [ |
|
|
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 |
|
|
|
88 |
|
|
|
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 |
|
|
|
116 |
|
|
|
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 |
|
|
|
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 |
|
|
|
35 |
|
|
|
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, |
@@ -87,123 +87,133 b' ZeroDivisionError: ...' | |||
|
87 | 87 | |
|
88 | 88 | def doctest_tb_verbose(): |
|
89 | 89 | """ |
|
90 | In [5]: xmode verbose | |
|
91 | Exception reporting mode: Verbose | |
|
92 | ||
|
93 | In [6]: run simpleerr.py | |
|
94 | --------------------------------------------------------------------------- | |
|
95 | ZeroDivisionError Traceback (most recent call last) | |
|
96 | <BLANKLINE> | |
|
97 | ... in <module> | |
|
98 | 29 except IndexError: | |
|
99 | 30 mode = 'div' | |
|
100 | ---> 32 bar(mode) | |
|
101 | mode = 'div' | |
|
102 | <BLANKLINE> | |
|
103 | ... in bar(mode='div') | |
|
104 | 14 "bar" | |
|
105 | 15 if mode=='div': | |
|
106 | ---> 16 div0() | |
|
107 | 17 elif mode=='exit': | |
|
108 | 18 try: | |
|
109 | <BLANKLINE> | |
|
110 | ... in div0() | |
|
111 | 6 x = 1 | |
|
112 | 7 y = 0 | |
|
113 | ----> 8 x/y | |
|
114 | x = 1 | |
|
115 | y = 0 | |
|
116 | <BLANKLINE> | |
|
117 | ZeroDivisionError: ... | |
|
118 | """ | |
|
119 | ||
|
120 | def doctest_tb_sysexit(): | |
|
90 | In [5]: xmode verbose | |
|
91 | Exception reporting mode: Verbose | |
|
92 | ||
|
93 | In [6]: run simpleerr.py | |
|
94 | --------------------------------------------------------------------------- | |
|
95 | ZeroDivisionError Traceback (most recent call last) | |
|
96 | <BLANKLINE> | |
|
97 | ... in <module> | |
|
98 | 29 except IndexError: | |
|
99 | 30 mode = 'div' | |
|
100 | ---> 32 bar(mode) | |
|
101 | mode = 'div' | |
|
102 | <BLANKLINE> | |
|
103 | ... in bar(mode='div') | |
|
104 | 14 "bar" | |
|
105 | 15 if mode=='div': | |
|
106 | ---> 16 div0() | |
|
107 | 17 elif mode=='exit': | |
|
108 | 18 try: | |
|
109 | <BLANKLINE> | |
|
110 | ... in div0() | |
|
111 | 6 x = 1 | |
|
112 | 7 y = 0 | |
|
113 | ----> 8 x/y | |
|
114 | x = 1 | |
|
115 | y = 0 | |
|
116 | <BLANKLINE> | |
|
117 | ZeroDivisionError: ... | |
|
121 | 118 |
|
|
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 | 119 | |
|
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 | 120 | |
|
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 | ||
|
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 |
|
|
|
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 |
|
|
208 | 218 | print "hello" |
|
209 | 219 |
|
@@ -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[ |
|
|
229 |
|
|
|
230 |
del ip.db[ |
|
|
231 |
assert |
|
|
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,8 +12,8 b' def test_logstart_inaccessible_file():' | |||
|
12 | 12 | except IOError: |
|
13 | 13 | pass |
|
14 | 14 | else: |
|
15 |
|
|
|
16 | ||
|
15 | assert False # The try block should never pass. | |
|
16 | ||
|
17 | 17 | try: |
|
18 | 18 | _ip.run_cell("a=1") # Check it doesn't try to log this |
|
19 | 19 | finally: |
@@ -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 |
|
|
82 | ||
|
83 | assert_equal(magic_foo2.__doc__, '::\n\n %foo2\n\n A docstring.\n') | |
|
84 |
assert |
|
|
85 | assert_equal(real_name(magic_foo2), 'foo2') | |
|
86 | assert_equal(magic_foo2(None, ''), argparse.Namespace()) | |
|
87 |
assert |
|
|
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 |
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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= |
|
|
171 |
|
|
|
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 |
|
|
|
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 |
|
|
|
181 |
|
|
|
182 |
|
|
|
183 |
|
|
|
184 |
nt.assert_true(i[ |
|
|
185 |
|
|
|
186 |
|
|
|
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[ |
|
|
190 |
|
|
|
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 |
|
|
|
196 |
|
|
|
197 |
|
|
|
198 |
|
|
|
199 |
|
|
|
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, |
|
|
203 |
|
|
|
204 |
|
|
|
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= |
|
|
235 |
|
|
|
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 |
|
|
|
257 |
|
|
|
258 |
|
|
|
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[ |
|
|
303 |
|
|
|
304 |
|
|
|
305 |
ip.object_inspect( |
|
|
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[ |
|
|
308 |
|
|
|
309 |
|
|
|
310 |
ip.object_inspect( |
|
|
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 |
|
|
|
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 |
|
|
|
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+ |
|
|
36 |
|
|
|
37 |
del ip.magics_manager.magics[ |
|
|
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+ |
|
|
43 |
|
|
|
44 |
del ip.magics_manager.magics[ |
|
|
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 |
|
|
|
56 |
for t in [ |
|
|
57 |
|
|
|
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 |
|
|
|
70 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 = |
|
|
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') |
@@ -160,7 +160,7 b' def _format_traceback_lines(lines, Colors, has_colors, lvals):' | |||
|
160 | 160 | else: |
|
161 | 161 | num = '%*s' % (numbers_width, lineno) |
|
162 | 162 | start_color = Colors.lineno |
|
163 | ||
|
163 | ||
|
164 | 164 | line = '%s%s%s %s' % (start_color, num, Colors.Normal, line) |
|
165 | 165 | |
|
166 | 166 | res.append(line) |
@@ -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): |
@@ -300,7 +325,7 b' class ListTB(TBTools):' | |||
|
300 | 325 | |
|
301 | 326 | Calling requires 3 arguments: (etype, evalue, elist) |
|
302 | 327 | as would be obtained by:: |
|
303 | ||
|
328 | ||
|
304 | 329 | etype, evalue, tb = sys.exc_info() |
|
305 | 330 | if tb: |
|
306 | 331 | elist = traceback.extract_tb(tb) |
@@ -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 = |
|
|
418 |
|
|
|
419 |
|
|
|
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 = |
|
|
427 |
|
|
|
428 |
|
|
|
429 |
|
|
|
430 | Colors.nameEm, name, Colors.normalEm, | |
|
431 |
|
|
|
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 = |
|
|
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 |
|
|
|
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: | |
|
638 | lvals_list.append(tpl_name_val % (var.name, repr(var.value))) | |
|
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: | |
|
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 = |
|
|
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. |
@@ -94,21 +99,21 b' Some of the known remaining caveats are:' | |||
|
94 | 99 | |
|
95 | 100 | skip_doctest = True |
|
96 | 101 | |
|
97 | #----------------------------------------------------------------------------- | |
|
102 | # ----------------------------------------------------------------------------- | |
|
98 | 103 | # Copyright (C) 2000 Thomas Heller |
|
99 | 104 | # Copyright (C) 2008 Pauli Virtanen <pav@iki.fi> |
|
100 | 105 | # Copyright (C) 2012 The IPython Development Team |
|
101 | 106 | # |
|
102 | 107 | # Distributed under the terms of the BSD License. The full license is in |
|
103 | 108 | # the file COPYING, distributed as part of this software. |
|
104 | #----------------------------------------------------------------------------- | |
|
109 | # ----------------------------------------------------------------------------- | |
|
105 | 110 | # |
|
106 | 111 | # This IPython module is written by Pauli Virtanen, based on the autoreload |
|
107 | 112 | # code by Thomas Heller. |
|
108 | 113 | |
|
109 | #----------------------------------------------------------------------------- | |
|
114 | # ----------------------------------------------------------------------------- | |
|
110 | 115 | # Imports |
|
111 | #----------------------------------------------------------------------------- | |
|
116 | # ----------------------------------------------------------------------------- | |
|
112 | 117 | |
|
113 | 118 | import os |
|
114 | 119 | import sys |
@@ -120,18 +125,22 b' from importlib import import_module' | |||
|
120 | 125 | from importlib.util import source_from_cache |
|
121 | 126 | from imp import reload |
|
122 | 127 | |
|
123 | #------------------------------------------------------------------------------ | |
|
128 | # ------------------------------------------------------------------------------ | |
|
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( |
|
|
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, |
|
|
194 | if not hasattr(module, "__file__") or module.__file__ is None: | |
|
185 | 195 | return None, None |
|
186 | 196 | |
|
187 |
if getattr(module, |
|
|
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() == |
|
|
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: |
|
245 |
|
|
|
255 | if self.autoload_obj: | |
|
256 | superreload(m, reload, self.old_objects, self.shell) | |
|
257 | else: | |
|
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 | |
|
253 | #------------------------------------------------------------------------------ | |
|
270 | ||
|
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): |
@@ -272,7 +296,7 b' def update_instances(old, new):' | |||
|
272 | 296 | """Use garbage collector to find all instances that refer to the old |
|
273 | 297 | class definition and update their __class__ to point to the new class |
|
274 | 298 | definition""" |
|
275 | ||
|
299 | ||
|
276 | 300 | refs = gc.get_referrers(old) |
|
277 | 301 | |
|
278 | 302 | for ref in refs: |
@@ -299,19 +323,20 b' def update_class(old, new):' | |||
|
299 | 323 | pass |
|
300 | 324 | continue |
|
301 | 325 | |
|
302 |
if update_generic(old_obj, new_obj): |
|
|
326 | if update_generic(old_obj, new_obj): | |
|
327 | continue | |
|
303 | 328 | |
|
304 | 329 | try: |
|
305 | 330 | setattr(old, key, getattr(new, key)) |
|
306 | 331 | except (AttributeError, TypeError): |
|
307 | pass # skip non-writable attributes | |
|
332 | pass # skip non-writable attributes | |
|
308 | 333 | |
|
309 | 334 | for key in list(new.__dict__.keys()): |
|
310 | 335 | if key not in list(old.__dict__.keys()): |
|
311 | 336 | try: |
|
312 | 337 | setattr(old, key, getattr(new, key)) |
|
313 | 338 | except (AttributeError, TypeError): |
|
314 | pass # skip non-writable attributes | |
|
339 | pass # skip non-writable attributes | |
|
315 | 340 | |
|
316 | 341 | # update all instances of class |
|
317 | 342 | update_instances(old, new) |
@@ -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, |
|
|
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 |
|
|
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__[ |
|
|
389 |
module.__dict__[ |
|
|
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: |
|
|
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: |
|
|
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 | |
|
419 | #------------------------------------------------------------------------------ | |
|
486 | ||
|
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( |
|
|
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 == |
|
|
550 | elif parameter_s == "0": | |
|
481 | 551 | self._reloader.enabled = False |
|
482 |
elif parameter_s == |
|
|
552 | elif parameter_s == "1": | |
|
483 | 553 | self._reloader.check_all = False |
|
484 | 554 | self._reloader.enabled = True |
|
485 |
elif parameter_s == |
|
|
555 | elif parameter_s == "2": | |
|
556 | self._reloader.check_all = True | |
|
557 | self._reloader.enabled = True | |
|
558 | self._reloader.enabled = True | |
|
559 | elif parameter_s == "3": | |
|
486 | 560 | self._reloader.check_all = True |
|
487 | 561 | self._reloader.enabled = True |
|
562 | self._reloader.autoload_obj = True | |
|
488 | 563 | |
|
489 | 564 | @line_magic |
|
490 |
def aimport(self, parameter_s= |
|
|
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" % |
|
|
515 |
stream.write("\nModules to skip:\n%s\n" % |
|
|
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 |
|
|
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( |
|
|
550 |
ip.events.register( |
|
|
623 | ip.events.register("pre_run_cell", auto_reload.pre_run_cell) | |
|
624 | ip.events.register("post_execute", auto_reload.post_execute_hook) |
@@ -1,16 +1,16 b'' | |||
|
1 | 1 | """Tests for autoreload extension. |
|
2 | 2 | """ |
|
3 | #----------------------------------------------------------------------------- | |
|
3 | # ----------------------------------------------------------------------------- | |
|
4 | 4 | # Copyright (c) 2012 IPython Development Team. |
|
5 | 5 | # |
|
6 | 6 | # Distributed under the terms of the Modified BSD License. |
|
7 | 7 | # |
|
8 | 8 | # The full license is in the file COPYING.txt, distributed with this software. |
|
9 | #----------------------------------------------------------------------------- | |
|
9 | # ----------------------------------------------------------------------------- | |
|
10 | 10 | |
|
11 | #----------------------------------------------------------------------------- | |
|
11 | # ----------------------------------------------------------------------------- | |
|
12 | 12 | # Imports |
|
13 | #----------------------------------------------------------------------------- | |
|
13 | # ----------------------------------------------------------------------------- | |
|
14 | 14 | |
|
15 | 15 | import os |
|
16 | 16 | import sys |
@@ -29,26 +29,26 b' from unittest import TestCase' | |||
|
29 | 29 | from IPython.extensions.autoreload import AutoreloadMagics |
|
30 | 30 | from IPython.core.events import EventManager, pre_run_cell |
|
31 | 31 | |
|
32 | #----------------------------------------------------------------------------- | |
|
32 | # ----------------------------------------------------------------------------- | |
|
33 | 33 | # Test fixture |
|
34 | #----------------------------------------------------------------------------- | |
|
34 | # ----------------------------------------------------------------------------- | |
|
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, { |
|
|
44 | self.events = EventManager(self, {"pre_run_cell", pre_run_cell}) | |
|
45 | 45 | self.auto_magics = AutoreloadMagics(shell=self) |
|
46 |
self.events.register( |
|
|
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( |
|
|
51 | self.events.trigger("pre_run_cell") | |
|
52 | 52 | exec(code, self.user_ns) |
|
53 | 53 | self.auto_magics.post_execute_hook() |
|
54 | 54 | |
@@ -85,7 +85,7 b' class Fixture(TestCase):' | |||
|
85 | 85 | self.shell = None |
|
86 | 86 | |
|
87 | 87 | def get_module(self): |
|
88 | module_name = "tmpmod_" + "".join(random.sample(self.filename_chars,20)) | |
|
88 | module_name = "tmpmod_" + "".join(random.sample(self.filename_chars, 20)) | |
|
89 | 89 | if module_name in sys.modules: |
|
90 | 90 | del sys.modules[module_name] |
|
91 | 91 | file_name = os.path.join(self.test_dir, module_name + ".py") |
@@ -111,19 +111,21 b' class Fixture(TestCase):' | |||
|
111 | 111 | time.sleep(1.05) |
|
112 | 112 | |
|
113 | 113 | # Write |
|
114 |
with open(filename, |
|
|
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, |
|
|
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 | # ----------------------------------------------------------------------------- | |
|
128 | ||
|
127 | 129 | |
|
128 | 130 | def pickle_get_current_class(obj): |
|
129 | 131 | """ |
@@ -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( |
|
|
144 | mod_name, mod_fn = self.new_module( | |
|
145 | textwrap.dedent( | |
|
146 | """ | |
|
143 | 147 |
|
|
144 | 148 |
|
|
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( |
|
|
156 | self.write_file( | |
|
157 | mod_fn, | |
|
158 | textwrap.dedent( | |
|
159 | """ | |
|
151 | 160 |
|
|
152 | 161 |
|
|
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( |
|
|
211 | mod_name, mod_fn = self.new_module( | |
|
212 | textwrap.dedent( | |
|
213 | """ | |
|
199 | 214 |
|
|
200 | 215 |
|
|
201 | 216 |
|
@@ -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 { |
|
|
245 |
self.shell.run_code("{object_name}.power(5)" |
|
|
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()" |
|
|
262 | self.shell.run_code(f"{object_name}.cube()") | |
|
248 | 263 | with nt.assert_raises(AttributeError): |
|
249 |
self.shell.run_code("{object_name}.square()" |
|
|
250 |
self.shell.run_code("{object_name}.b" |
|
|
251 |
self.shell.run_code("{object_name}.a" |
|
|
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" |
|
|
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 |
|
|
263 | 362 |
|
|
264 | 363 |
|
@@ -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 |
|
|
283 | 382 |
|
|
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 |
|
|
|
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( |
|
|
440 | self.write_file( | |
|
441 | mod_fn, | |
|
442 | """ | |
|
340 | 443 |
|
|
341 |
|
|
|
444 | """, | |
|
445 | ) | |
|
342 | 446 | |
|
343 | with tt.AssertPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'): | |
|
344 | self.shell.run_code("pass") # trigger reload | |
|
345 | with tt.AssertNotPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'): | |
|
346 |
self.shell.run_code("pass") # trigg |
|
|
447 | with tt.AssertPrints( | |
|
448 | ("[autoreload of %s failed:" % mod_name), channel="stderr" | |
|
449 | ): | |
|
450 | self.shell.run_code("pass") # trigger reload | |
|
451 | with tt.AssertNotPrints( | |
|
452 | ("[autoreload of %s failed:" % mod_name), channel="stderr" | |
|
453 | ): | |
|
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( |
|
|
460 | self.write_file( | |
|
461 | mod_fn, | |
|
462 | """ | |
|
353 | 463 |
|
|
354 | 464 |
|
|
355 | 465 |
|
@@ -367,30 +477,31 b' class Baz(object):' | |||
|
367 | 477 |
|
|
368 | 478 |
|
|
369 | 479 |
|
|
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, |
|
|
485 | nt.assert_false(hasattr(mod, "z")) | |
|
375 | 486 | |
|
376 | nt.assert_equal(old_foo(0), 4) # superreload magic! | |
|
487 | nt.assert_equal(old_foo(0), 4) # superreload magic! | |
|
377 | 488 | nt.assert_equal(mod.foo(0), 4) |
|
378 | 489 | |
|
379 | 490 | obj = mod.Baz(9) |
|
380 | nt.assert_equal(old_obj.bar(1), 11) # superreload magic! | |
|
491 | nt.assert_equal(old_obj.bar(1), 11) # superreload magic! | |
|
381 | 492 | nt.assert_equal(obj.bar(1), 11) |
|
382 | 493 | |
|
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, |
|
|
387 |
nt.assert_false(hasattr(obj, |
|
|
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) |
|
391 | 502 | nt.assert_equal(obj2.foo(), 2) |
|
392 | 503 | |
|
393 | self.shell.run_code("pass") # trigger reload | |
|
504 | self.shell.run_code("pass") # trigger reload | |
|
394 | 505 | check_module_contents() |
|
395 | 506 | |
|
396 | 507 | # |
@@ -398,7 +509,7 b' class Bar: # old-style class' | |||
|
398 | 509 | # |
|
399 | 510 | os.unlink(mod_fn) |
|
400 | 511 | |
|
401 | self.shell.run_code("pass") # trigger reload | |
|
512 | self.shell.run_code("pass") # trigger reload | |
|
402 | 513 | check_module_contents() |
|
403 | 514 | |
|
404 | 515 | # |
@@ -408,19 +519,21 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( |
|
|
529 | self.write_file( | |
|
530 | mod_fn, | |
|
531 | """ | |
|
420 | 532 |
|
|
421 |
|
|
|
533 | """, | |
|
534 | ) | |
|
422 | 535 | |
|
423 | self.shell.run_code("pass") # trigger reload | |
|
536 | self.shell.run_code("pass") # trigger reload | |
|
424 | 537 | self.shell.run_code("pass") |
|
425 | 538 | check_module_contents() |
|
426 | 539 | |
@@ -432,7 +545,7 b' x = -99' | |||
|
432 | 545 | else: |
|
433 | 546 | self.shell.magic_autoreload("") |
|
434 | 547 | |
|
435 | self.shell.run_code("pass") # trigger reload | |
|
548 | self.shell.run_code("pass") # trigger reload | |
|
436 | 549 | nt.assert_equal(mod.x, -99) |
|
437 | 550 | |
|
438 | 551 | def test_smoketest_aimport(self): |
@@ -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 ( |
|
|
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 [ |
|
|
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,26 +10,41 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', | |
|
32 | } | |
|
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", | |
|
47 | } | |
|
33 | 48 | |
|
34 | 49 | |
|
35 | 50 | class ImportDenier(object): |
@@ -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( |
|
|
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', |
|
|
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 |
|
|
186 | return current in [QT_API_PYQT6, None] | |
|
178 | 187 | else: |
|
179 | 188 | return current in [api, None] |
|
180 | 189 | |
@@ -224,7 +233,7 b' def import_pyqt5():' | |||
|
224 | 233 | """ |
|
225 | 234 | |
|
226 | 235 | from PyQt5 import QtCore, QtSvg, QtWidgets, QtGui |
|
227 | ||
|
236 | ||
|
228 | 237 | # Alias PyQt-specific functions for PySide compatibility. |
|
229 | 238 | QtCore.Signal = QtCore.pyqtSignal |
|
230 | 239 | QtCore.Slot = QtCore.pyqtSlot |
@@ -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,13 +339,19 b' def load_qt(api_options):' | |||
|
291 | 339 | an incompatible library has already been installed) |
|
292 | 340 | """ |
|
293 | 341 | loaders = { |
|
294 | QT_API_PYSIDE2: import_pyside2, | |
|
295 |
|
|
|
296 |
|
|
|
297 | QT_API_PYQT5: import_pyqt5, | |
|
298 |
|
|
|
299 |
|
|
|
300 |
|
|
|
342 | # Qt6 | |
|
343 | QT_API_PYQT6: import_pyqt6, | |
|
344 | QT_API_PYSIDE6: import_pyside6, | |
|
345 | # Qt5 | |
|
346 | QT_API_PYQT5: import_pyqt5, | |
|
347 | QT_API_PYSIDE2: import_pyside2, | |
|
348 | # Qt4 | |
|
349 | QT_API_PYSIDE: import_pyside, | |
|
350 | QT_API_PYQT: import_pyqt4, | |
|
351 | QT_API_PYQTv1: partial(import_pyqt4, version=1), | |
|
352 | # default | |
|
353 | QT_API_PYQT_DEFAULT: import_pyqt6, | |
|
354 | } | |
|
301 | 355 | |
|
302 | 356 | for api in api_options: |
|
303 | 357 | |
@@ -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( |
|
|
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( |
|
|
282 | width=self.width, | |
|
283 | height=self.height, | |
|
284 | params=params) | |
|
287 | return self.iframe.format( | |
|
288 | src=self.src, | |
|
289 | width=self.width, | |
|
290 | height=self.height, | |
|
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,11 +319,14 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): |
|
318 | 331 | # Deferred import |
|
319 | 332 | from urllib.request import urlopen |
@@ -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= |
|
|
49 | cmd = template.format(filename=shlex.quote(filename), line=line) | |
|
51 | 50 | print(">", cmd) |
|
52 |
# |
|
|
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() | |
|
127 | dpi = 120*scale | |
|
128 | mt.to_png(f, s, fontsize=12, dpi=dpi, color=color) | |
|
129 | return f.getvalue() | |
|
126 | prop = font_manager.FontProperties(size=12) | |
|
127 | dpi = 120 * scale | |
|
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 |
|
|
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,18 +46,18 b' def test_result():' | |||
|
49 | 46 | jobs = bg.BackgroundJobManager() |
|
50 | 47 | j = jobs.new(sleeper) |
|
51 | 48 | j.join() |
|
52 |
|
|
|
53 | ||
|
49 | assert j.result["interval"] == t_short | |
|
50 | ||
|
54 | 51 | |
|
55 | 52 | def test_flush(): |
|
56 | 53 | """Test job control""" |
|
57 | 54 | jobs = bg.BackgroundJobManager() |
|
58 | 55 | j = jobs.new(sleeper) |
|
59 | 56 | j.join() |
|
60 |
|
|
|
61 |
|
|
|
57 | assert len(jobs.completed) == 1 | |
|
58 | assert len(jobs.dead) == 0 | |
|
62 | 59 | jobs.flush() |
|
63 |
|
|
|
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 |
|
|
|
72 |
|
|
|
68 | assert len(jobs.completed) == 0 | |
|
69 | assert len(jobs.dead) == 1 | |
|
73 | 70 | jobs.flush() |
|
74 |
|
|
|
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 |
|
|
|
85 |
|
|
|
81 | assert len(jobs.running) == 1 | |
|
82 | assert len(jobs.completed) == 0 | |
|
86 | 83 | j.join() |
|
87 |
|
|
|
88 |
|
|
|
84 | assert len(jobs.running) == 0 | |
|
85 | assert len(jobs.completed) == 1 |
@@ -59,8 +59,9 b' def test_existing_path_FileLink():' | |||
|
59 | 59 | tf = NamedTemporaryFile() |
|
60 | 60 | fl = display.FileLink(tf.name) |
|
61 | 61 | actual = fl._repr_html_() |
|
62 | expected = "<a href='%s' target='_blank'>%s</a><br>" % (tf.name,tf.name) | |
|
63 |
|
|
|
62 | expected = "<a href='%s' target='_blank'>%s</a><br>" % (tf.name, tf.name) | |
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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,8 +146,9 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 |
|
|
|
146 | ||
|
149 | assert actual == expected | |
|
150 | ||
|
151 | ||
|
147 | 152 | def test_existing_path_FileLinks_repr_alt_formatter(): |
|
148 | 153 | """FileLinks: Calling repr() functions as expected w/ alt formatter |
|
149 | 154 | """ |
@@ -159,8 +164,9 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 |
|
|
|
163 | ||
|
167 | assert actual == expected | |
|
168 | ||
|
169 | ||
|
164 | 170 | def test_error_on_file_to_FileLinks(): |
|
165 | 171 | """FileLinks: Raises error when passed file |
|
166 | 172 | """ |
@@ -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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
24 |
args = called[0][ |
|
|
25 |
kwargs = called[0][ |
|
|
26 | ||
|
27 |
|
|
|
28 | ||
|
29 |
if sys.platform.startswith( |
|
|
30 |
expected = [ |
|
|
21 | assert len(called) == 1 | |
|
22 | args = called[0]["args"] | |
|
23 | kwargs = called[0]["kwargs"] | |
|
24 | ||
|
25 | assert kwargs == {"shell": True} | |
|
26 | ||
|
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 |
|
|
|
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) |
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
138 | assert got_output == expected_output | |
|
139 | 139 | |
|
140 | 140 | |
|
141 | 141 | @skip_without('xxlimited') |
@@ -145,22 +145,24 b' def test_pprint_heap_allocated_type():' | |||
|
145 | 145 | """ |
|
146 | 146 | import xxlimited |
|
147 | 147 | output = pretty.pretty(xxlimited.Null) |
|
148 |
|
|
|
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 |
|
|
|
156 | ||
|
156 | assert output == "NoModule" | |
|
157 | ||
|
158 | ||
|
157 | 159 | def test_pprint_break(): |
|
158 | 160 | """ |
|
159 | 161 | Test that p.break_ produces expected output |
|
160 | 162 | """ |
|
161 | 163 | output = pretty.pretty(Breaking()) |
|
162 | 164 | expected = "TG: Breaking(\n ):" |
|
163 |
|
|
|
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 |
|
|
|
173 | assert output == expected | |
|
172 | 174 | |
|
173 | 175 | output = pretty.pretty([[BreakingRepr()]*2]) |
|
174 | 176 | expected = "[[Breaking(\n ),\n Breaking(\n )]]" |
|
175 |
|
|
|
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 |
|
|
|
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 |
|
|
|
276 | assert p == u | |
|
275 | 277 | p = pretty.pretty([c]) |
|
276 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
464 | assert env_repr == "environ" + dict_indented | |
|
456 | 465 | |
|
457 | 466 | |
|
458 | 467 | def test_function_pretty(): |
@@ -460,8 +469,9 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)>') | |
|
464 | ||
|
472 | ||
|
473 | assert pretty.pretty(posixpath.join) == "<function posixpath.join(a, *p)>" | |
|
474 | ||
|
465 | 475 | # custom function |
|
466 | 476 | def meaning_of_life(question=None): |
|
467 | 477 | if question: |
@@ -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 |
|
|
|
502 | assert pretty.pretty(MySet()) == "mine" |
@@ -1,26 +1,27 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( |
|
|
8 |
algorithm, salt, hashed = p.split( |
|
|
9 |
|
|
|
10 |
|
|
|
11 |
|
|
|
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( |
|
|
15 |
|
|
|
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 |
|
|
|
20 |
|
|
|
21 |
|
|
|
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 |
|
25 | 26 | phash = u'sha1:23862bc21dd3:7a415a95ae4580582e314072143d9c382c491e4f' |
|
26 | assert passwd_check(phash, u"łe¶ŧ←↓→") No newline at end of file | |
|
27 | assert passwd_check(phash, u"łe¶ŧ←↓→") |
@@ -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 |
s |
|
|
580 | sys.stdout.write(('-' * 76) + '\n') | |
|
581 | s=warnings.formatwarning(w.message, w.category, | |
|
582 | w.filename, w.lineno, w.line) | |
|
583 |
|
|
|
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 |
|
|
|
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 = |
|
|
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 |
|
|
|
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 == |
|
|
46 |
os.environ[ |
|
|
47 |
gui_mod = |
|
|
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( |
|
|
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( |
|
|
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( |
|
|
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 |
|
|
83 | _exec(event_loop) | |
|
69 | 84 | finally: |
|
70 | 85 | notifier.setEnabled(False) |
@@ -49,18 +49,16 b' def process_handler(cmd, callback, stderr=subprocess.PIPE):' | |||
|
49 | 49 | Parameters |
|
50 | 50 | ---------- |
|
51 | 51 | cmd : str or list |
|
52 | A command to be executed by the system, using :class:`subprocess.Popen`. | |
|
53 | If a string is passed, it will be run in the system shell. If a list is | |
|
54 | passed, it will be used directly as arguments. | |
|
55 | ||
|
52 | A command to be executed by the system, using :class:`subprocess.Popen`. | |
|
53 | If a string is passed, it will be run in the system shell. If a list is | |
|
54 | passed, it will be used directly as arguments. | |
|
56 | 55 | callback : callable |
|
57 | A one-argument function that will be called with the Popen object. | |
|
58 | ||
|
56 | A one-argument function that will be called with the Popen object. | |
|
59 | 57 | stderr : file descriptor number, optional |
|
60 | By default this is set to ``subprocess.PIPE``, but you can also pass the | |
|
61 | value ``subprocess.STDOUT`` to force the subprocess' stderr to go into | |
|
62 | the same file descriptor as its stdout. This is useful to read stdout | |
|
63 | and stderr combined in the order they are generated. | |
|
58 | By default this is set to ``subprocess.PIPE``, but you can also pass the | |
|
59 | value ``subprocess.STDOUT`` to force the subprocess' stderr to go into | |
|
60 | the same file descriptor as its stdout. This is useful to read stdout | |
|
61 | and stderr combined in the order they are generated. | |
|
64 | 62 | |
|
65 | 63 | Returns |
|
66 | 64 | ------- |
@@ -117,12 +115,12 b' def getoutput(cmd):' | |||
|
117 | 115 | Parameters |
|
118 | 116 | ---------- |
|
119 | 117 | cmd : str or list |
|
120 | A command to be executed in the system shell. | |
|
118 | A command to be executed in the system shell. | |
|
121 | 119 | |
|
122 | 120 | Returns |
|
123 | 121 | ------- |
|
124 | 122 | output : str |
|
125 | A string containing the combination of stdout and stderr from the | |
|
123 | A string containing the combination of stdout and stderr from the | |
|
126 | 124 | subprocess, in whatever order the subprocess originally wrote to its |
|
127 | 125 | file descriptors (so the order of the information in this string is the |
|
128 | 126 | correct order as would be seen if running the command in a terminal). |
@@ -141,7 +139,7 b' def getoutputerror(cmd):' | |||
|
141 | 139 | Parameters |
|
142 | 140 | ---------- |
|
143 | 141 | cmd : str or list |
|
144 | A command to be executed in the system shell. | |
|
142 | A command to be executed in the system shell. | |
|
145 | 143 | |
|
146 | 144 | Returns |
|
147 | 145 | ------- |
@@ -159,7 +157,7 b' def get_output_error_code(cmd):' | |||
|
159 | 157 | Parameters |
|
160 | 158 | ---------- |
|
161 | 159 | cmd : str or list |
|
162 | A command to be executed in the system shell. | |
|
160 | A command to be executed in the system shell. | |
|
163 | 161 | |
|
164 | 162 | Returns |
|
165 | 163 | ------- |
@@ -82,12 +82,12 b' class ProcessHandler(object):' | |||
|
82 | 82 | Parameters |
|
83 | 83 | ---------- |
|
84 | 84 | cmd : str |
|
85 | A command to be executed in the system shell. | |
|
85 | A command to be executed in the system shell. | |
|
86 | 86 | |
|
87 | 87 | Returns |
|
88 | 88 | ------- |
|
89 | 89 | output : str |
|
90 | A string containing the combination of stdout and stderr from the | |
|
90 | A string containing the combination of stdout and stderr from the | |
|
91 | 91 | subprocess, in whatever order the subprocess originally wrote to its |
|
92 | 92 | file descriptors (so the order of the information in this string is the |
|
93 | 93 | correct order as would be seen if running the command in a terminal). |
@@ -103,12 +103,12 b' class ProcessHandler(object):' | |||
|
103 | 103 | Parameters |
|
104 | 104 | ---------- |
|
105 | 105 | cmd : str |
|
106 | A command to be executed in the system shell. | |
|
106 | A command to be executed in the system shell. | |
|
107 | 107 | |
|
108 | 108 | Returns |
|
109 | 109 | ------- |
|
110 | 110 | output : str |
|
111 | A string containing the combination of stdout and stderr from the | |
|
111 | A string containing the combination of stdout and stderr from the | |
|
112 | 112 | subprocess, in whatever order the subprocess originally wrote to its |
|
113 | 113 | file descriptors (so the order of the information in this string is the |
|
114 | 114 | correct order as would be seen if running the command in a terminal). |
@@ -124,7 +124,7 b' class ProcessHandler(object):' | |||
|
124 | 124 | Parameters |
|
125 | 125 | ---------- |
|
126 | 126 | cmd : str |
|
127 | A command to be executed in the system shell. | |
|
127 | A command to be executed in the system shell. | |
|
128 | 128 | |
|
129 | 129 | Returns |
|
130 | 130 | ------- |
@@ -128,7 +128,7 b' def system(cmd):' | |||
|
128 | 128 | Parameters |
|
129 | 129 | ---------- |
|
130 | 130 | cmd : str or list |
|
131 | A command to be executed in the system shell. | |
|
131 | A command to be executed in the system shell. | |
|
132 | 132 | |
|
133 | 133 | Returns |
|
134 | 134 | ------- |
@@ -152,7 +152,7 b' def getoutput(cmd):' | |||
|
152 | 152 | Parameters |
|
153 | 153 | ---------- |
|
154 | 154 | cmd : str or list |
|
155 | A command to be executed in the system shell. | |
|
155 | A command to be executed in the system shell. | |
|
156 | 156 | |
|
157 | 157 | Returns |
|
158 | 158 | ------- |
@@ -181,7 +181,7 b' try:' | |||
|
181 | 181 | |
|
182 | 182 | This is a special version for windows that use a ctypes call to CommandLineToArgvW |
|
183 | 183 | to do the argv splitting. The posix parameter is ignored. |
|
184 | ||
|
184 | ||
|
185 | 185 | If strict=False, process_common.arg_split(...strict=False) is used instead. |
|
186 | 186 | """ |
|
187 | 187 | #CommandLineToArgvW returns path to executable if called with empty string. |
@@ -551,13 +551,13 b' def system(cmd):' | |||
|
551 | 551 | Parameters |
|
552 | 552 | ---------- |
|
553 | 553 | cmd : str |
|
554 | A command to be executed in the system shell. | |
|
554 | A command to be executed in the system shell. | |
|
555 | 555 | |
|
556 | 556 | Returns |
|
557 | 557 | ------- |
|
558 | 558 | None : we explicitly do NOT return the subprocess status code, as this |
|
559 | 559 | utility is meant to be used extensively in IPython, where any return value |
|
560 | would trigger :func:`sys.displayhook` calls. | |
|
560 | would trigger : func:`sys.displayhook` calls. | |
|
561 | 561 | """ |
|
562 | 562 | with AvoidUNCPath() as path: |
|
563 | 563 | if path is not None: |
@@ -50,7 +50,7 b' def flag_calls(func):' | |||
|
50 | 50 | |
|
51 | 51 | def undoc(func): |
|
52 | 52 | """Mark a function or class as undocumented. |
|
53 | ||
|
53 | ||
|
54 | 54 | This is found by inspecting the AST, so for now it must be used directly |
|
55 | 55 | as @undoc, not as e.g. @decorators.undoc |
|
56 | 56 | """ |
@@ -37,10 +37,10 b' def get_stream_enc(stream, default=None):' | |||
|
37 | 37 | # won't need to make changes all over IPython. |
|
38 | 38 | def getdefaultencoding(prefer_stream=True): |
|
39 | 39 | """Return IPython's guess for the default encoding for bytes as text. |
|
40 | ||
|
40 | ||
|
41 | 41 | If prefer_stream is True (default), asks for stdin.encoding first, |
|
42 | 42 | to match the calling Terminal, but that is often None for subprocesses. |
|
43 | ||
|
43 | ||
|
44 | 44 | Then fall back on locale.getpreferredencoding(), |
|
45 | 45 | which should be a sensible platform default (that respects LANG environment), |
|
46 | 46 | and finally to sys.getdefaultencoding() which is the most conservative option, |
@@ -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. |
@@ -16,12 +16,12 b' def import_item(name):' | |||
|
16 | 16 | Parameters |
|
17 | 17 | ---------- |
|
18 | 18 | name : string |
|
19 | The fully qualified name of the module/package being imported. | |
|
19 | The fully qualified name of the module/package being imported. | |
|
20 | 20 | |
|
21 | 21 | Returns |
|
22 | 22 | ------- |
|
23 | 23 | mod : module object |
|
24 | The module that was imported. | |
|
24 | The module that was imported. | |
|
25 | 25 | """ |
|
26 | 26 | |
|
27 | 27 | parts = name.rsplit('.', 1) |
@@ -113,11 +113,9 b' class Tee(object):' | |||
|
113 | 113 | Parameters |
|
114 | 114 | ---------- |
|
115 | 115 | file_or_name : filename or open filehandle (writable) |
|
116 | File that will be duplicated | |
|
117 | ||
|
116 | File that will be duplicated | |
|
118 | 117 | mode : optional, valid mode for open(). |
|
119 | If a filename was give, open with this mode. | |
|
120 | ||
|
118 | If a filename was give, open with this mode. | |
|
121 | 119 | channel : str, one of ['stdout', 'stderr'] |
|
122 | 120 | """ |
|
123 | 121 | if channel not in ['stdout', 'stderr']: |
@@ -195,15 +193,14 b" def temp_pyfile(src, ext='.py'):" | |||
|
195 | 193 | Parameters |
|
196 | 194 | ---------- |
|
197 | 195 | src : string or list of strings (no need for ending newlines if list) |
|
198 | Source code to be written to the file. | |
|
199 | ||
|
196 | Source code to be written to the file. | |
|
200 | 197 | ext : optional, string |
|
201 | Extension for the generated file. | |
|
198 | Extension for the generated file. | |
|
202 | 199 | |
|
203 | 200 | Returns |
|
204 | 201 | ------- |
|
205 | 202 | (filename, open filehandle) |
|
206 | It is the caller's responsibility to close the open file and unlink it. | |
|
203 | It is the caller's responsibility to close the open file and unlink it. | |
|
207 | 204 | """ |
|
208 | 205 | fname = tempfile.mkstemp(ext)[1] |
|
209 | 206 | with open(Path(fname), "w") as f: |
@@ -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) |
@@ -43,7 +43,7 b' def find_mod(module_name):' | |||
|
43 | 43 | """ |
|
44 | 44 | Find module `module_name` on sys.path, and return the path to module `module_name`. |
|
45 | 45 | |
|
46 |
- If `module_name` refers to a module directory, then return path to __init__ file. |
|
|
46 | - If `module_name` refers to a module directory, then return path to __init__ file. | |
|
47 | 47 | - If `module_name` is a directory without an __init__file, return None. |
|
48 | 48 | - If module is missing or does not have a `.py` or `.pyw` extension, return None. |
|
49 | 49 | - Note that we are not interested in running bytecode. |
@@ -52,7 +52,7 b' def find_mod(module_name):' | |||
|
52 | 52 | Parameters |
|
53 | 53 | ---------- |
|
54 | 54 | module_name : str |
|
55 | ||
|
55 | ||
|
56 | 56 | Returns |
|
57 | 57 | ------- |
|
58 | 58 | module_path : str |
@@ -60,15 +60,15 b' def strip_encoding_cookie(filelike):' | |||
|
60 | 60 | |
|
61 | 61 | def read_py_file(filename, skip_encoding_cookie=True): |
|
62 | 62 | """Read a Python file, using the encoding declared inside the file. |
|
63 | ||
|
63 | ||
|
64 | 64 | Parameters |
|
65 | 65 | ---------- |
|
66 | 66 | filename : str |
|
67 | The path to the file to read. | |
|
67 | The path to the file to read. | |
|
68 | 68 | skip_encoding_cookie : bool |
|
69 | If True (the default), and the encoding declaration is found in the first | |
|
70 | two lines, that line will be excluded from the output. | |
|
71 | ||
|
69 | If True (the default), and the encoding declaration is found in the first | |
|
70 | two lines, that line will be excluded from the output. | |
|
71 | ||
|
72 | 72 | Returns |
|
73 | 73 | ------- |
|
74 | 74 | A unicode string containing the contents of the file. |
@@ -82,18 +82,18 b' def read_py_file(filename, skip_encoding_cookie=True):' | |||
|
82 | 82 | |
|
83 | 83 | def read_py_url(url, errors='replace', skip_encoding_cookie=True): |
|
84 | 84 | """Read a Python file from a URL, using the encoding declared inside the file. |
|
85 | ||
|
85 | ||
|
86 | 86 | Parameters |
|
87 | 87 | ---------- |
|
88 | 88 | url : str |
|
89 | The URL from which to fetch the file. | |
|
89 | The URL from which to fetch the file. | |
|
90 | 90 | errors : str |
|
91 | How to handle decoding errors in the file. Options are the same as for | |
|
92 | bytes.decode(), but here 'replace' is the default. | |
|
91 | How to handle decoding errors in the file. Options are the same as for | |
|
92 | bytes.decode(), but here 'replace' is the default. | |
|
93 | 93 | skip_encoding_cookie : bool |
|
94 | If True (the default), and the encoding declaration is found in the first | |
|
95 | two lines, that line will be excluded from the output. | |
|
96 | ||
|
94 | If True (the default), and the encoding declaration is found in the first | |
|
95 | two lines, that line will be excluded from the output. | |
|
96 | ||
|
97 | 97 | Returns |
|
98 | 98 | ------- |
|
99 | 99 | A unicode string containing the contents of the 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 |
@@ -40,15 +40,15 b' def pkg_commit_hash(pkg_path):' | |||
|
40 | 40 | Parameters |
|
41 | 41 | ---------- |
|
42 | 42 | pkg_path : str |
|
43 | directory containing package | |
|
44 | only used for getting commit from active repo | |
|
43 | directory containing package | |
|
44 | only used for getting commit from active repo | |
|
45 | 45 | |
|
46 | 46 | Returns |
|
47 | 47 | ------- |
|
48 | 48 | hash_from : str |
|
49 | Where we got the hash from - description | |
|
49 | Where we got the hash from - description | |
|
50 | 50 | hash_str : str |
|
51 | short form of hash | |
|
51 | short form of hash | |
|
52 | 52 | """ |
|
53 | 53 | # Try and get commit from written commit text file |
|
54 | 54 | if _sysinfo.commit: |
@@ -71,12 +71,12 b' def pkg_info(pkg_path):' | |||
|
71 | 71 | Parameters |
|
72 | 72 | ---------- |
|
73 | 73 | pkg_path : str |
|
74 | path containing __init__.py for package | |
|
74 | path containing __init__.py for package | |
|
75 | 75 | |
|
76 | 76 | Returns |
|
77 | 77 | ------- |
|
78 | 78 | context : dict |
|
79 | with named parameters of interest | |
|
79 | with named parameters of interest | |
|
80 | 80 | """ |
|
81 | 81 | src, hsh = pkg_commit_hash(pkg_path) |
|
82 | 82 | return dict( |
@@ -45,7 +45,7 b' def toggle_set_term_title(val):' | |||
|
45 | 45 | |
|
46 | 46 | Parameters |
|
47 | 47 | ---------- |
|
48 |
|
|
|
48 | val : bool | |
|
49 | 49 | If True, set_term_title() actually writes to the terminal (using the |
|
50 | 50 | appropriate platform-specific module). If False, it is a no-op. |
|
51 | 51 | """ |
@@ -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 |
|
|
|
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 |
|
|
|
86 |
|
|
|
87 |
|
|
|
88 |
|
|
|
89 |
|
|
|
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 |
|
|
|
98 | assert getattr(rich, method)() == data[mime] | |
|
100 | 99 | |
|
101 | 100 | |
|
102 | 101 | @skip_iptest_but_not_pytest |
@@ -107,11 +106,11 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 |
|
|
|
109 | assert getattr(rich, method)() == (data[mime], metadata[mime]) | |
|
111 | 110 | |
|
112 | 111 | def test_rich_output_display(): |
|
113 | 112 | """test RichOutput.display |
|
114 | ||
|
113 | ||
|
115 | 114 | This is a bit circular, because we are actually using the capture code we are testing |
|
116 | 115 | to test itself. |
|
117 | 116 | """ |
@@ -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 |
|
|
|
121 | assert len(cap.outputs) == 1 | |
|
123 | 122 | rich2 = cap.outputs[0] |
|
124 |
|
|
|
125 |
|
|
|
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 |
|
|
|
135 |
|
|
|
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 |
|
|
|
146 |
|
|
|
147 |
|
|
|
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 |
|
|
|
159 |
|
|
|
160 |
|
|
|
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 |
|
|
|
171 |
|
|
|
172 |
|
|
|
169 | assert hello_stdout == cap.stdout | |
|
170 | assert hello_stderr == cap.stderr | |
|
171 | assert cap.outputs == [] |
@@ -54,8 +54,8 b' def setup_module():' | |||
|
54 | 54 | def teardown_module(): |
|
55 | 55 | """Teardown testenvironment for the module: |
|
56 | 56 | |
|
57 |
|
|
|
58 |
|
|
|
57 | - Remove tempdir | |
|
58 | - restore sys.path | |
|
59 | 59 | """ |
|
60 | 60 | # Note: we remove the parent test dir, which is the root of all test |
|
61 | 61 | # subdirs we may have created. Use shutil instead of os.removedirs, so |
@@ -56,7 +56,7 b' HOME_TEST_DIR = join(TMP_TEST_DIR, "home_test_dir")' | |||
|
56 | 56 | def setup_module(): |
|
57 | 57 | """Setup testenvironment for the module: |
|
58 | 58 | |
|
59 |
|
|
|
59 | - Adds dummy home dir tree | |
|
60 | 60 | """ |
|
61 | 61 | # Do not mask exceptions here. In particular, catching WindowsError is a |
|
62 | 62 | # problem because that exception is only defined on Windows... |
@@ -66,7 +66,7 b' def setup_module():' | |||
|
66 | 66 | def teardown_module(): |
|
67 | 67 | """Teardown testenvironment for the module: |
|
68 | 68 | |
|
69 |
|
|
|
69 | - Remove dummy home dir tree | |
|
70 | 70 | """ |
|
71 | 71 | # Note: we remove the parent test dir, which is the root of all test |
|
72 | 72 | # subdirs we may have created. Use shutil instead of os.removedirs, so |
@@ -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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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( |
|
|
272 |
|
|
|
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( |
|
|
304 |
|
|
|
305 |
|
|
|
306 |
with make_tempfile( |
|
|
307 |
|
|
|
308 |
nt.assert_raises(IOError, path.get_py_filename, |
|
|
309 |
nt.assert_raises(IOError, path.get_py_filename, |
|
|
310 |
nt.assert_raises(IOError, path.get_py_filename, |
|
|
311 |
true_fn = |
|
|
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 |
|
|
|
314 |
|
|
|
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 |
|
|
|
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 |
|
|
|
402 |
|
|
|
403 |
|
|
|
404 |
|
|
|
405 |
|
|
|
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 |
|
|
|
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 |
|
|
|
494 | assert sorted(os.listdir(self.tempdir.name)) == ["src", "target"] |
@@ -66,7 +66,8 b' def test_find_cmd_fail():' | |||
|
66 | 66 | """Make sure that FindCmdError is raised if we can't find the cmd.""" |
|
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 |
|
|
|
84 | ||
|
84 | assert arg_split(argstr) == argv | |
|
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 |
|
|
|
98 | assert arg_split(argstr) == argv | |
|
96 | 99 | |
|
97 | 100 | |
|
98 | 101 | class SubProcessTestCase(tt.TempFileMixin): |
@@ -12,6 +12,6 b' from IPython.utils import sysinfo' | |||
|
12 | 12 | |
|
13 | 13 | def test_json_getsysinfo(): |
|
14 | 14 | """ |
|
15 |
test that it is easily jsonable and don't return bytes somewhere. |
|
|
15 | test that it is easily jsonable and don't return bytes somewhere. | |
|
16 | 16 | """ |
|
17 | 17 | json.dumps(sysinfo.get_sys_info()) |
@@ -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 |
|
|
|
35 | assert out == "aaaaa bbbbb ccccc ddddd\n" | |
|
36 | 36 | out = text.columnize(items, displaywidth=25) |
|
37 |
|
|
|
37 | assert out == "aaaaa ccccc\nbbbbb ddddd\n" | |
|
38 | 38 | out = text.columnize(items, displaywidth=12) |
|
39 |
|
|
|
39 | assert out == "aaaaa ccccc\nbbbbb ddddd\n" | |
|
40 | 40 | out = text.columnize(items, displaywidth=10) |
|
41 |
|
|
|
41 | assert out == "aaaaa\nbbbbb\nccccc\nddddd\n" | |
|
42 | 42 | |
|
43 | 43 | out = text.columnize(items, row_first=True, displaywidth=80) |
|
44 |
|
|
|
44 | assert out == "aaaaa bbbbb ccccc ddddd\n" | |
|
45 | 45 | out = text.columnize(items, row_first=True, displaywidth=25) |
|
46 |
|
|
|
46 | assert out == "aaaaa bbbbb\nccccc ddddd\n" | |
|
47 | 47 | out = text.columnize(items, row_first=True, displaywidth=12) |
|
48 |
|
|
|
48 | assert out == "aaaaa bbbbb\nccccc ddddd\n" | |
|
49 | 49 | out = text.columnize(items, row_first=True, displaywidth=10) |
|
50 |
|
|
|
50 | assert out == "aaaaa\nbbbbb\nccccc\nddddd\n" | |
|
51 | 51 | |
|
52 | 52 | out = text.columnize(items, displaywidth=40, spread=True) |
|
53 |
|
|
|
53 | assert out == "aaaaa bbbbb ccccc ddddd\n" | |
|
54 | 54 | out = text.columnize(items, displaywidth=20, spread=True) |
|
55 |
|
|
|
55 | assert out == "aaaaa ccccc\nbbbbb ddddd\n" | |
|
56 | 56 | out = text.columnize(items, displaywidth=12, spread=True) |
|
57 |
|
|
|
57 | assert out == "aaaaa ccccc\nbbbbb ddddd\n" | |
|
58 | 58 | out = text.columnize(items, displaywidth=10, spread=True) |
|
59 |
|
|
|
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 |
|
|
|
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 | out = text.columnize(items, row_first=row_first, displaywidth=size-1) | |
|
94 |
|
|
|
97 | out = text.columnize(items, row_first=row_first, displaywidth=size - 1) | |
|
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 |
|
|
|
104 | assert s == "12 3 hello" | |
|
100 | 105 | s = f.format(' '.join(['{n//%i}'%i for i in range(1,8)]), **ns) |
|
101 |
|
|
|
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 |
|
|
|
108 | assert s == "[12, 6, 4, 3, 2, 2, 1]" | |
|
104 | 109 | s = f.format("{stuff!s}", **ns) |
|
105 |
|
|
|
110 | assert s == ns["stuff"] | |
|
106 | 111 | s = f.format("{stuff!r}", **ns) |
|
107 |
|
|
|
108 | ||
|
112 | assert s == repr(ns["stuff"]) | |
|
113 | ||
|
109 | 114 | # Check with unicode: |
|
110 | 115 | s = f.format("{u}", **ns) |
|
111 |
|
|
|
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,25 +122,25 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 |
|
|
|
125 | assert s == " ['hello', 'there'] " | |
|
121 | 126 | s = f.format(" {stuff.split()[::-1]} ", **ns) |
|
122 |
|
|
|
127 | assert s == " ['there', 'hello'] " | |
|
123 | 128 | s = f.format("{stuff[::2]}", **ns) |
|
124 |
|
|
|
125 | ||
|
129 | assert s == ns["stuff"][::2] | |
|
130 | ||
|
126 | 131 | nt.assert_raises(SyntaxError, f.format, "{n:x}", **ns) |
|
127 | 132 | |
|
128 | 133 | 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 |
|
|
|
133 | ||
|
134 |
s = f.format( |
|
|
135 |
|
|
|
137 | assert s == "c +9.869604" | |
|
138 | ||
|
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 |
|
|
|
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 |
|
|
|
162 | assert s == "12" | |
|
158 | 163 | s = f.format("$n.real", **ns) |
|
159 |
|
|
|
164 | assert s == "12" | |
|
160 | 165 | s = f.format("$n/{stuff[:5]}", **ns) |
|
161 |
|
|
|
166 | assert s == "12/hello" | |
|
162 | 167 | s = f.format("$n $$HOME", **ns) |
|
163 |
|
|
|
168 | assert s == "12 $HOME" | |
|
164 | 169 | s = f.format("${foo}", foo="HOME") |
|
165 |
|
|
|
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 |
|
|
|
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 |
|
|
|
190 | assert text.strip_email_quotes(src) == cln | |
|
199 | 191 | |
|
200 | 192 | def test_LSString(): |
|
201 | 193 | lss = text.LSString("abc\ndef") |
|
202 |
|
|
|
203 |
|
|
|
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([ |
|
|
209 |
|
|
|
210 |
|
|
|
211 |
|
|
|
212 |
|
|
|
213 |
|
|
|
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 | |
@@ -450,31 +432,34 b' def strip_email_quotes(text):' | |||
|
450 | 432 | In [4]: strip_email_quotes('> > text\\n> > more\\n> more...') |
|
451 | 433 | Out[4]: '> text\\n> more\\nmore...' |
|
452 | 434 | |
|
453 |
So if any line has no quote marks ('>') |
|
|
435 | So if any line has no quote marks ('>'), then none are stripped from any | |
|
454 | 436 | of them :: |
|
455 | ||
|
437 | ||
|
456 | 438 | In [5]: strip_email_quotes('> > text\\n> > more\\nlast different') |
|
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)) | |
|
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: | |
|
447 | break | |
|
448 | prefix_char = characters[0] | |
|
449 | ||
|
450 | if prefix_char in string.whitespace or prefix_char == ">": | |
|
451 | strip_len += 1 | |
|
465 | 452 | else: |
|
466 | 453 | break |
|
467 | else: | |
|
468 | prefix = long_substr(list(matches)) | |
|
469 | if prefix: | |
|
470 | strip = len(prefix) | |
|
471 | text = '\n'.join([ ln[strip:] for ln in lines]) | |
|
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. |
|
477 | ||
|
462 | ||
|
478 | 463 | Parameters |
|
479 | 464 | ---------- |
|
480 | 465 | source : str |
@@ -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 |
@@ -23,20 +23,18 b' def generate_tokens(readline):' | |||
|
23 | 23 | |
|
24 | 24 | def line_at_cursor(cell, cursor_pos=0): |
|
25 | 25 | """Return the line in a cell at a given cursor position |
|
26 | ||
|
26 | ||
|
27 | 27 | Used for calling line-based APIs that don't support multi-line input, yet. |
|
28 | ||
|
28 | ||
|
29 | 29 | Parameters |
|
30 | 30 | ---------- |
|
31 | ||
|
32 | cell: str | |
|
31 | cell : str | |
|
33 | 32 | multiline block of text |
|
34 | cursor_pos: integer | |
|
33 | cursor_pos : integer | |
|
35 | 34 | the cursor position |
|
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 | """ |
@@ -58,15 +56,14 b' def line_at_cursor(cell, cursor_pos=0):' | |||
|
58 | 56 | |
|
59 | 57 | def token_at_cursor(cell, cursor_pos=0): |
|
60 | 58 | """Get the token at a given cursor |
|
61 | ||
|
59 | ||
|
62 | 60 | Used for introspection. |
|
63 | ||
|
61 | ||
|
64 | 62 | Function calls are prioritized, so the token for the callable will be returned |
|
65 | 63 | if the cursor is anywhere inside the call. |
|
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:// |
|
|
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``, `` |
|
|
11 |
``osx`` and ``tk``. Make sure the event loop you specify matches the |
|
|
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* |
|
|
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: |
|
|
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: |
|
|
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 |
|
|
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 {file |
|
|
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 |
|
|
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 |
|
|
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,45 +176,44 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 |
|
|
180 |
after the equal sign, or separated from it by a white space. In |
|
|
181 |
standard output of the command after the bang |
|
|
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 | !cp $file {file.split('.')[0]+'.bak'} | |
|
198 | !cp $file {file.split('.')[0] + '.bak'} | |
|
200 | 199 | |
|
201 | 200 | |
|
202 | 201 | Magics |
|
203 | 202 | ------ |
|
204 | 203 | |
|
205 |
Magic |
|
|
206 |
|
|
|
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 si |
|
|
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 |
|
|
|
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 |
|
|
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 |
|
|
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`` |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 ``__tracebackb |
|
|
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 | " <a href='gui/gui-glut.py' target='_blank'>gui-glut.py</a><br>\n", |
|
151 | 151 | " <a href='gui/gui-gtk.py' target='_blank'>gui-gtk.py</a><br>\n", |
|
152 | 152 | " <a href='gui/gui-gtk3.py' target='_blank'>gui-gtk3.py</a><br>\n", |
|
153 | " <a href='gui/gui-gtk4.py' target='_blank'>gui-gtk4.py</a><br>\n", | |
|
153 | 154 | " <a href='gui/gui-pyglet.py' target='_blank'>gui-pyglet.py</a><br>\n", |
|
154 | 155 | " <a href='gui/gui-qt.py' target='_blank'>gui-qt.py</a><br>\n", |
|
155 | 156 | " <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 | " <a href='./gui/gui-glut.py' target='_blank'>gui-glut.py</a><br>\n", |
|
3181 | 3181 | " <a href='./gui/gui-gtk.py' target='_blank'>gui-gtk.py</a><br>\n", |
|
3182 | 3182 | " <a href='./gui/gui-gtk3.py' target='_blank'>gui-gtk3.py</a><br>\n", |
|
3183 | " <a href='./gui/gui-gtk4.py' target='_blank'>gui-gtk4.py</a><br>\n", | |
|
3183 | 3184 | " <a href='./gui/gui-pyglet.py' target='_blank'>gui-pyglet.py</a><br>\n", |
|
3184 | 3185 | " <a href='./gui/gui-qt.py' target='_blank'>gui-qt.py</a><br>\n", |
|
3185 | 3186 | " <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 |
|
|
175 |
qtconsole |
|
|
176 |
doc |
|
|
177 | test = ['nose>=0.10.1', 'requests', 'testpath', 'pygments', 'nbformat', 'ipykernel', 'numpy>=1.14'], | |
|
178 | terminal = [], | |
|
179 | kernel = ['ipykernel'], | |
|
180 | nbformat = ['nbformat'], | |
|
181 | notebook = ['notebook', 'ipywidgets'], | |
|
182 | nbconvert = ['nbconvert'], | |
|
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 | ], | |
|
186 | terminal=[], | |
|
187 | kernel=["ipykernel"], | |
|
188 | nbformat=["nbformat"], | |
|
189 | notebook=["notebook", "ipywidgets"], | |
|
190 | nbconvert=["nbconvert"], | |
|
183 | 191 | ) |
|
184 | 192 | |
|
185 | 193 | install_requires = [ |
|
186 |
|
|
|
187 |
|
|
|
188 |
|
|
|
189 |
|
|
|
190 |
|
|
|
191 |
|
|
|
192 |
|
|
|
193 |
|
|
|
194 |
|
|
|
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