Show More
@@ -0,0 +1,26 b'' | |||||
|
1 | name: Build docs | |||
|
2 | ||||
|
3 | on: [push, pull_request] | |||
|
4 | ||||
|
5 | jobs: | |||
|
6 | build: | |||
|
7 | runs-on: ubuntu-latest | |||
|
8 | ||||
|
9 | steps: | |||
|
10 | - uses: actions/checkout@v2 | |||
|
11 | - name: Set up Python 3.8 | |||
|
12 | uses: actions/setup-python@v2 | |||
|
13 | with: | |||
|
14 | python-version: 3.8 | |||
|
15 | - name: Install Graphviz | |||
|
16 | run: | | |||
|
17 | sudo apt-get update | |||
|
18 | sudo apt-get install graphviz | |||
|
19 | - name: Install Python dependencies | |||
|
20 | run: | | |||
|
21 | python -m pip install --upgrade pip setuptools | |||
|
22 | pip install -r docs/requirements.txt | |||
|
23 | - name: Build docs | |||
|
24 | run: | | |||
|
25 | python tools/fixup_whats_new_pr.py | |||
|
26 | make -C docs/ html SPHINXOPTS="-W" |
@@ -0,0 +1,23 b'' | |||||
|
1 | name: Run tests on OSX | |||
|
2 | ||||
|
3 | on: [push, pull_request] | |||
|
4 | ||||
|
5 | jobs: | |||
|
6 | test: | |||
|
7 | runs-on: macos-latest | |||
|
8 | ||||
|
9 | steps: | |||
|
10 | - uses: actions/checkout@v2 | |||
|
11 | - name: Set up Python 3.7 | |||
|
12 | uses: actions/setup-python@v2 | |||
|
13 | with: | |||
|
14 | python-version: 3.7 | |||
|
15 | - name: Install and update Python dependencies | |||
|
16 | run: | | |||
|
17 | python -m pip install --upgrade pip setuptools wheel | |||
|
18 | python -m pip install --upgrade -e file://$PWD#egg=ipython[test] | |||
|
19 | python -m pip install --upgrade --upgrade-strategy eager trio curio | |||
|
20 | python -m pip install --upgrade pytest pytest-trio 'matplotlib!=3.2.0' | |||
|
21 | python -m pip install --upgrade anyio | |||
|
22 | - name: pytest | |||
|
23 | run: pytest |
@@ -0,0 +1,36 b'' | |||||
|
1 | name: Run tests | |||
|
2 | ||||
|
3 | on: [push, pull_request] | |||
|
4 | ||||
|
5 | jobs: | |||
|
6 | test: | |||
|
7 | runs-on: ubuntu-latest | |||
|
8 | strategy: | |||
|
9 | matrix: | |||
|
10 | python-version: [3.7, 3.8, 3.9] | |||
|
11 | ||||
|
12 | steps: | |||
|
13 | - uses: actions/checkout@v2 | |||
|
14 | - name: Set up Python ${{ matrix.python-version }} | |||
|
15 | uses: actions/setup-python@v2 | |||
|
16 | with: | |||
|
17 | python-version: ${{ matrix.python-version }} | |||
|
18 | - name: Install and update Python dependencies | |||
|
19 | run: | | |||
|
20 | python -m pip install --upgrade pip setuptools wheel | |||
|
21 | python -m pip install --upgrade -e file://$PWD#egg=ipython[test] | |||
|
22 | python -m pip install --upgrade --upgrade-strategy eager trio curio | |||
|
23 | python -m pip install --upgrade pytest pytest-trio 'matplotlib!=3.2.0' | |||
|
24 | python -m pip install --upgrade check-manifest pytest-cov anyio | |||
|
25 | - name: Check manifest | |||
|
26 | run: check-manifest | |||
|
27 | - name: iptest | |||
|
28 | run: | | |||
|
29 | cd /tmp && iptest --coverage xml && cd - | |||
|
30 | cp /tmp/ipy_coverage.xml ./ | |||
|
31 | cp /tmp/.coverage ./ | |||
|
32 | - name: pytest | |||
|
33 | run: | | |||
|
34 | pytest | |||
|
35 | - name: Upload coverage to Codecov | |||
|
36 | uses: codecov/codecov-action@v1 |
@@ -0,0 +1,43 b'' | |||||
|
1 | """ | |||
|
2 | Enable Gtk4 to be used interactively by IPython. | |||
|
3 | """ | |||
|
4 | # ----------------------------------------------------------------------------- | |||
|
5 | # Copyright (c) 2021, the IPython Development Team. | |||
|
6 | # | |||
|
7 | # Distributed under the terms of the Modified BSD License. | |||
|
8 | # | |||
|
9 | # The full license is in the file COPYING.txt, distributed with this software. | |||
|
10 | # ----------------------------------------------------------------------------- | |||
|
11 | ||||
|
12 | # ----------------------------------------------------------------------------- | |||
|
13 | # Imports | |||
|
14 | # ----------------------------------------------------------------------------- | |||
|
15 | ||||
|
16 | import sys | |||
|
17 | ||||
|
18 | from gi.repository import GLib | |||
|
19 | ||||
|
20 | # ----------------------------------------------------------------------------- | |||
|
21 | # Code | |||
|
22 | # ----------------------------------------------------------------------------- | |||
|
23 | ||||
|
24 | ||||
|
25 | class _InputHook: | |||
|
26 | def __init__(self, context): | |||
|
27 | self._quit = False | |||
|
28 | GLib.io_add_watch(sys.stdin, GLib.PRIORITY_DEFAULT, GLib.IO_IN, self.quit) | |||
|
29 | ||||
|
30 | def quit(self, *args, **kwargs): | |||
|
31 | self._quit = True | |||
|
32 | return False | |||
|
33 | ||||
|
34 | def run(self): | |||
|
35 | context = GLib.MainContext.default() | |||
|
36 | while not self._quit: | |||
|
37 | context.iteration(True) | |||
|
38 | ||||
|
39 | ||||
|
40 | def inputhook_gtk4(): | |||
|
41 | hook = _InputHook() | |||
|
42 | hook.run() | |||
|
43 | return 0 |
@@ -0,0 +1,27 b'' | |||||
|
1 | """ | |||
|
2 | prompt_toolkit input hook for GTK 4. | |||
|
3 | """ | |||
|
4 | ||||
|
5 | from gi.repository import GLib | |||
|
6 | ||||
|
7 | ||||
|
8 | class _InputHook: | |||
|
9 | def __init__(self, context): | |||
|
10 | self._quit = False | |||
|
11 | GLib.io_add_watch( | |||
|
12 | context.fileno(), GLib.PRIORITY_DEFAULT, GLib.IO_IN, self.quit | |||
|
13 | ) | |||
|
14 | ||||
|
15 | def quit(self, *args, **kwargs): | |||
|
16 | self._quit = True | |||
|
17 | return False | |||
|
18 | ||||
|
19 | def run(self): | |||
|
20 | context = GLib.MainContext.default() | |||
|
21 | while not self._quit: | |||
|
22 | context.iteration(True) | |||
|
23 | ||||
|
24 | ||||
|
25 | def inputhook(context): | |||
|
26 | hook = _InputHook(context) | |||
|
27 | hook.run() |
@@ -0,0 +1,7 b'' | |||||
|
1 | {%- extends "sphinx_rtd_theme/breadcrumbs.html" %} | |||
|
2 | ||||
|
3 | {% block breadcrumbs_aside %} | |||
|
4 | {% if not meta or meta.get('github_url') != 'hide' %} | |||
|
5 | {{ super() }} | |||
|
6 | {% endif %} | |||
|
7 | {% endblock %} |
@@ -0,0 +1,19 b'' | |||||
|
1 | Empty History Ranges | |||
|
2 | ==================== | |||
|
3 | ||||
|
4 | A number of magics that take history ranges can now be used with an empty | |||
|
5 | range. These magics are: | |||
|
6 | ||||
|
7 | * ``%save`` | |||
|
8 | * ``%load`` | |||
|
9 | * ``%pastebin`` | |||
|
10 | * ``%pycat`` | |||
|
11 | ||||
|
12 | Using them this way will make them take the history of the current session up | |||
|
13 | to the point of the magic call (such that the magic itself will not be | |||
|
14 | included). | |||
|
15 | ||||
|
16 | Therefore it is now possible to save the whole history to a file using simple | |||
|
17 | ``%save <filename>``, load and edit it using ``%load`` (makes for a nice usage | |||
|
18 | when followed with :kbd:`F2`), send it to dpaste.org using ``%pastebin``, or | |||
|
19 | view the whole thing syntax-highlighted with a single ``%pycat``. |
@@ -0,0 +1,38 b'' | |||||
|
1 | ``YouTubeVideo`` autoplay and the ability to add extra attributes to ``IFrame`` | |||
|
2 | =============================================================================== | |||
|
3 | ||||
|
4 | You can add any extra attributes to the ``<iframe>`` tag using the new | |||
|
5 | ``extras`` argument in the ``IFrame`` class. For example:: | |||
|
6 | ||||
|
7 | In [1]: from IPython.display import IFrame | |||
|
8 | ||||
|
9 | In [2]: IFrame(src="src", width=300, height=300, extras=['loading="eager"']) | |||
|
10 | ||||
|
11 | The above cells will result in the following HTML code being displayed in a | |||
|
12 | notebook:: | |||
|
13 | ||||
|
14 | <iframe | |||
|
15 | width="300" | |||
|
16 | height="300" | |||
|
17 | src="src" | |||
|
18 | frameborder="0" | |||
|
19 | allowfullscreen | |||
|
20 | loading="eager" | |||
|
21 | ></iframe> | |||
|
22 | ||||
|
23 | Related to the above, the ``YouTubeVideo`` class now takes an | |||
|
24 | ``allow_autoplay`` flag, which sets up the iframe of the embedded YouTube video | |||
|
25 | such that it allows autoplay. | |||
|
26 | ||||
|
27 | .. note:: | |||
|
28 | Whether this works depends on the autoplay policy of the browser rendering | |||
|
29 | the HTML allowing it. It also could get blocked by some browser extensions. | |||
|
30 | ||||
|
31 | Try it out! | |||
|
32 | :: | |||
|
33 | ||||
|
34 | In [1]: from IPython.display import YouTubeVideo | |||
|
35 | ||||
|
36 | In [2]: YouTubeVideo("dQw4w9WgXcQ", allow_autoplay=True) | |||
|
37 | ||||
|
38 | 🙃 |
@@ -0,0 +1,4 b'' | |||||
|
1 | IPython shell for ipdb interact | |||
|
2 | ------------------------------- | |||
|
3 | ||||
|
4 | The ipdb ``interact`` starts an IPython shell instead of Python's built-in ``code.interact()``. |
@@ -0,0 +1,7 b'' | |||||
|
1 | Pastebin magic expiry days option | |||
|
2 | ================================= | |||
|
3 | ||||
|
4 | The Pastebin magic now has ``-e`` option to determine | |||
|
5 | the number of days for paste expiration. For example | |||
|
6 | the paste that created with ``%pastebin -e 20 1`` magic will | |||
|
7 | be available for next 20 days. |
@@ -0,0 +1,7 b'' | |||||
|
1 | Don't start a multiline cell with sunken parenthesis | |||
|
2 | ---------------------------------------------------- | |||
|
3 | ||||
|
4 | From now on IPython will not ask for the next line of input when given a single | |||
|
5 | line with more closing than opening brackets. For example, this means that if | |||
|
6 | you (mis)type ']]' instead of '[]', a ``SyntaxError`` will show up, instead of | |||
|
7 | the ``...:`` prompt continuation. |
@@ -0,0 +1,39 b'' | |||||
|
1 | Traceback improvements | |||
|
2 | ====================== | |||
|
3 | ||||
|
4 | Previously, error tracebacks for errors happening in code cells were showing a hash, the one used for compiling the Python AST:: | |||
|
5 | ||||
|
6 | In [1]: def foo(): | |||
|
7 | ...: return 3 / 0 | |||
|
8 | ...: | |||
|
9 | ||||
|
10 | In [2]: foo() | |||
|
11 | --------------------------------------------------------------------------- | |||
|
12 | ZeroDivisionError Traceback (most recent call last) | |||
|
13 | <ipython-input-2-c19b6d9633cf> in <module> | |||
|
14 | ----> 1 foo() | |||
|
15 | ||||
|
16 | <ipython-input-1-1595a74c32d5> in foo() | |||
|
17 | 1 def foo(): | |||
|
18 | ----> 2 return 3 / 0 | |||
|
19 | 3 | |||
|
20 | ||||
|
21 | ZeroDivisionError: division by zero | |||
|
22 | ||||
|
23 | The error traceback is now correctly formatted, showing the cell number in which the error happened:: | |||
|
24 | ||||
|
25 | In [1]: def foo(): | |||
|
26 | ...: return 3 / 0 | |||
|
27 | ...: | |||
|
28 | ||||
|
29 | In [2]: foo() | |||
|
30 | --------------------------------------------------------------------------- | |||
|
31 | ZeroDivisionError Traceback (most recent call last) | |||
|
32 | In [2], in <module> | |||
|
33 | ----> 1 foo() | |||
|
34 | ||||
|
35 | In [1], in foo() | |||
|
36 | 1 def foo(): | |||
|
37 | ----> 2 return 3 / 0 | |||
|
38 | ||||
|
39 | ZeroDivisionError: division by zero |
@@ -0,0 +1,29 b'' | |||||
|
1 | Automatic Vi prompt stripping | |||
|
2 | ============================= | |||
|
3 | ||||
|
4 | When pasting code into IPython, it will strip the leading prompt characters if | |||
|
5 | there are any. For example, you can paste the following code into the console - | |||
|
6 | it will still work, even though each line is prefixed with prompts (`In`, | |||
|
7 | `Out`):: | |||
|
8 | ||||
|
9 | In [1]: 2 * 2 == 4 | |||
|
10 | Out[1]: True | |||
|
11 | ||||
|
12 | In [2]: print("This still works as pasted") | |||
|
13 | ||||
|
14 | ||||
|
15 | Previously, this was not the case for the Vi-mode prompts:: | |||
|
16 | ||||
|
17 | In [1]: [ins] In [13]: 2 * 2 == 4 | |||
|
18 | ...: Out[13]: True | |||
|
19 | ...: | |||
|
20 | File "<ipython-input-1-727bb88eaf33>", line 1 | |||
|
21 | [ins] In [13]: 2 * 2 == 4 | |||
|
22 | ^ | |||
|
23 | SyntaxError: invalid syntax | |||
|
24 | ||||
|
25 | This is now fixed, and Vi prompt prefixes - ``[ins]`` and ``[nav]`` - are | |||
|
26 | skipped just as the normal ``In`` would be. | |||
|
27 | ||||
|
28 | IPython shell can be started in the Vi mode using ``ipython | |||
|
29 | --TerminalInteractiveShell.editing_mode=vi`` |
@@ -0,0 +1,37 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | """Simple Gtk example to manually test event loop integration. | |||
|
3 | ||||
|
4 | This is meant to run tests manually in ipython as: | |||
|
5 | ||||
|
6 | In [1]: %gui gtk4 | |||
|
7 | ||||
|
8 | In [2]: %run gui-gtk4.py | |||
|
9 | """ | |||
|
10 | ||||
|
11 | import gi | |||
|
12 | ||||
|
13 | gi.require_version("Gtk", "4.0") | |||
|
14 | from gi.repository import Gtk, GLib # noqa | |||
|
15 | ||||
|
16 | ||||
|
17 | def hello_world(wigdet, data=None): | |||
|
18 | print("Hello World") | |||
|
19 | ||||
|
20 | ||||
|
21 | def close_request_cb(widget, data=None): | |||
|
22 | global running | |||
|
23 | running = False | |||
|
24 | ||||
|
25 | ||||
|
26 | running = True | |||
|
27 | window = Gtk.Window() | |||
|
28 | window.connect("close-request", close_request_cb) | |||
|
29 | button = Gtk.Button(label="Hello World") | |||
|
30 | button.connect("clicked", hello_world, None) | |||
|
31 | ||||
|
32 | window.set_child(button) | |||
|
33 | window.show() | |||
|
34 | ||||
|
35 | context = GLib.MainContext.default() | |||
|
36 | while running: | |||
|
37 | context.iteration(True) |
@@ -13,3 +13,9 b'' | |||||
13 | # <full commit hash> # initial black-format |
|
13 | # <full commit hash> # initial black-format | |
14 | # <full commit hash> # rename something internal |
|
14 | # <full commit hash> # rename something internal | |
15 | 6e748726282d1acb9a4f9f264ee679c474c4b8f5 # Apply pygrade --36plus on IPython/core/tests/test_inputtransformer.py. |
|
15 | 6e748726282d1acb9a4f9f264ee679c474c4b8f5 # Apply pygrade --36plus on IPython/core/tests/test_inputtransformer.py. | |
|
16 | 0233e65d8086d0ec34acb8685b7a5411633f0899 # apply pyupgrade to IPython/extensions/tests/test_autoreload.py | |||
|
17 | a6a7e4dd7e51b892147895006d3a2a6c34b79ae6 # apply black to IPython/extensions/tests/test_autoreload.py | |||
|
18 | c5ca5a8f25432dfd6b9eccbbe446a8348bf37cfa # apply pyupgrade to IPython/extensions/autoreload.py | |||
|
19 | 50624b84ccdece781750f5eb635a9efbf2fe30d6 # apply black to IPython/extensions/autoreload.py | |||
|
20 | b7aaa47412b96379198705955004930c57f9d74a # apply pyupgrade to IPython/extensions/autoreload.py | |||
|
21 | 9c7476a88af3e567426b412f1b3c778401d8f6aa # apply black to IPython/extensions/autoreload.py |
@@ -13,7 +13,4 b" If it's a generic Python/Jupyter question, try other forums or discourse.jupyter" | |||||
13 |
|
13 | |||
14 | If you are unsure, it's ok to post here, though, there are few maintainer so you might not get a fast response. |
|
14 | If you are unsure, it's ok to post here, though, there are few maintainer so you might not get a fast response. | |
15 |
|
15 | |||
16 | Ability of maintainers to spend time and resources on project like IPython is heavily influenced by US politics, and the current government policies have been harmful to the IPython Maintainers and Community. |
|
|||
17 |
|
||||
18 | If you are on the fence on who to vote for or wether to vote, please cast your vote in for the democrat party in the US. |
|
|||
19 | --> |
|
16 | --> |
@@ -2,9 +2,9 b' name: Run MyPy' | |||||
2 |
|
2 | |||
3 | on: |
|
3 | on: | |
4 | push: |
|
4 | push: | |
5 | branches: [ master ] |
|
5 | branches: [ master, 7.x] | |
6 | pull_request: |
|
6 | pull_request: | |
7 | branches: [ master ] |
|
7 | branches: [ master, 7.x] | |
8 |
|
8 | |||
9 | jobs: |
|
9 | jobs: | |
10 | build: |
|
10 | build: | |
@@ -26,8 +26,9 b' jobs:' | |||||
26 | pip install mypy pyflakes flake8 |
|
26 | pip install mypy pyflakes flake8 | |
27 | - name: Lint with mypy |
|
27 | - name: Lint with mypy | |
28 | run: | |
|
28 | run: | | |
29 |
mypy IPython |
|
29 | mypy -p IPython.terminal | |
30 |
mypy IPython |
|
30 | mypy -p IPython.core.magics | |
31 | - name: Lint with pyflakes |
|
31 | - name: Lint with pyflakes | |
32 | run: | |
|
32 | run: | | |
33 | flake8 IPython/core/magics/script.py |
|
33 | flake8 IPython/core/magics/script.py | |
|
34 | flake8 IPython/core/magics/packaging.py |
@@ -5,9 +5,9 b' name: Python package' | |||||
5 |
|
5 | |||
6 | on: |
|
6 | on: | |
7 | push: |
|
7 | push: | |
8 | branches: [ master ] |
|
8 | branches: [ master, 7.x ] | |
9 | pull_request: |
|
9 | pull_request: | |
10 | branches: [ master ] |
|
10 | branches: [ master, 7.x ] | |
11 |
|
11 | |||
12 | jobs: |
|
12 | jobs: | |
13 | build: |
|
13 | build: | |
@@ -28,7 +28,7 b' jobs:' | |||||
28 | - name: Install dependencies |
|
28 | - name: Install dependencies | |
29 | run: | |
|
29 | run: | | |
30 | python -m pip install --upgrade pip |
|
30 | python -m pip install --upgrade pip | |
31 | pip install darker |
|
31 | pip install darker isort | |
32 | - name: Lint with darker |
|
32 | - name: Lint with darker | |
33 | run: | |
|
33 | run: | | |
34 | darker -r 60625f241f298b5039cb2debc365db38aa7bb522 --check --diff . || ( |
|
34 | darker -r 60625f241f298b5039cb2debc365db38aa7bb522 --check --diff . || ( |
@@ -6,6 +6,7 b' docs/man/*.gz' | |||||
6 | docs/source/api/generated |
|
6 | docs/source/api/generated | |
7 | docs/source/config/options |
|
7 | docs/source/config/options | |
8 | docs/source/config/shortcuts/*.csv |
|
8 | docs/source/config/shortcuts/*.csv | |
|
9 | docs/source/savefig | |||
9 | docs/source/interactive/magics-generated.txt |
|
10 | docs/source/interactive/magics-generated.txt | |
10 | docs/gh-pages |
|
11 | docs/gh-pages | |
11 | jupyter_notebook/notebook/static/mathjax |
|
12 | jupyter_notebook/notebook/static/mathjax | |
@@ -28,3 +29,4 b' __pycache__' | |||||
28 | .python-version |
|
29 | .python-version | |
29 | venv*/ |
|
30 | venv*/ | |
30 | .idea/ |
|
31 | .idea/ | |
|
32 | .mypy_cache/ |
@@ -80,8 +80,7 b' def embed_kernel(module=None, local_ns=None, **kwargs):' | |||||
80 | The module to load into IPython globals (default: caller) |
|
80 | The module to load into IPython globals (default: caller) | |
81 | local_ns : dict, optional |
|
81 | local_ns : dict, optional | |
82 | The namespace to load into IPython user namespace (default: caller) |
|
82 | The namespace to load into IPython user namespace (default: caller) | |
83 |
|
83 | **kwargs : various, optional | ||
84 | kwargs : various, optional |
|
|||
85 | Further keyword args are relayed to the IPKernelApp constructor, |
|
84 | Further keyword args are relayed to the IPKernelApp constructor, | |
86 | allowing configuration of the Kernel. Will only have an effect |
|
85 | allowing configuration of the Kernel. Will only have an effect | |
87 | on the first embed_kernel call for a given process. |
|
86 | on the first embed_kernel call for a given process. | |
@@ -112,13 +111,12 b' def start_ipython(argv=None, **kwargs):' | |||||
112 |
|
111 | |||
113 | Parameters |
|
112 | Parameters | |
114 | ---------- |
|
113 | ---------- | |
115 |
|
||||
116 | argv : list or None, optional |
|
114 | argv : list or None, optional | |
117 | If unspecified or None, IPython will parse command-line options from sys.argv. |
|
115 | If unspecified or None, IPython will parse command-line options from sys.argv. | |
118 | To prevent any command-line parsing, pass an empty list: `argv=[]`. |
|
116 | To prevent any command-line parsing, pass an empty list: `argv=[]`. | |
119 | user_ns : dict, optional |
|
117 | user_ns : dict, optional | |
120 | specify this dictionary to initialize the IPython user namespace with particular values. |
|
118 | specify this dictionary to initialize the IPython user namespace with particular values. | |
121 | kwargs : various, optional |
|
119 | **kwargs : various, optional | |
122 | Any other kwargs will be passed to the Application constructor, |
|
120 | Any other kwargs will be passed to the Application constructor, | |
123 | such as `config`. |
|
121 | such as `config`. | |
124 | """ |
|
122 | """ | |
@@ -138,13 +136,12 b' def start_kernel(argv=None, **kwargs):' | |||||
138 |
|
136 | |||
139 | Parameters |
|
137 | Parameters | |
140 | ---------- |
|
138 | ---------- | |
141 |
|
||||
142 | argv : list or None, optional |
|
139 | argv : list or None, optional | |
143 | If unspecified or None, IPython will parse command-line options from sys.argv. |
|
140 | If unspecified or None, IPython will parse command-line options from sys.argv. | |
144 | To prevent any command-line parsing, pass an empty list: `argv=[]`. |
|
141 | To prevent any command-line parsing, pass an empty list: `argv=[]`. | |
145 | user_ns : dict, optional |
|
142 | user_ns : dict, optional | |
146 | specify this dictionary to initialize the IPython user namespace with particular values. |
|
143 | specify this dictionary to initialize the IPython user namespace with particular values. | |
147 | kwargs : various, optional |
|
144 | **kwargs : various, optional | |
148 | Any other kwargs will be passed to the Application constructor, |
|
145 | Any other kwargs will be passed to the Application constructor, | |
149 | such as `config`. |
|
146 | such as `config`. | |
150 | """ |
|
147 | """ |
@@ -66,26 +66,48 b' else:' | |||||
66 |
|
66 | |||
67 | # aliases and flags |
|
67 | # aliases and flags | |
68 |
|
68 | |||
69 | base_aliases = { |
|
69 | base_aliases = {} | |
70 | 'profile-dir' : 'ProfileDir.location', |
|
70 | if isinstance(Application.aliases, dict): | |
71 | 'profile' : 'BaseIPythonApplication.profile', |
|
71 | # traitlets 5 | |
72 | 'ipython-dir' : 'BaseIPythonApplication.ipython_dir', |
|
72 | base_aliases.update(Application.aliases) | |
73 | 'log-level' : 'Application.log_level', |
|
73 | base_aliases.update( | |
74 | 'config' : 'BaseIPythonApplication.extra_config_file', |
|
74 | { | |
|
75 | "profile-dir": "ProfileDir.location", | |||
|
76 | "profile": "BaseIPythonApplication.profile", | |||
|
77 | "ipython-dir": "BaseIPythonApplication.ipython_dir", | |||
|
78 | "log-level": "Application.log_level", | |||
|
79 | "config": "BaseIPythonApplication.extra_config_file", | |||
75 | } |
|
80 | } | |
|
81 | ) | |||
76 |
|
82 | |||
77 | base_flags = dict( |
|
83 | base_flags = dict() | |
78 | debug = ({'Application' : {'log_level' : logging.DEBUG}}, |
|
84 | if isinstance(Application.flags, dict): | |
79 | "set log level to logging.DEBUG (maximize logging output)"), |
|
85 | # traitlets 5 | |
80 | quiet = ({'Application' : {'log_level' : logging.CRITICAL}}, |
|
86 | base_flags.update(Application.flags) | |
81 | "set log level to logging.CRITICAL (minimize logging output)"), |
|
87 | base_flags.update( | |
82 | init = ({'BaseIPythonApplication' : { |
|
88 | dict( | |
83 | 'copy_config_files' : True, |
|
89 | debug=( | |
84 | 'auto_create' : True} |
|
90 | {"Application": {"log_level": logging.DEBUG}}, | |
85 | }, """Initialize profile with default config files. This is equivalent |
|
91 | "set log level to logging.DEBUG (maximize logging output)", | |
|
92 | ), | |||
|
93 | quiet=( | |||
|
94 | {"Application": {"log_level": logging.CRITICAL}}, | |||
|
95 | "set log level to logging.CRITICAL (minimize logging output)", | |||
|
96 | ), | |||
|
97 | init=( | |||
|
98 | { | |||
|
99 | "BaseIPythonApplication": { | |||
|
100 | "copy_config_files": True, | |||
|
101 | "auto_create": True, | |||
|
102 | } | |||
|
103 | }, | |||
|
104 | """Initialize profile with default config files. This is equivalent | |||
86 |
|
|
105 | to running `ipython profile create <profile>` prior to startup. | |
87 |
|
|
106 | """, | |
|
107 | ), | |||
88 | ) |
|
108 | ) | |
|
109 | ) | |||
|
110 | ||||
89 |
|
111 | |||
90 | class ProfileAwareConfigLoader(PyFileConfigLoader): |
|
112 | class ProfileAwareConfigLoader(PyFileConfigLoader): | |
91 | """A Python file config loader that is aware of IPython profiles.""" |
|
113 | """A Python file config loader that is aware of IPython profiles.""" |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file |
@@ -92,6 +92,10 b' class CachingCompiler(codeop.Compile):' | |||||
92 | # (otherwise we'd lose our tracebacks). |
|
92 | # (otherwise we'd lose our tracebacks). | |
93 | linecache.checkcache = check_linecache_ipython |
|
93 | linecache.checkcache = check_linecache_ipython | |
94 |
|
94 | |||
|
95 | # Caching a dictionary { filename: execution_count } for nicely | |||
|
96 | # rendered tracebacks. The filename corresponds to the filename | |||
|
97 | # argument used for the builtins.compile function. | |||
|
98 | self._filename_map = {} | |||
95 |
|
99 | |||
96 | def ast_parse(self, source, filename='<unknown>', symbol='exec'): |
|
100 | def ast_parse(self, source, filename='<unknown>', symbol='exec'): | |
97 | """Parse code to an AST with the current compiler flags active. |
|
101 | """Parse code to an AST with the current compiler flags active. | |
@@ -153,6 +157,10 b' class CachingCompiler(codeop.Compile):' | |||||
153 | raw_code = transformed_code |
|
157 | raw_code = transformed_code | |
154 |
|
158 | |||
155 | name = self.get_code_name(raw_code, transformed_code, number) |
|
159 | name = self.get_code_name(raw_code, transformed_code, number) | |
|
160 | ||||
|
161 | # Save the execution count | |||
|
162 | self._filename_map[name] = number | |||
|
163 | ||||
156 | entry = ( |
|
164 | entry = ( | |
157 | len(transformed_code), |
|
165 | len(transformed_code), | |
158 | time.time(), |
|
166 | time.time(), |
@@ -763,11 +763,11 b' def match_dict_keys(keys: List[Union[str, bytes, Tuple[Union[str, bytes]]]], pre' | |||||
763 |
|
763 | |||
764 | Parameters |
|
764 | Parameters | |
765 | ---------- |
|
765 | ---------- | |
766 |
keys |
|
766 | keys | |
767 | list of keys in dictionary currently being completed. |
|
767 | list of keys in dictionary currently being completed. | |
768 |
prefix |
|
768 | prefix | |
769 | Part of the text already typed by the user. E.g. `mydict[b'fo` |
|
769 | Part of the text already typed by the user. E.g. `mydict[b'fo` | |
770 |
delims |
|
770 | delims | |
771 | String of delimiters to consider when finding the current key. |
|
771 | String of delimiters to consider when finding the current key. | |
772 | extra_prefix: optional |
|
772 | extra_prefix : optional | |
773 | Part of the text already typed in multi-key index cases. E.g. for |
|
773 | Part of the text already typed in multi-key index cases. E.g. for | |
@@ -993,7 +993,7 b' def _formatparamchildren(parameter) -> str:' | |||||
993 |
|
993 | |||
994 | Parameters |
|
994 | Parameters | |
995 | ---------- |
|
995 | ---------- | |
996 |
parameter |
|
996 | parameter | |
997 | Jedi's function `Param` |
|
997 | Jedi's function `Param` | |
998 |
|
998 | |||
999 | Returns |
|
999 | Returns |
@@ -154,6 +154,17 b' def is_importable(module, attr, only_modules):' | |||||
154 | else: |
|
154 | else: | |
155 | return not(attr[:2] == '__' and attr[-2:] == '__') |
|
155 | return not(attr[:2] == '__' and attr[-2:] == '__') | |
156 |
|
156 | |||
|
157 | def is_possible_submodule(module, attr): | |||
|
158 | try: | |||
|
159 | obj = getattr(module, attr) | |||
|
160 | except AttributeError: | |||
|
161 | # Is possilby an unimported submodule | |||
|
162 | return True | |||
|
163 | except TypeError: | |||
|
164 | # https://github.com/ipython/ipython/issues/9678 | |||
|
165 | return False | |||
|
166 | return inspect.ismodule(obj) | |||
|
167 | ||||
157 |
|
168 | |||
158 | def try_import(mod: str, only_modules=False) -> List[str]: |
|
169 | def try_import(mod: str, only_modules=False) -> List[str]: | |
159 | """ |
|
170 | """ | |
@@ -172,7 +183,12 b' def try_import(mod: str, only_modules=False) -> List[str]:' | |||||
172 | completions.extend( [attr for attr in dir(m) if |
|
183 | completions.extend( [attr for attr in dir(m) if | |
173 | is_importable(m, attr, only_modules)]) |
|
184 | is_importable(m, attr, only_modules)]) | |
174 |
|
185 | |||
175 |
|
|
186 | m_all = getattr(m, "__all__", []) | |
|
187 | if only_modules: | |||
|
188 | completions.extend(attr for attr in m_all if is_possible_submodule(m, attr)) | |||
|
189 | else: | |||
|
190 | completions.extend(m_all) | |||
|
191 | ||||
176 | if m_is_init: |
|
192 | if m_is_init: | |
177 | completions.extend(module_list(os.path.dirname(m.__file__))) |
|
193 | completions.extend(module_list(os.path.dirname(m.__file__))) | |
178 | completions_set = {c for c in completions if isinstance(c, str)} |
|
194 | completions_set = {c for c in completions if isinstance(c, str)} |
@@ -104,22 +104,16 b' class CrashHandler(object):' | |||||
104 |
app : |
|
104 | app : Application | |
105 | A running :class:`Application` instance, which will be queried at |
|
105 | A running :class:`Application` instance, which will be queried at | |
106 | crash time for internal information. |
|
106 | crash time for internal information. | |
107 |
|
||||
108 | contact_name : str |
|
107 | contact_name : str | |
109 | A string with the name of the person to contact. |
|
108 | A string with the name of the person to contact. | |
110 |
|
||||
111 | contact_email : str |
|
109 | contact_email : str | |
112 | A string with the email address of the contact. |
|
110 | A string with the email address of the contact. | |
113 |
|
||||
114 | bug_tracker : str |
|
111 | bug_tracker : str | |
115 | A string with the URL for your project's bug tracker. |
|
112 | A string with the URL for your project's bug tracker. | |
116 |
|
||||
117 | show_crash_traceback : bool |
|
113 | show_crash_traceback : bool | |
118 | If false, don't print the crash traceback on stderr, only generate |
|
114 | If false, don't print the crash traceback on stderr, only generate | |
119 | the on-disk report |
|
115 | the on-disk report | |
120 |
|
116 | Non-argument instance attributes | ||
121 | Non-argument instance attributes: |
|
|||
122 |
|
||||
123 | These instances contain some non-argument attributes which allow for |
|
117 | These instances contain some non-argument attributes which allow for | |
124 | further customization of the crash handler's behavior. Please see the |
|
118 | further customization of the crash handler's behavior. Please see the | |
125 | source for further details. |
|
119 | source for further details. |
@@ -33,6 +33,7 b' import linecache' | |||||
33 | import sys |
|
33 | import sys | |
34 | import warnings |
|
34 | import warnings | |
35 | import re |
|
35 | import re | |
|
36 | import os | |||
36 |
|
37 | |||
37 | from IPython import get_ipython |
|
38 | from IPython import get_ipython | |
38 | from IPython.utils import PyColorize |
|
39 | from IPython.utils import PyColorize | |
@@ -109,7 +110,6 b' class Tracer(object):' | |||||
109 |
|
110 | |||
110 | Parameters |
|
111 | Parameters | |
111 | ---------- |
|
112 | ---------- | |
112 |
|
||||
113 | colors : str, optional |
|
113 | colors : str, optional | |
114 | The name of the color scheme to use, it must be one of IPython's |
|
114 | The name of the color scheme to use, it must be one of IPython's | |
115 | valid color schemes. If not given, the function will default to |
|
115 | valid color schemes. If not given, the function will default to | |
@@ -199,19 +199,37 b' class Pdb(OldPdb):' | |||||
199 | for a standalone version that uses prompt_toolkit, see |
|
199 | for a standalone version that uses prompt_toolkit, see | |
200 | `IPython.terminal.debugger.TerminalPdb` and |
|
200 | `IPython.terminal.debugger.TerminalPdb` and | |
201 | `IPython.terminal.debugger.set_trace()` |
|
201 | `IPython.terminal.debugger.set_trace()` | |
|
202 | ||||
|
203 | ||||
|
204 | This debugger can hide and skip frames that are tagged according to some predicates. | |||
|
205 | See the `skip_predicates` commands. | |||
|
206 | ||||
202 | """ |
|
207 | """ | |
203 |
|
208 | |||
|
209 | default_predicates = {"tbhide": True, "readonly": False, "ipython_internal": True} | |||
|
210 | ||||
204 | def __init__(self, color_scheme=None, completekey=None, |
|
211 | def __init__(self, color_scheme=None, completekey=None, | |
205 | stdin=None, stdout=None, context=5, **kwargs): |
|
212 | stdin=None, stdout=None, context=5, **kwargs): | |
206 | """Create a new IPython debugger. |
|
213 | """Create a new IPython debugger. | |
207 |
|
214 | |||
208 | :param color_scheme: Deprecated, do not use. |
|
215 | Parameters | |
209 | :param completekey: Passed to pdb.Pdb. |
|
216 | ---------- | |
210 | :param stdin: Passed to pdb.Pdb. |
|
217 | color_scheme : default None | |
211 | :param stdout: Passed to pdb.Pdb. |
|
218 | Deprecated, do not use. | |
212 | :param context: Number of lines of source code context to show when |
|
219 | completekey : default None | |
|
220 | Passed to pdb.Pdb. | |||
|
221 | stdin : default None | |||
|
222 | Passed to pdb.Pdb. | |||
|
223 | stdout : default None | |||
|
224 | Passed to pdb.Pdb. | |||
|
225 | context : int | |||
|
226 | Number of lines of source code context to show when | |||
213 | displaying stacktrace information. |
|
227 | displaying stacktrace information. | |
214 | :param kwargs: Passed to pdb.Pdb. |
|
228 | **kwargs | |
|
229 | Passed to pdb.Pdb. | |||
|
230 | ||||
|
231 | Notes | |||
|
232 | ----- | |||
215 |
|
|
233 | The possibilities are python version dependent, see the python | |
216 |
|
|
234 | docs for more info. | |
217 | """ |
|
235 | """ | |
@@ -281,6 +299,10 b' class Pdb(OldPdb):' | |||||
281 | # Set the prompt - the default prompt is '(Pdb)' |
|
299 | # Set the prompt - the default prompt is '(Pdb)' | |
282 | self.prompt = prompt |
|
300 | self.prompt = prompt | |
283 | self.skip_hidden = True |
|
301 | self.skip_hidden = True | |
|
302 | self.report_skipped = True | |||
|
303 | ||||
|
304 | # list of predicates we use to skip frames | |||
|
305 | self._predicates = self.default_predicates | |||
284 |
|
306 | |||
285 | def set_colors(self, scheme): |
|
307 | def set_colors(self, scheme): | |
286 | """Shorthand access to the color table scheme selector method.""" |
|
308 | """Shorthand access to the color table scheme selector method.""" | |
@@ -293,6 +315,26 b' class Pdb(OldPdb):' | |||||
293 | self.initial_frame = frame |
|
315 | self.initial_frame = frame | |
294 | return super().set_trace(frame) |
|
316 | return super().set_trace(frame) | |
295 |
|
317 | |||
|
318 | def _hidden_predicate(self, frame): | |||
|
319 | """ | |||
|
320 | Given a frame return whether it it should be hidden or not by IPython. | |||
|
321 | """ | |||
|
322 | ||||
|
323 | if self._predicates["readonly"]: | |||
|
324 | fname = frame.f_code.co_filename | |||
|
325 | # we need to check for file existence and interactively define | |||
|
326 | # function would otherwise appear as RO. | |||
|
327 | if os.path.isfile(fname) and not os.access(fname, os.W_OK): | |||
|
328 | return True | |||
|
329 | ||||
|
330 | if self._predicates["tbhide"]: | |||
|
331 | if frame in (self.curframe, getattr(self, "initial_frame", None)): | |||
|
332 | return False | |||
|
333 | else: | |||
|
334 | return self._get_frame_locals(frame).get("__tracebackhide__", False) | |||
|
335 | ||||
|
336 | return False | |||
|
337 | ||||
296 | def hidden_frames(self, stack): |
|
338 | def hidden_frames(self, stack): | |
297 | """ |
|
339 | """ | |
298 | Given an index in the stack return whether it should be skipped. |
|
340 | Given an index in the stack return whether it should be skipped. | |
@@ -303,14 +345,9 b' class Pdb(OldPdb):' | |||||
303 | # locals whenever the .f_locals accessor is called, so we |
|
345 | # locals whenever the .f_locals accessor is called, so we | |
304 | # avoid calling it here to preserve self.curframe_locals. |
|
346 | # avoid calling it here to preserve self.curframe_locals. | |
305 | # Futhermore, there is no good reason to hide the current frame. |
|
347 | # Futhermore, there is no good reason to hide the current frame. | |
306 | ip_hide = [ |
|
348 | ip_hide = [self._hidden_predicate(s[0]) for s in stack] | |
307 | False |
|
|||
308 | if s[0] in (self.curframe, getattr(self, "initial_frame", None)) |
|
|||
309 | else s[0].f_locals.get("__tracebackhide__", False) |
|
|||
310 | for s in stack |
|
|||
311 | ] |
|
|||
312 | ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"] |
|
349 | ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"] | |
313 | if ip_start: |
|
350 | if ip_start and self._predicates["ipython_internal"]: | |
314 | ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)] |
|
351 | ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)] | |
315 | return ip_hide |
|
352 | return ip_hide | |
316 |
|
353 | |||
@@ -398,6 +435,28 b' class Pdb(OldPdb):' | |||||
398 | self.shell.hooks.synchronize_with_editor(filename, lineno, 0) |
|
435 | self.shell.hooks.synchronize_with_editor(filename, lineno, 0) | |
399 | # vds: << |
|
436 | # vds: << | |
400 |
|
437 | |||
|
438 | def _get_frame_locals(self, frame): | |||
|
439 | """ " | |||
|
440 | Acessing f_local of current frame reset the namespace, so we want to avoid | |||
|
441 | that or the following can happend | |||
|
442 | ||||
|
443 | ipdb> foo | |||
|
444 | "old" | |||
|
445 | ipdb> foo = "new" | |||
|
446 | ipdb> foo | |||
|
447 | "new" | |||
|
448 | ipdb> where | |||
|
449 | ipdb> foo | |||
|
450 | "old" | |||
|
451 | ||||
|
452 | So if frame is self.current_frame we instead return self.curframe_locals | |||
|
453 | ||||
|
454 | """ | |||
|
455 | if frame is self.curframe: | |||
|
456 | return self.curframe_locals | |||
|
457 | else: | |||
|
458 | return frame.f_locals | |||
|
459 | ||||
401 | def format_stack_entry(self, frame_lineno, lprefix=': ', context=None): |
|
460 | def format_stack_entry(self, frame_lineno, lprefix=': ', context=None): | |
402 | if context is None: |
|
461 | if context is None: | |
403 | context = self.context |
|
462 | context = self.context | |
@@ -422,10 +481,11 b' class Pdb(OldPdb):' | |||||
422 | frame, lineno = frame_lineno |
|
481 | frame, lineno = frame_lineno | |
423 |
|
482 | |||
424 | return_value = '' |
|
483 | return_value = '' | |
425 | if '__return__' in frame.f_locals: |
|
484 | loc_frame = self._get_frame_locals(frame) | |
426 | rv = frame.f_locals['__return__'] |
|
485 | if "__return__" in loc_frame: | |
|
486 | rv = loc_frame["__return__"] | |||
427 | #return_value += '->' |
|
487 | # return_value += '->' | |
428 |
return_value += reprlib.repr(rv) + |
|
488 | return_value += reprlib.repr(rv) + "\n" | |
429 | ret.append(return_value) |
|
489 | ret.append(return_value) | |
430 |
|
490 | |||
431 | #s = filename + '(' + `lineno` + ')' |
|
491 | #s = filename + '(' + `lineno` + ')' | |
@@ -437,10 +497,10 b' class Pdb(OldPdb):' | |||||
437 | else: |
|
497 | else: | |
438 | func = "<lambda>" |
|
498 | func = "<lambda>" | |
439 |
|
499 | |||
440 |
call = |
|
500 | call = "" | |
441 |
if func != |
|
501 | if func != "?": | |
442 |
if |
|
502 | if "__args__" in loc_frame: | |
443 |
args = reprlib.repr(frame |
|
503 | args = reprlib.repr(loc_frame["__args__"]) | |
444 | else: |
|
504 | else: | |
445 | args = '()' |
|
505 | args = '()' | |
446 | call = tpl_call % (func, args) |
|
506 | call = tpl_call % (func, args) | |
@@ -533,15 +593,68 b' class Pdb(OldPdb):' | |||||
533 | except KeyboardInterrupt: |
|
593 | except KeyboardInterrupt: | |
534 | pass |
|
594 | pass | |
535 |
|
595 | |||
|
596 | def do_skip_predicates(self, args): | |||
|
597 | """ | |||
|
598 | Turn on/off individual predicates as to whether a frame should be hidden/skip. | |||
|
599 | ||||
|
600 | The global option to skip (or not) hidden frames is set with skip_hidden | |||
|
601 | ||||
|
602 | To change the value of a predicate | |||
|
603 | ||||
|
604 | skip_predicates key [true|false] | |||
|
605 | ||||
|
606 | Call without arguments to see the current values. | |||
|
607 | ||||
|
608 | To permanently change the value of an option add the corresponding | |||
|
609 | command to your ``~/.pdbrc`` file. If you are programmatically using the | |||
|
610 | Pdb instance you can also change the ``default_predicates`` class | |||
|
611 | attribute. | |||
|
612 | """ | |||
|
613 | if not args.strip(): | |||
|
614 | print("current predicates:") | |||
|
615 | for (p, v) in self._predicates.items(): | |||
|
616 | print(" ", p, ":", v) | |||
|
617 | return | |||
|
618 | type_value = args.strip().split(" ") | |||
|
619 | if len(type_value) != 2: | |||
|
620 | print( | |||
|
621 | f"Usage: skip_predicates <type> <value>, with <type> one of {set(self._predicates.keys())}" | |||
|
622 | ) | |||
|
623 | return | |||
|
624 | ||||
|
625 | type_, value = type_value | |||
|
626 | if type_ not in self._predicates: | |||
|
627 | print(f"{type_!r} not in {set(self._predicates.keys())}") | |||
|
628 | return | |||
|
629 | if value.lower() not in ("true", "yes", "1", "no", "false", "0"): | |||
|
630 | print( | |||
|
631 | f"{value!r} is invalid - use one of ('true', 'yes', '1', 'no', 'false', '0')" | |||
|
632 | ) | |||
|
633 | return | |||
|
634 | ||||
|
635 | self._predicates[type_] = value.lower() in ("true", "yes", "1") | |||
|
636 | if not any(self._predicates.values()): | |||
|
637 | print( | |||
|
638 | "Warning, all predicates set to False, skip_hidden may not have any effects." | |||
|
639 | ) | |||
|
640 | ||||
536 | def do_skip_hidden(self, arg): |
|
641 | def do_skip_hidden(self, arg): | |
537 | """ |
|
642 | """ | |
538 | Change whether or not we should skip frames with the |
|
643 | Change whether or not we should skip frames with the | |
539 | __tracebackhide__ attribute. |
|
644 | __tracebackhide__ attribute. | |
540 | """ |
|
645 | """ | |
541 | if arg.strip().lower() in ("true", "yes"): |
|
646 | if not arg.strip(): | |
|
647 | print( | |||
|
648 | f"skip_hidden = {self.skip_hidden}, use 'yes','no', 'true', or 'false' to change." | |||
|
649 | ) | |||
|
650 | elif arg.strip().lower() in ("true", "yes"): | |||
542 | self.skip_hidden = True |
|
651 | self.skip_hidden = True | |
543 | elif arg.strip().lower() in ("false", "no"): |
|
652 | elif arg.strip().lower() in ("false", "no"): | |
544 | self.skip_hidden = False |
|
653 | self.skip_hidden = False | |
|
654 | if not any(self._predicates.values()): | |||
|
655 | print( | |||
|
656 | "Warning, all predicates set to False, skip_hidden may not have any effects." | |||
|
657 | ) | |||
545 |
|
658 | |||
546 | def do_list(self, arg): |
|
659 | def do_list(self, arg): | |
547 | """Print lines of code from the current stack frame |
|
660 | """Print lines of code from the current stack frame | |
@@ -581,7 +694,7 b' class Pdb(OldPdb):' | |||||
581 |
|
694 | |||
582 | def getsourcelines(self, obj): |
|
695 | def getsourcelines(self, obj): | |
583 | lines, lineno = inspect.findsource(obj) |
|
696 | lines, lineno = inspect.findsource(obj) | |
584 |
if inspect.isframe(obj) and obj.f_globals is obj |
|
697 | if inspect.isframe(obj) and obj.f_globals is self._get_frame_locals(obj): | |
585 | # must be a module frame: do not try to cut a block out of it |
|
698 | # must be a module frame: do not try to cut a block out of it | |
586 | return lines, 1 |
|
699 | return lines, 1 | |
587 | elif inspect.ismodule(obj): |
|
700 | elif inspect.ismodule(obj): | |
@@ -705,12 +818,14 b' class Pdb(OldPdb):' | |||||
705 | def stop_here(self, frame): |
|
818 | def stop_here(self, frame): | |
706 | hidden = False |
|
819 | hidden = False | |
707 | if self.skip_hidden: |
|
820 | if self.skip_hidden: | |
708 | hidden = frame.f_locals.get("__tracebackhide__", False) |
|
821 | hidden = self._hidden_predicate(frame) | |
709 | if hidden: |
|
822 | if hidden: | |
|
823 | if self.report_skipped: | |||
710 | Colors = self.color_scheme_table.active_colors |
|
824 | Colors = self.color_scheme_table.active_colors | |
711 | ColorsNormal = Colors.Normal |
|
825 | ColorsNormal = Colors.Normal | |
712 | print(f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n") |
|
826 | print( | |
713 |
|
827 | f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n" | ||
|
828 | ) | |||
714 | return super().stop_here(frame) |
|
829 | return super().stop_here(frame) | |
715 |
|
830 | |||
716 | def do_up(self, arg): |
|
831 | def do_up(self, arg): | |
@@ -721,7 +836,7 b' class Pdb(OldPdb):' | |||||
721 | Will skip hidden frames. |
|
836 | Will skip hidden frames. | |
722 | """ |
|
837 | """ | |
723 | # modified version of upstream that skips |
|
838 | # modified version of upstream that skips | |
724 | # frames with __tracebackide__ |
|
839 | # frames with __tracebackhide__ | |
725 | if self.curindex == 0: |
|
840 | if self.curindex == 0: | |
726 | self.error("Oldest frame") |
|
841 | self.error("Oldest frame") | |
727 | return |
|
842 | return |
@@ -1178,7 +1178,12 b' class Video(DisplayObject):' | |||||
1178 |
|
1178 | |||
1179 | @skip_doctest |
|
1179 | @skip_doctest | |
1180 | def set_matplotlib_formats(*formats, **kwargs): |
|
1180 | def set_matplotlib_formats(*formats, **kwargs): | |
1181 | """Select figure formats for the inline backend. Optionally pass quality for JPEG. |
|
1181 | """ | |
|
1182 | .. deprecated:: 7.23 | |||
|
1183 | ||||
|
1184 | use `matplotlib_inline.backend_inline.set_matplotlib_formats()` | |||
|
1185 | ||||
|
1186 | Select figure formats for the inline backend. Optionally pass quality for JPEG. | |||
1182 |
|
1187 | |||
1183 | For example, this enables PNG and JPEG output with a JPEG quality of 90%:: |
|
1188 | For example, this enables PNG and JPEG output with a JPEG quality of 90%:: | |
1184 |
|
1189 | |||
@@ -1196,20 +1201,28 b' def set_matplotlib_formats(*formats, **kwargs):' | |||||
1196 | **kwargs |
|
1201 | **kwargs | |
1197 | Keyword args will be relayed to ``figure.canvas.print_figure``. |
|
1202 | Keyword args will be relayed to ``figure.canvas.print_figure``. | |
1198 | """ |
|
1203 | """ | |
1199 | from IPython.core.interactiveshell import InteractiveShell |
|
1204 | warnings.warn( | |
1200 | from IPython.core.pylabtools import select_figure_formats |
|
1205 | "`set_matplotlib_formats` is deprecated since IPython 7.23, directly " | |
1201 | # build kwargs, starting with InlineBackend config |
|
1206 | "use `matplotlib_inline.backend_inline.set_matplotlib_formats()`", | |
1202 | kw = {} |
|
1207 | DeprecationWarning, | |
1203 | from ipykernel.pylab.config import InlineBackend |
|
1208 | stacklevel=2, | |
1204 | cfg = InlineBackend.instance() |
|
1209 | ) | |
1205 | kw.update(cfg.print_figure_kwargs) |
|
1210 | ||
1206 | kw.update(**kwargs) |
|
1211 | from matplotlib_inline.backend_inline import ( | |
1207 | shell = InteractiveShell.instance() |
|
1212 | set_matplotlib_formats as set_matplotlib_formats_orig, | |
1208 | select_figure_formats(shell, formats, **kw) |
|
1213 | ) | |
|
1214 | ||||
|
1215 | set_matplotlib_formats_orig(*formats, **kwargs) | |||
1209 |
|
1216 | |||
1210 | @skip_doctest |
|
1217 | @skip_doctest | |
1211 | def set_matplotlib_close(close=True): |
|
1218 | def set_matplotlib_close(close=True): | |
1212 | """Set whether the inline backend closes all figures automatically or not. |
|
1219 | """ | |
|
1220 | .. deprecated:: 7.23 | |||
|
1221 | ||||
|
1222 | use `matplotlib_inline.backend_inline.set_matplotlib_close()` | |||
|
1223 | ||||
|
1224 | ||||
|
1225 | Set whether the inline backend closes all figures automatically or not. | |||
1213 |
|
1226 | |||
1214 | By default, the inline backend used in the IPython Notebook will close all |
|
1227 | By default, the inline backend used in the IPython Notebook will close all | |
1215 | matplotlib figures automatically after each cell is run. This means that |
|
1228 | matplotlib figures automatically after each cell is run. This means that | |
@@ -1229,6 +1242,15 b' def set_matplotlib_close(close=True):' | |||||
1229 | Should all matplotlib figures be automatically closed after each cell is |
|
1242 | Should all matplotlib figures be automatically closed after each cell is | |
1230 | run? |
|
1243 | run? | |
1231 | """ |
|
1244 | """ | |
1232 | from ipykernel.pylab.config import InlineBackend |
|
1245 | warnings.warn( | |
1233 | cfg = InlineBackend.instance() |
|
1246 | "`set_matplotlib_close` is deprecated since IPython 7.23, directly " | |
1234 | cfg.close_figures = close |
|
1247 | "use `matplotlib_inline.backend_inline.set_matplotlib_close()`", | |
|
1248 | DeprecationWarning, | |||
|
1249 | stacklevel=2, | |||
|
1250 | ) | |||
|
1251 | ||||
|
1252 | from matplotlib_inline.backend_inline import ( | |||
|
1253 | set_matplotlib_close as set_matplotlib_close_orig, | |||
|
1254 | ) | |||
|
1255 | ||||
|
1256 | set_matplotlib_close_orig(close) |
@@ -85,7 +85,17 b' def _new_id():' | |||||
85 | return b2a_hex(os.urandom(16)).decode('ascii') |
|
85 | return b2a_hex(os.urandom(16)).decode('ascii') | |
86 |
|
86 | |||
87 |
|
87 | |||
88 | def display(*objs, include=None, exclude=None, metadata=None, transient=None, display_id=None, **kwargs): |
|
88 | def display( | |
|
89 | *objs, | |||
|
90 | include=None, | |||
|
91 | exclude=None, | |||
|
92 | metadata=None, | |||
|
93 | transient=None, | |||
|
94 | display_id=None, | |||
|
95 | raw=False, | |||
|
96 | clear=False, | |||
|
97 | **kwargs | |||
|
98 | ): | |||
89 | """Display a Python object in all frontends. |
|
99 | """Display a Python object in all frontends. | |
90 |
|
100 | |||
91 | By default all representations will be computed and sent to the frontends. |
|
101 | By default all representations will be computed and sent to the frontends. | |
@@ -120,12 +130,14 b' def display(*objs, include=None, exclude=None, metadata=None, transient=None, di' | |||||
120 | Set an id for the display. |
|
130 | Set an id for the display. | |
121 | This id can be used for updating this display area later via update_display. |
|
131 | This id can be used for updating this display area later via update_display. | |
122 | If given as `True`, generate a new `display_id` |
|
132 | If given as `True`, generate a new `display_id` | |
123 | kwargs: additional keyword-args, optional |
|
133 | clear : bool, optional | |
|
134 | Should the output area be cleared before displaying anything? If True, | |||
|
135 | this will wait for additional output before clearing. [default: False] | |||
|
136 | **kwargs : additional keyword-args, optional | |||
124 | Additional keyword-arguments are passed through to the display publisher. |
|
137 | Additional keyword-arguments are passed through to the display publisher. | |
125 |
|
138 | |||
126 | Returns |
|
139 | Returns | |
127 | ------- |
|
140 | ------- | |
128 |
|
||||
129 | handle: DisplayHandle |
|
141 | handle: DisplayHandle | |
130 | Returns a handle on updatable displays for use with :func:`update_display`, |
|
142 | Returns a handle on updatable displays for use with :func:`update_display`, | |
131 | if `display_id` is given. Returns :any:`None` if no `display_id` is given |
|
143 | if `display_id` is given. Returns :any:`None` if no `display_id` is given | |
@@ -133,7 +145,6 b' def display(*objs, include=None, exclude=None, metadata=None, transient=None, di' | |||||
133 |
|
145 | |||
134 | Examples |
|
146 | Examples | |
135 | -------- |
|
147 | -------- | |
136 |
|
||||
137 | >>> class Json(object): |
|
148 | >>> class Json(object): | |
138 | ... def __init__(self, json): |
|
149 | ... def __init__(self, json): | |
139 | ... self.json = json |
|
150 | ... self.json = json | |
@@ -172,12 +183,10 b' def display(*objs, include=None, exclude=None, metadata=None, transient=None, di' | |||||
172 |
|
183 | |||
173 | See Also |
|
184 | See Also | |
174 | -------- |
|
185 | -------- | |
175 |
|
||||
176 | :func:`update_display` |
|
186 | :func:`update_display` | |
177 |
|
187 | |||
178 | Notes |
|
188 | Notes | |
179 | ----- |
|
189 | ----- | |
180 |
|
||||
181 | In Python, objects can declare their textual representation using the |
|
190 | In Python, objects can declare their textual representation using the | |
182 | `__repr__` method. IPython expands on this idea and allows objects to declare |
|
191 | `__repr__` method. IPython expands on this idea and allows objects to declare | |
183 | other, rich representations including: |
|
192 | other, rich representations including: | |
@@ -239,7 +248,6 b' def display(*objs, include=None, exclude=None, metadata=None, transient=None, di' | |||||
239 | print(*objs) |
|
248 | print(*objs) | |
240 | return |
|
249 | return | |
241 |
|
250 | |||
242 | raw = kwargs.pop('raw', False) |
|
|||
243 | if transient is None: |
|
251 | if transient is None: | |
244 | transient = {} |
|
252 | transient = {} | |
245 | if metadata is None: |
|
253 | if metadata is None: | |
@@ -263,6 +271,9 b' def display(*objs, include=None, exclude=None, metadata=None, transient=None, di' | |||||
263 | if not raw: |
|
271 | if not raw: | |
264 | format = InteractiveShell.instance().display_formatter.format |
|
272 | format = InteractiveShell.instance().display_formatter.format | |
265 |
|
273 | |||
|
274 | if clear: | |||
|
275 | clear_output(wait=True) | |||
|
276 | ||||
266 | for obj in objs: |
|
277 | for obj in objs: | |
267 | if raw: |
|
278 | if raw: | |
268 | publish_display_data(data=obj, metadata=metadata, **kwargs) |
|
279 | publish_display_data(data=obj, metadata=metadata, **kwargs) | |
@@ -285,15 +296,13 b' def update_display(obj, *, display_id, **kwargs):' | |||||
285 |
|
296 | |||
286 | Parameters |
|
297 | Parameters | |
287 | ---------- |
|
298 | ---------- | |
288 |
|
299 | obj | ||
289 | obj: |
|
|||
290 | The object with which to update the display |
|
300 | The object with which to update the display | |
291 | display_id: keyword-only |
|
301 | display_id : keyword-only | |
292 | The id of the display to update |
|
302 | The id of the display to update | |
293 |
|
303 | |||
294 | See Also |
|
304 | See Also | |
295 | -------- |
|
305 | -------- | |
296 |
|
||||
297 | :func:`display` |
|
306 | :func:`display` | |
298 | """ |
|
307 | """ | |
299 | kwargs['update'] = True |
|
308 | kwargs['update'] = True | |
@@ -328,10 +337,9 b' class DisplayHandle(object):' | |||||
328 |
|
337 | |||
329 | Parameters |
|
338 | Parameters | |
330 | ---------- |
|
339 | ---------- | |
331 |
|
340 | obj | ||
332 | obj: |
|
|||
333 | object to display |
|
341 | object to display | |
334 |
**kwargs |
|
342 | **kwargs | |
335 | additional keyword arguments passed to display |
|
343 | additional keyword arguments passed to display | |
336 | """ |
|
344 | """ | |
337 | display(obj, display_id=self.display_id, **kwargs) |
|
345 | display(obj, display_id=self.display_id, **kwargs) | |
@@ -341,10 +349,9 b' class DisplayHandle(object):' | |||||
341 |
|
349 | |||
342 | Parameters |
|
350 | Parameters | |
343 | ---------- |
|
351 | ---------- | |
344 |
|
352 | obj | ||
345 | obj: |
|
|||
346 | object to display |
|
353 | object to display | |
347 |
**kwargs |
|
354 | **kwargs | |
348 | additional keyword arguments passed to update_display |
|
355 | additional keyword arguments passed to update_display | |
349 | """ |
|
356 | """ | |
350 | update_display(obj, display_id=self.display_id, **kwargs) |
|
357 | update_display(obj, display_id=self.display_id, **kwargs) |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file |
@@ -33,7 +33,7 b' class EventManager(object):' | |||||
33 | ---------- |
|
33 | ---------- | |
34 | shell |
|
34 | shell | |
35 | The :class:`~IPython.core.interactiveshell.InteractiveShell` instance |
|
35 | The :class:`~IPython.core.interactiveshell.InteractiveShell` instance | |
36 |
available_ |
|
36 | available_events | |
37 | An iterable of names for callback events. |
|
37 | An iterable of names for callback events. | |
38 | """ |
|
38 | """ | |
39 | self.shell = shell |
|
39 | self.shell = shell |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file |
@@ -121,7 +121,6 b' class DisplayFormatter(Configurable):' | |||||
121 | Returns |
|
121 | Returns | |
122 | ------- |
|
122 | ------- | |
123 | (format_dict, metadata_dict) : tuple of two dicts |
|
123 | (format_dict, metadata_dict) : tuple of two dicts | |
124 |
|
||||
125 | format_dict is a dictionary of key/value pairs, one of each format that was |
|
124 | format_dict is a dictionary of key/value pairs, one of each format that was | |
126 | generated for the object. The keys are the format types, which |
|
125 | generated for the object. The keys are the format types, which | |
127 | will usually be MIME type strings and the values and JSON'able |
|
126 | will usually be MIME type strings and the values and JSON'able | |
@@ -133,7 +132,6 b' class DisplayFormatter(Configurable):' | |||||
133 |
|
132 | |||
134 | Notes |
|
133 | Notes | |
135 | ----- |
|
134 | ----- | |
136 |
|
||||
137 | If an object implement `_repr_mimebundle_` as well as various |
|
135 | If an object implement `_repr_mimebundle_` as well as various | |
138 | `_repr_*_`, the data returned by `_repr_mimebundle_` will take |
|
136 | `_repr_*_`, the data returned by `_repr_mimebundle_` will take | |
139 | precedence and the corresponding `_repr_*_` for this mimetype will |
|
137 | precedence and the corresponding `_repr_*_` for this mimetype will | |
@@ -636,7 +634,6 b' class PlainTextFormatter(BaseFormatter):' | |||||
636 |
|
634 | |||
637 | This parameter can be set via the '%precision' magic. |
|
635 | This parameter can be set via the '%precision' magic. | |
638 | """ |
|
636 | """ | |
639 |
|
||||
640 | new = change['new'] |
|
637 | new = change['new'] | |
641 | if '%' in new: |
|
638 | if '%' in new: | |
642 | # got explicit format string |
|
639 | # got explicit format string | |
@@ -678,6 +675,11 b' class PlainTextFormatter(BaseFormatter):' | |||||
678 | def _type_printers_default(self): |
|
675 | def _type_printers_default(self): | |
679 | d = pretty._type_pprinters.copy() |
|
676 | d = pretty._type_pprinters.copy() | |
680 | d[float] = lambda obj,p,cycle: p.text(self.float_format%obj) |
|
677 | d[float] = lambda obj,p,cycle: p.text(self.float_format%obj) | |
|
678 | # if NumPy is used, set precision for its float64 type | |||
|
679 | if "numpy" in sys.modules: | |||
|
680 | import numpy | |||
|
681 | ||||
|
682 | d[numpy.float64] = lambda obj, p, cycle: p.text(self.float_format % obj) | |||
681 | return d |
|
683 | return d | |
682 |
|
684 | |||
683 | @default('deferred_printers') |
|
685 | @default('deferred_printers') |
@@ -445,8 +445,11 b' class HistoryAccessor(HistoryAccessorBase):' | |||||
445 | Parameters |
|
445 | Parameters | |
446 | ---------- |
|
446 | ---------- | |
447 | rangestr : str |
|
447 | rangestr : str | |
448 |
A string specifying ranges, e.g. "5 ~2/1-4". |
|
448 | A string specifying ranges, e.g. "5 ~2/1-4". If empty string is used, | |
449 | :func:`magic_history` for full details. |
|
449 | this will return everything from current session's history. | |
|
450 | ||||
|
451 | See the documentation of :func:`%history` for the full details. | |||
|
452 | ||||
450 | raw, output : bool |
|
453 | raw, output : bool | |
451 | As :meth:`get_range` |
|
454 | As :meth:`get_range` | |
452 |
|
455 | |||
@@ -851,11 +854,18 b' $""", re.VERBOSE)' | |||||
851 | def extract_hist_ranges(ranges_str): |
|
854 | def extract_hist_ranges(ranges_str): | |
852 | """Turn a string of history ranges into 3-tuples of (session, start, stop). |
|
855 | """Turn a string of history ranges into 3-tuples of (session, start, stop). | |
853 |
|
856 | |||
|
857 | Empty string results in a `[(0, 1, None)]`, i.e. "everything from current | |||
|
858 | session". | |||
|
859 | ||||
854 | Examples |
|
860 | Examples | |
855 | -------- |
|
861 | -------- | |
856 | >>> list(extract_hist_ranges("~8/5-~7/4 2")) |
|
862 | >>> list(extract_hist_ranges("~8/5-~7/4 2")) | |
857 | [(-8, 5, None), (-7, 1, 5), (0, 2, 3)] |
|
863 | [(-8, 5, None), (-7, 1, 5), (0, 2, 3)] | |
858 | """ |
|
864 | """ | |
|
865 | if ranges_str == "": | |||
|
866 | yield (0, 1, None) # Everything from current session | |||
|
867 | return | |||
|
868 | ||||
859 | for range_str in ranges_str.split(): |
|
869 | for range_str in ranges_str.split(): | |
860 | rmatch = range_re.match(range_str) |
|
870 | rmatch = range_re.match(range_str) | |
861 | if not rmatch: |
|
871 | if not rmatch: |
@@ -10,7 +10,9 b' deprecated in 7.0.' | |||||
10 | # Copyright (c) IPython Development Team. |
|
10 | # Copyright (c) IPython Development Team. | |
11 | # Distributed under the terms of the Modified BSD License. |
|
11 | # Distributed under the terms of the Modified BSD License. | |
12 |
|
12 | |||
13 | from codeop import compile_command |
|
13 | import ast | |
|
14 | import sys | |||
|
15 | from codeop import CommandCompiler, Compile | |||
14 | import re |
|
16 | import re | |
15 | import tokenize |
|
17 | import tokenize | |
16 | from typing import List, Tuple, Optional, Any |
|
18 | from typing import List, Tuple, Optional, Any | |
@@ -89,7 +91,30 b' classic_prompt = PromptStripper(' | |||||
89 | initial_re=re.compile(r'^>>>( |$)') |
|
91 | initial_re=re.compile(r'^>>>( |$)') | |
90 | ) |
|
92 | ) | |
91 |
|
93 | |||
92 | ipython_prompt = PromptStripper(re.compile(r'^(In \[\d+\]: |\s*\.{3,}: ?)')) |
|
94 | ipython_prompt = PromptStripper( | |
|
95 | re.compile( | |||
|
96 | r""" | |||
|
97 | ^( # Match from the beginning of a line, either: | |||
|
98 | ||||
|
99 | # 1. First-line prompt: | |||
|
100 | ((\[nav\]|\[ins\])?\ )? # Vi editing mode prompt, if it's there | |||
|
101 | In\ # The 'In' of the prompt, with a space | |||
|
102 | \[\d+\]: # Command index, as displayed in the prompt | |||
|
103 | \ # With a mandatory trailing space | |||
|
104 | ||||
|
105 | | # ... or ... | |||
|
106 | ||||
|
107 | # 2. The three dots of the multiline prompt | |||
|
108 | \s* # All leading whitespace characters | |||
|
109 | \.{3,}: # The three (or more) dots | |||
|
110 | \ ? # With an optional trailing space | |||
|
111 | ||||
|
112 | ) | |||
|
113 | """, | |||
|
114 | re.VERBOSE, | |||
|
115 | ) | |||
|
116 | ) | |||
|
117 | ||||
93 |
|
118 | |||
94 | def cell_magic(lines): |
|
119 | def cell_magic(lines): | |
95 | if not lines or not lines[0].startswith('%%'): |
|
120 | if not lines or not lines[0].startswith('%%'): | |
@@ -508,6 +533,20 b' def make_tokens_by_line(lines:List[str]):' | |||||
508 |
|
533 | |||
509 | return tokens_by_line |
|
534 | return tokens_by_line | |
510 |
|
535 | |||
|
536 | ||||
|
537 | def has_sunken_brackets(tokens: List[tokenize.TokenInfo]): | |||
|
538 | """Check if the depth of brackets in the list of tokens drops below 0""" | |||
|
539 | parenlev = 0 | |||
|
540 | for token in tokens: | |||
|
541 | if token.string in {"(", "[", "{"}: | |||
|
542 | parenlev += 1 | |||
|
543 | elif token.string in {")", "]", "}"}: | |||
|
544 | parenlev -= 1 | |||
|
545 | if parenlev < 0: | |||
|
546 | return True | |||
|
547 | return False | |||
|
548 | ||||
|
549 | ||||
511 | def show_linewise_tokens(s: str): |
|
550 | def show_linewise_tokens(s: str): | |
512 | """For investigation and debugging""" |
|
551 | """For investigation and debugging""" | |
513 | if not s.endswith('\n'): |
|
552 | if not s.endswith('\n'): | |
@@ -662,6 +701,15 b' class TransformerManager:' | |||||
662 |
|
701 | |||
663 | tokens_by_line = make_tokens_by_line(lines) |
|
702 | tokens_by_line = make_tokens_by_line(lines) | |
664 |
|
703 | |||
|
704 | # Bail if we got one line and there are more closing parentheses than | |||
|
705 | # the opening ones | |||
|
706 | if ( | |||
|
707 | len(lines) == 1 | |||
|
708 | and tokens_by_line | |||
|
709 | and has_sunken_brackets(tokens_by_line[0]) | |||
|
710 | ): | |||
|
711 | return "invalid", None | |||
|
712 | ||||
665 | if not tokens_by_line: |
|
713 | if not tokens_by_line: | |
666 | return 'incomplete', find_last_indent(lines) |
|
714 | return 'incomplete', find_last_indent(lines) | |
667 |
|
715 | |||
@@ -727,3 +775,25 b' def find_last_indent(lines):' | |||||
727 | if not m: |
|
775 | if not m: | |
728 | return 0 |
|
776 | return 0 | |
729 | return len(m.group(0).replace('\t', ' '*4)) |
|
777 | return len(m.group(0).replace('\t', ' '*4)) | |
|
778 | ||||
|
779 | ||||
|
780 | class MaybeAsyncCompile(Compile): | |||
|
781 | def __init__(self, extra_flags=0): | |||
|
782 | super().__init__() | |||
|
783 | self.flags |= extra_flags | |||
|
784 | ||||
|
785 | def __call__(self, *args, **kwds): | |||
|
786 | return compile(*args, **kwds) | |||
|
787 | ||||
|
788 | ||||
|
789 | class MaybeAsyncCommandCompiler(CommandCompiler): | |||
|
790 | def __init__(self, extra_flags=0): | |||
|
791 | self.compiler = MaybeAsyncCompile(extra_flags=extra_flags) | |||
|
792 | ||||
|
793 | ||||
|
794 | if (sys.version_info.major, sys.version_info.minor) >= (3, 8): | |||
|
795 | _extra_flags = ast.PyCF_ALLOW_TOP_LEVEL_AWAIT | |||
|
796 | else: | |||
|
797 | _extra_flags = ast.PyCF_ONLY_AST | |||
|
798 | ||||
|
799 | compile_command = MaybeAsyncCommandCompiler(extra_flags=_extra_flags) |
@@ -923,12 +923,11 b' class InteractiveShell(SingletonConfigurable):' | |||||
923 | paths.append(p.resolve()) |
|
923 | paths.append(p.resolve()) | |
924 |
|
924 | |||
925 | # In Cygwin paths like "c:\..." and '\cygdrive\c\...' are possible |
|
925 | # In Cygwin paths like "c:\..." and '\cygdrive\c\...' are possible | |
926 |
if |
|
926 | if p_venv.parts[1] == "cygdrive": | |
927 | p_venv = Path(str(p_venv)[11:]) |
|
927 | drive_name = p_venv.parts[2] | |
928 | elif len(str(p_venv)) >= 2 and str(p_venv)[1] == ":": |
|
928 | p_venv = (drive_name + ":/") / Path(*p_venv.parts[3:]) | |
929 | p_venv = Path(str(p_venv)[2:]) |
|
|||
930 |
|
929 | |||
931 |
if any( |
|
930 | if any(p_venv == p.parents[1] for p in paths): | |
932 | # Our exe is inside or has access to the virtualenv, don't need to do anything. |
|
931 | # Our exe is inside or has access to the virtualenv, don't need to do anything. | |
933 | return |
|
932 | return | |
934 |
|
933 | |||
@@ -1896,9 +1895,14 b' class InteractiveShell(SingletonConfigurable):' | |||||
1896 | exception or returns an invalid result, it will be immediately |
|
1895 | exception or returns an invalid result, it will be immediately | |
1897 | disabled. |
|
1896 | disabled. | |
1898 |
|
1897 | |||
|
1898 | Notes | |||
|
1899 | ----- | |||
|
1900 | ||||
1899 | WARNING: by putting in your own exception handler into IPython's main |
|
1901 | WARNING: by putting in your own exception handler into IPython's main | |
1900 | execution loop, you run a very good chance of nasty crashes. This |
|
1902 | execution loop, you run a very good chance of nasty crashes. This | |
1901 |
facility should only be used if you really know what you are doing. |
|
1903 | facility should only be used if you really know what you are doing. | |
|
1904 | """ | |||
|
1905 | ||||
1902 | if not isinstance(exc_tuple, tuple): |
|
1906 | if not isinstance(exc_tuple, tuple): | |
1903 | raise TypeError("The custom exceptions must be given as a tuple.") |
|
1907 | raise TypeError("The custom exceptions must be given as a tuple.") | |
1904 |
|
1908 | |||
@@ -1966,7 +1970,7 b' class InteractiveShell(SingletonConfigurable):' | |||||
1966 | sys.excepthook themselves. I guess this is a feature that |
|
1970 | sys.excepthook themselves. I guess this is a feature that | |
1967 | enables them to keep running after exceptions that would |
|
1971 | enables them to keep running after exceptions that would | |
1968 | otherwise kill their mainloop. This is a bother for IPython |
|
1972 | otherwise kill their mainloop. This is a bother for IPython | |
1969 |
which ex |
|
1973 | which expects to catch all of the program exceptions with a try: | |
1970 | except: statement. |
|
1974 | except: statement. | |
1971 |
|
1975 | |||
1972 | Normally, IPython sets sys.excepthook to a CrashHandler instance, so if |
|
1976 | Normally, IPython sets sys.excepthook to a CrashHandler instance, so if | |
@@ -2085,13 +2089,17 b' class InteractiveShell(SingletonConfigurable):' | |||||
2085 | except KeyboardInterrupt: |
|
2089 | except KeyboardInterrupt: | |
2086 | print('\n' + self.get_exception_only(), file=sys.stderr) |
|
2090 | print('\n' + self.get_exception_only(), file=sys.stderr) | |
2087 |
|
2091 | |||
2088 | def _showtraceback(self, etype, evalue, stb): |
|
2092 | def _showtraceback(self, etype, evalue, stb: str): | |
2089 | """Actually show a traceback. |
|
2093 | """Actually show a traceback. | |
2090 |
|
2094 | |||
2091 | Subclasses may override this method to put the traceback on a different |
|
2095 | Subclasses may override this method to put the traceback on a different | |
2092 | place, like a side channel. |
|
2096 | place, like a side channel. | |
2093 | """ |
|
2097 | """ | |
2094 |
|
|
2098 | val = self.InteractiveTB.stb2text(stb) | |
|
2099 | try: | |||
|
2100 | print(val) | |||
|
2101 | except UnicodeEncodeError: | |||
|
2102 | print(val.encode("utf-8", "backslashreplace").decode()) | |||
2095 |
|
2103 | |||
2096 | def showsyntaxerror(self, filename=None, running_compiled_code=False): |
|
2104 | def showsyntaxerror(self, filename=None, running_compiled_code=False): | |
2097 | """Display the syntax error that just occurred. |
|
2105 | """Display the syntax error that just occurred. | |
@@ -2218,6 +2226,9 b' class InteractiveShell(SingletonConfigurable):' | |||||
2218 | matches : list |
|
2226 | matches : list | |
2219 | A sorted list with all possible completions. |
|
2227 | A sorted list with all possible completions. | |
2220 |
|
2228 | |||
|
2229 | ||||
|
2230 | Notes | |||
|
2231 | ----- | |||
2221 | The optional arguments allow the completion to take more context into |
|
2232 | The optional arguments allow the completion to take more context into | |
2222 | account, and are part of the low-level completion API. |
|
2233 | account, and are part of the low-level completion API. | |
2223 |
|
2234 | |||
@@ -2226,7 +2237,8 b' class InteractiveShell(SingletonConfigurable):' | |||||
2226 | exposing it as a method, it can be used by other non-readline |
|
2237 | exposing it as a method, it can be used by other non-readline | |
2227 | environments (such as GUIs) for text completion. |
|
2238 | environments (such as GUIs) for text completion. | |
2228 |
|
2239 | |||
2229 |
|
|
2240 | Examples | |
|
2241 | -------- | |||
2230 |
|
2242 | |||
2231 | In [1]: x = 'hello' |
|
2243 | In [1]: x = 'hello' | |
2232 |
|
2244 | |||
@@ -2508,6 +2520,23 b' class InteractiveShell(SingletonConfigurable):' | |||||
2508 | Command to execute. |
|
2520 | Command to execute. | |
2509 | """ |
|
2521 | """ | |
2510 | cmd = self.var_expand(cmd, depth=1) |
|
2522 | cmd = self.var_expand(cmd, depth=1) | |
|
2523 | # warn if there is an IPython magic alternative. | |||
|
2524 | main_cmd = cmd.split()[0] | |||
|
2525 | has_magic_alternatives = ("pip", "conda", "cd", "ls") | |||
|
2526 | ||||
|
2527 | # had to check if the command was an alias expanded because of `ls` | |||
|
2528 | is_alias_expanded = self.alias_manager.is_alias(main_cmd) and ( | |||
|
2529 | self.alias_manager.retrieve_alias(main_cmd).strip() == cmd.strip() | |||
|
2530 | ) | |||
|
2531 | ||||
|
2532 | if main_cmd in has_magic_alternatives and not is_alias_expanded: | |||
|
2533 | warnings.warn( | |||
|
2534 | ( | |||
|
2535 | "You executed the system command !{0} which may not work " | |||
|
2536 | "as expected. Try the IPython magic %{0} instead." | |||
|
2537 | ).format(main_cmd) | |||
|
2538 | ) | |||
|
2539 | ||||
2511 | # protect os.system from UNC paths on Windows, which it can't handle: |
|
2540 | # protect os.system from UNC paths on Windows, which it can't handle: | |
2512 | if sys.platform == 'win32': |
|
2541 | if sys.platform == 'win32': | |
2513 | from IPython.utils._process_win32 import AvoidUNCPath |
|
2542 | from IPython.utils._process_win32 import AvoidUNCPath | |
@@ -3109,9 +3138,7 b' class InteractiveShell(SingletonConfigurable):' | |||||
3109 | _run_async = False |
|
3138 | _run_async = False | |
3110 |
|
3139 | |||
3111 | with self.builtin_trap: |
|
3140 | with self.builtin_trap: | |
3112 |
cell_name = |
|
3141 | cell_name = compiler.cache(cell, self.execution_count, raw_code=raw_cell) | |
3113 | cell, self.execution_count, raw_code=raw_cell |
|
|||
3114 | ) |
|
|||
3115 |
|
3142 | |||
3116 | with self.display_trap: |
|
3143 | with self.display_trap: | |
3117 | # Compile to bytecode |
|
3144 | # Compile to bytecode | |
@@ -3514,6 +3541,7 b' class InteractiveShell(SingletonConfigurable):' | |||||
3514 | display figures inline. |
|
3541 | display figures inline. | |
3515 | """ |
|
3542 | """ | |
3516 | from IPython.core import pylabtools as pt |
|
3543 | from IPython.core import pylabtools as pt | |
|
3544 | from matplotlib_inline.backend_inline import configure_inline_support | |||
3517 | gui, backend = pt.find_gui_and_backend(gui, self.pylab_gui_select) |
|
3545 | gui, backend = pt.find_gui_and_backend(gui, self.pylab_gui_select) | |
3518 |
|
3546 | |||
3519 | if gui != 'inline': |
|
3547 | if gui != 'inline': | |
@@ -3527,7 +3555,7 b' class InteractiveShell(SingletonConfigurable):' | |||||
3527 | gui, backend = pt.find_gui_and_backend(self.pylab_gui_select) |
|
3555 | gui, backend = pt.find_gui_and_backend(self.pylab_gui_select) | |
3528 |
|
3556 | |||
3529 | pt.activate_matplotlib(backend) |
|
3557 | pt.activate_matplotlib(backend) | |
3530 |
|
|
3558 | configure_inline_support(self, backend) | |
3531 |
|
3559 | |||
3532 | # Now we must activate the gui pylab wants to use, and fix %run to take |
|
3560 | # Now we must activate the gui pylab wants to use, and fix %run to take | |
3533 | # plot updates into account |
|
3561 | # plot updates into account | |
@@ -3667,12 +3695,15 b' class InteractiveShell(SingletonConfigurable):' | |||||
3667 |
|
3695 | |||
3668 | Parameters |
|
3696 | Parameters | |
3669 | ---------- |
|
3697 | ---------- | |
3670 |
range_str : str |
|
3698 | range_str : str | |
3671 | The set of slices is given as a string, like "~5/6-~4/2 4:8 9", |
|
3699 | The set of slices is given as a string, like "~5/6-~4/2 4:8 9", | |
3672 | since this function is for use by magic functions which get their |
|
3700 | since this function is for use by magic functions which get their | |
3673 | arguments as strings. The number before the / is the session |
|
3701 | arguments as strings. The number before the / is the session | |
3674 | number: ~n goes n back from the current session. |
|
3702 | number: ~n goes n back from the current session. | |
3675 |
|
3703 | |||
|
3704 | If empty string is given, returns history of current session | |||
|
3705 | without the last input. | |||
|
3706 | ||||
3676 | raw : bool, optional |
|
3707 | raw : bool, optional | |
3677 | By default, the processed input is used. If this is true, the raw |
|
3708 | By default, the processed input is used. If this is true, the raw | |
3678 | input history is used instead. |
|
3709 | input history is used instead. | |
@@ -3686,7 +3717,16 b' class InteractiveShell(SingletonConfigurable):' | |||||
3686 | * ``N-M`` -> include items N..M (closed endpoint). |
|
3717 | * ``N-M`` -> include items N..M (closed endpoint). | |
3687 | """ |
|
3718 | """ | |
3688 | lines = self.history_manager.get_range_by_str(range_str, raw=raw) |
|
3719 | lines = self.history_manager.get_range_by_str(range_str, raw=raw) | |
3689 |
|
|
3720 | text = "\n".join(x for _, _, x in lines) | |
|
3721 | ||||
|
3722 | # Skip the last line, as it's probably the magic that called this | |||
|
3723 | if not range_str: | |||
|
3724 | if "\n" not in text: | |||
|
3725 | text = "" | |||
|
3726 | else: | |||
|
3727 | text = text[: text.rfind("\n")] | |||
|
3728 | ||||
|
3729 | return text | |||
3690 |
|
3730 | |||
3691 | def find_user_code(self, target, raw=True, py_only=False, skip_encoding_cookie=True, search_ns=False): |
|
3731 | def find_user_code(self, target, raw=True, py_only=False, skip_encoding_cookie=True, search_ns=False): | |
3692 | """Get a code string from history, file, url, or a string or macro. |
|
3732 | """Get a code string from history, file, url, or a string or macro. | |
@@ -3695,14 +3735,15 b' class InteractiveShell(SingletonConfigurable):' | |||||
3695 |
|
3735 | |||
3696 | Parameters |
|
3736 | Parameters | |
3697 | ---------- |
|
3737 | ---------- | |
3698 |
|
||||
3699 | target : str |
|
3738 | target : str | |
3700 |
|
||||
3701 | A string specifying code to retrieve. This will be tried respectively |
|
3739 | A string specifying code to retrieve. This will be tried respectively | |
3702 | as: ranges of input history (see %history for syntax), url, |
|
3740 | as: ranges of input history (see %history for syntax), url, | |
3703 | corresponding .py file, filename, or an expression evaluating to a |
|
3741 | corresponding .py file, filename, or an expression evaluating to a | |
3704 | string or Macro in the user namespace. |
|
3742 | string or Macro in the user namespace. | |
3705 |
|
3743 | |||
|
3744 | If empty string is given, returns complete history of current | |||
|
3745 | session, without the last line. | |||
|
3746 | ||||
3706 | raw : bool |
|
3747 | raw : bool | |
3707 | If true (default), retrieve raw history. Has no effect on the other |
|
3748 | If true (default), retrieve raw history. Has no effect on the other | |
3708 | retrieval mechanisms. |
|
3749 | retrieval mechanisms. | |
@@ -3771,6 +3812,22 b' class InteractiveShell(SingletonConfigurable):' | |||||
3771 | raise TypeError("%s is neither a string nor a macro." % target, |
|
3812 | raise TypeError("%s is neither a string nor a macro." % target, | |
3772 | codeobj) |
|
3813 | codeobj) | |
3773 |
|
3814 | |||
|
3815 | def _atexit_once(self): | |||
|
3816 | """ | |||
|
3817 | At exist operation that need to be called at most once. | |||
|
3818 | Second call to this function per instance will do nothing. | |||
|
3819 | """ | |||
|
3820 | ||||
|
3821 | if not getattr(self, "_atexit_once_called", False): | |||
|
3822 | self._atexit_once_called = True | |||
|
3823 | # Clear all user namespaces to release all references cleanly. | |||
|
3824 | self.reset(new_session=False) | |||
|
3825 | # Close the history session (this stores the end time and line count) | |||
|
3826 | # this must be *before* the tempfile cleanup, in case of temporary | |||
|
3827 | # history db | |||
|
3828 | self.history_manager.end_session() | |||
|
3829 | self.history_manager = None | |||
|
3830 | ||||
3774 | #------------------------------------------------------------------------- |
|
3831 | #------------------------------------------------------------------------- | |
3775 | # Things related to IPython exiting |
|
3832 | # Things related to IPython exiting | |
3776 | #------------------------------------------------------------------------- |
|
3833 | #------------------------------------------------------------------------- | |
@@ -3785,26 +3842,24 b' class InteractiveShell(SingletonConfigurable):' | |||||
3785 | code that has the appropriate information, rather than trying to |
|
3842 | code that has the appropriate information, rather than trying to | |
3786 | clutter |
|
3843 | clutter | |
3787 | """ |
|
3844 | """ | |
3788 | # Close the history session (this stores the end time and line count) |
|
3845 | self._atexit_once() | |
3789 | # this must be *before* the tempfile cleanup, in case of temporary |
|
|||
3790 | # history db |
|
|||
3791 | self.history_manager.end_session() |
|
|||
3792 |
|
3846 | |||
3793 | # Cleanup all tempfiles and folders left around |
|
3847 | # Cleanup all tempfiles and folders left around | |
3794 | for tfile in self.tempfiles: |
|
3848 | for tfile in self.tempfiles: | |
3795 | try: |
|
3849 | try: | |
3796 | tfile.unlink() |
|
3850 | tfile.unlink() | |
|
3851 | self.tempfiles.remove(tfile) | |||
3797 | except FileNotFoundError: |
|
3852 | except FileNotFoundError: | |
3798 | pass |
|
3853 | pass | |
3799 |
|
3854 | del self.tempfiles | ||
3800 | for tdir in self.tempdirs: |
|
3855 | for tdir in self.tempdirs: | |
3801 | try: |
|
3856 | try: | |
3802 | tdir.rmdir() |
|
3857 | tdir.rmdir() | |
|
3858 | self.tempdirs.remove(tdir) | |||
3803 | except FileNotFoundError: |
|
3859 | except FileNotFoundError: | |
3804 | pass |
|
3860 | pass | |
|
3861 | del self.tempdirs | |||
3805 |
|
3862 | |||
3806 | # Clear all user namespaces to release all references cleanly. |
|
|||
3807 | self.reset(new_session=False) |
|
|||
3808 |
|
3863 | |||
3809 | # Run user hooks |
|
3864 | # Run user hooks | |
3810 | self.hooks.shutdown_hook() |
|
3865 | self.hooks.shutdown_hook() |
@@ -618,6 +618,9 b' class Magics(Configurable):' | |||||
618 | posix = kw.get('posix', os.name == 'posix') |
|
618 | posix = kw.get('posix', os.name == 'posix') | |
619 | strict = kw.get('strict', True) |
|
619 | strict = kw.get('strict', True) | |
620 |
|
620 | |||
|
621 | preserve_non_opts = kw.get("preserve_non_opts", False) | |||
|
622 | remainder_arg_str = arg_str | |||
|
623 | ||||
621 | # Check if we have more than one argument to warrant extra processing: |
|
624 | # Check if we have more than one argument to warrant extra processing: | |
622 | odict = {} # Dictionary with options |
|
625 | odict = {} # Dictionary with options | |
623 | args = arg_str.split() |
|
626 | args = arg_str.split() | |
@@ -629,10 +632,18 b' class Magics(Configurable):' | |||||
629 | try: |
|
632 | try: | |
630 | opts,args = getopt(argv, opt_str, long_opts) |
|
633 | opts,args = getopt(argv, opt_str, long_opts) | |
631 | except GetoptError as e: |
|
634 | except GetoptError as e: | |
632 |
raise UsageError( |
|
635 | raise UsageError( | |
633 |
|
|
636 | '%s ( allowed: "%s" %s)' % (e.msg, opt_str, " ".join(long_opts)) | |
|
637 | ) from e | |||
634 | for o,a in opts: |
|
638 | for o, a in opts: | |
635 | if o.startswith('--'): |
|
639 | if mode == "string" and preserve_non_opts: | |
|
640 | # remove option-parts from the original args-string and preserve remaining-part. | |||
|
641 | # This relies on the arg_split(...) and getopt(...)'s impl spec, that the parsed options are | |||
|
642 | # returned in the original order. | |||
|
643 | remainder_arg_str = remainder_arg_str.replace(o, "", 1).replace( | |||
|
644 | a, "", 1 | |||
|
645 | ) | |||
|
646 | if o.startswith("--"): | |||
636 | o = o[2:] |
|
647 | o = o[2:] | |
637 | else: |
|
648 | else: | |
638 | o = o[1:] |
|
649 | o = o[1:] | |
@@ -649,7 +660,10 b' class Magics(Configurable):' | |||||
649 | # Prepare opts,args for return |
|
660 | # Prepare opts,args for return | |
650 | opts = Struct(odict) |
|
661 | opts = Struct(odict) | |
651 | if mode == 'string': |
|
662 | if mode == 'string': | |
652 | args = ' '.join(args) |
|
663 | if preserve_non_opts: | |
|
664 | args = remainder_arg_str.lstrip() | |||
|
665 | else: | |||
|
666 | args = " ".join(args) | |||
653 |
|
667 | |||
654 | return opts,args |
|
668 | return opts,args | |
655 |
|
669 |
@@ -104,16 +104,32 b' class AutoMagics(Magics):' | |||||
104 | # all-random (note for auto-testing) |
|
104 | # all-random (note for auto-testing) | |
105 | """ |
|
105 | """ | |
106 |
|
106 | |||
|
107 | valid_modes = { | |||
|
108 | 0: "Off", | |||
|
109 | 1: "Smart", | |||
|
110 | 2: "Full", | |||
|
111 | } | |||
|
112 | ||||
|
113 | def errorMessage() -> str: | |||
|
114 | error = "Valid modes: " | |||
|
115 | for k, v in valid_modes.items(): | |||
|
116 | error += str(k) + "->" + v + ", " | |||
|
117 | error = error[:-2] # remove tailing `, ` after last element | |||
|
118 | return error | |||
|
119 | ||||
107 | if parameter_s: |
|
120 | if parameter_s: | |
|
121 | if not parameter_s in map(str, valid_modes.keys()): | |||
|
122 | error(errorMessage()) | |||
|
123 | return | |||
108 | arg = int(parameter_s) |
|
124 | arg = int(parameter_s) | |
109 | else: |
|
125 | else: | |
110 | arg = 'toggle' |
|
126 | arg = 'toggle' | |
111 |
|
127 | |||
112 |
if not arg in ( |
|
128 | if not arg in (*list(valid_modes.keys()), "toggle"): | |
113 | error('Valid modes: (0->Off, 1->Smart, 2->Full') |
|
129 | error(errorMessage()) | |
114 | return |
|
130 | return | |
115 |
|
131 | |||
116 |
if arg in ( |
|
132 | if arg in (valid_modes.keys()): | |
117 | self.shell.autocall = arg |
|
133 | self.shell.autocall = arg | |
118 | else: # toggle |
|
134 | else: # toggle | |
119 | if self.shell.autocall: |
|
135 | if self.shell.autocall: | |
@@ -125,4 +141,4 b' class AutoMagics(Magics):' | |||||
125 | except AttributeError: |
|
141 | except AttributeError: | |
126 | self.shell.autocall = self._magic_state.autocall_save = 1 |
|
142 | self.shell.autocall = self._magic_state.autocall_save = 1 | |
127 |
|
143 | |||
128 |
print("Automatic calling is:", |
|
144 | print("Automatic calling is:", list(valid_modes.values())[self.shell.autocall]) |
@@ -493,6 +493,7 b' Currently the magic system has the following functions:""",' | |||||
493 | %gui qt5 # enable PyQt5 event loop integration |
|
493 | %gui qt5 # enable PyQt5 event loop integration | |
494 | %gui gtk # enable PyGTK event loop integration |
|
494 | %gui gtk # enable PyGTK event loop integration | |
495 | %gui gtk3 # enable Gtk3 event loop integration |
|
495 | %gui gtk3 # enable Gtk3 event loop integration | |
|
496 | %gui gtk4 # enable Gtk4 event loop integration | |||
496 | %gui tk # enable Tk event loop integration |
|
497 | %gui tk # enable Tk event loop integration | |
497 | %gui osx # enable Cocoa event loop integration |
|
498 | %gui osx # enable Cocoa event loop integration | |
498 | # (requires %matplotlib 1.1) |
|
499 | # (requires %matplotlib 1.1) |
@@ -20,7 +20,7 b' import re' | |||||
20 | import sys |
|
20 | import sys | |
21 | import ast |
|
21 | import ast | |
22 | from itertools import chain |
|
22 | from itertools import chain | |
23 | from urllib.request import urlopen |
|
23 | from urllib.request import Request, urlopen | |
24 | from urllib.parse import urlencode |
|
24 | from urllib.parse import urlencode | |
25 | from pathlib import Path |
|
25 | from pathlib import Path | |
26 |
|
26 | |||
@@ -29,6 +29,7 b' from IPython.core.error import TryNext, StdinNotImplementedError, UsageError' | |||||
29 | from IPython.core.macro import Macro |
|
29 | from IPython.core.macro import Macro | |
30 | from IPython.core.magic import Magics, magics_class, line_magic |
|
30 | from IPython.core.magic import Magics, magics_class, line_magic | |
31 | from IPython.core.oinspect import find_file, find_source_lines |
|
31 | from IPython.core.oinspect import find_file, find_source_lines | |
|
32 | from IPython.core.release import version | |||
32 | from IPython.testing.skipdoctest import skip_doctest |
|
33 | from IPython.testing.skipdoctest import skip_doctest | |
33 | from IPython.utils.contexts import preserve_keys |
|
34 | from IPython.utils.contexts import preserve_keys | |
34 | from IPython.utils.path import get_py_filename |
|
35 | from IPython.utils.path import get_py_filename | |
@@ -201,6 +202,9 b' class CodeMagics(Magics):' | |||||
201 | This function uses the same syntax as %history for input ranges, |
|
202 | This function uses the same syntax as %history for input ranges, | |
202 | then saves the lines to the filename you specify. |
|
203 | then saves the lines to the filename you specify. | |
203 |
|
204 | |||
|
205 | If no ranges are specified, saves history of the current session up to | |||
|
206 | this point. | |||
|
207 | ||||
204 | It adds a '.py' extension to the file if you don't do so yourself, and |
|
208 | It adds a '.py' extension to the file if you don't do so yourself, and | |
205 | it asks for confirmation before overwriting existing files. |
|
209 | it asks for confirmation before overwriting existing files. | |
206 |
|
210 | |||
@@ -245,20 +249,25 b' class CodeMagics(Magics):' | |||||
245 |
|
249 | |||
246 | @line_magic |
|
250 | @line_magic | |
247 | def pastebin(self, parameter_s=''): |
|
251 | def pastebin(self, parameter_s=''): | |
248 |
"""Upload code to dpaste |
|
252 | """Upload code to dpaste.com, returning the URL. | |
249 |
|
253 | |||
250 | Usage:\\ |
|
254 | Usage:\\ | |
251 | %pastebin [-d "Custom description"] 1-7 |
|
255 | %pastebin [-d "Custom description"][-e 24] 1-7 | |
252 |
|
256 | |||
253 | The argument can be an input history range, a filename, or the name of a |
|
257 | The argument can be an input history range, a filename, or the name of a | |
254 | string or macro. |
|
258 | string or macro. | |
255 |
|
259 | |||
|
260 | If no arguments are given, uploads the history of this session up to | |||
|
261 | this point. | |||
|
262 | ||||
256 | Options: |
|
263 | Options: | |
257 |
|
264 | |||
258 |
-d: Pass a custom description |
|
265 | -d: Pass a custom description. The default will say | |
259 | "Pasted from IPython". |
|
266 | "Pasted from IPython". | |
|
267 | -e: Pass number of days for the link to be expired. | |||
|
268 | The default will be 7 days. | |||
260 | """ |
|
269 | """ | |
261 |
opts, args = self.parse_options(parameter_s, |
|
270 | opts, args = self.parse_options(parameter_s, "d:e:") | |
262 |
|
271 | |||
263 | try: |
|
272 | try: | |
264 | code = self.shell.find_user_code(args) |
|
273 | code = self.shell.find_user_code(args) | |
@@ -266,13 +275,30 b' class CodeMagics(Magics):' | |||||
266 | print(e.args[0]) |
|
275 | print(e.args[0]) | |
267 | return |
|
276 | return | |
268 |
|
277 | |||
269 | post_data = urlencode({ |
|
278 | expiry_days = 7 | |
270 | "title": opts.get('d', "Pasted from IPython"), |
|
279 | try: | |
271 | "syntax": "python3", |
|
280 | expiry_days = int(opts.get("e", 7)) | |
272 | "content": code |
|
281 | except ValueError as e: | |
273 | }).encode('utf-8') |
|
282 | print(e.args[0].capitalize()) | |
|
283 | return | |||
|
284 | if expiry_days < 1 or expiry_days > 365: | |||
|
285 | print("Expiry days should be in range of 1 to 365") | |||
|
286 | return | |||
274 |
|
287 | |||
275 | response = urlopen("http://dpaste.com/api/v2/", post_data) |
|
288 | post_data = urlencode( | |
|
289 | { | |||
|
290 | "title": opts.get("d", "Pasted from IPython"), | |||
|
291 | "syntax": "python", | |||
|
292 | "content": code, | |||
|
293 | "expiry_days": expiry_days, | |||
|
294 | } | |||
|
295 | ).encode("utf-8") | |||
|
296 | ||||
|
297 | request = Request( | |||
|
298 | "https://dpaste.com/api/v2/", | |||
|
299 | headers={"User-Agent": "IPython v{}".format(version)}, | |||
|
300 | ) | |||
|
301 | response = urlopen(request, post_data) | |||
276 | return response.headers.get('Location') |
|
302 | return response.headers.get('Location') | |
277 |
|
303 | |||
278 | @line_magic |
|
304 | @line_magic | |
@@ -295,6 +321,9 b' class CodeMagics(Magics):' | |||||
295 | where source can be a filename, URL, input history range, macro, or |
|
321 | where source can be a filename, URL, input history range, macro, or | |
296 | element in the user namespace |
|
322 | element in the user namespace | |
297 |
|
323 | |||
|
324 | If no arguments are given, loads the history of this session up to this | |||
|
325 | point. | |||
|
326 | ||||
298 | Options: |
|
327 | Options: | |
299 |
|
328 | |||
300 | -r <lines>: Specify lines or ranges of lines to load from the source. |
|
329 | -r <lines>: Specify lines or ranges of lines to load from the source. | |
@@ -313,6 +342,7 b' class CodeMagics(Magics):' | |||||
313 | confirmation before loading source with more than 200 000 characters, unless |
|
342 | confirmation before loading source with more than 200 000 characters, unless | |
314 | -y flag is passed or if the frontend does not support raw_input:: |
|
343 | -y flag is passed or if the frontend does not support raw_input:: | |
315 |
|
344 | |||
|
345 | %load | |||
316 | %load myscript.py |
|
346 | %load myscript.py | |
317 | %load 7-27 |
|
347 | %load 7-27 | |
318 | %load myMacro |
|
348 | %load myMacro | |
@@ -324,13 +354,7 b' class CodeMagics(Magics):' | |||||
324 | %load -n my_module.wonder_function |
|
354 | %load -n my_module.wonder_function | |
325 | """ |
|
355 | """ | |
326 | opts,args = self.parse_options(arg_s,'yns:r:') |
|
356 | opts,args = self.parse_options(arg_s,'yns:r:') | |
327 |
|
||||
328 | if not args: |
|
|||
329 | raise UsageError('Missing filename, URL, input history range, ' |
|
|||
330 | 'macro, or element in the user namespace.') |
|
|||
331 |
|
||||
332 | search_ns = 'n' in opts |
|
357 | search_ns = 'n' in opts | |
333 |
|
||||
334 | contents = self.shell.find_user_code(args, search_ns=search_ns) |
|
358 | contents = self.shell.find_user_code(args, search_ns=search_ns) | |
335 |
|
359 | |||
336 | if 's' in opts: |
|
360 | if 's' in opts: |
@@ -1073,8 +1073,9 b' class ExecutionMagics(Magics):' | |||||
1073 | does not matter as long as results from timeit.py are not mixed with |
|
1073 | does not matter as long as results from timeit.py are not mixed with | |
1074 | those from %timeit.""" |
|
1074 | those from %timeit.""" | |
1075 |
|
1075 | |||
1076 |
opts, stmt = self.parse_options( |
|
1076 | opts, stmt = self.parse_options( | |
1077 | posix=False, strict=False) |
|
1077 | line, "n:r:tcp:qo", posix=False, strict=False, preserve_non_opts=True | |
|
1078 | ) | |||
1078 | if stmt == "" and cell is None: |
|
1079 | if stmt == "" and cell is None: | |
1079 | return |
|
1080 | return | |
1080 |
|
1081 | |||
@@ -1234,11 +1235,15 b' class ExecutionMagics(Magics):' | |||||
1234 | CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s |
|
1235 | CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s | |
1235 | Wall time: 0.00 |
|
1236 | Wall time: 0.00 | |
1236 |
|
1237 | |||
1237 | Note that the time needed by Python to compile the given expression |
|
1238 | ||
1238 | will be reported if it is more than 0.1s. In this example, the |
|
1239 | .. note:: | |
1239 | actual exponentiation is done by Python at compilation time, so while |
|
1240 | The time needed by Python to compile the given expression will be | |
1240 | the expression can take a noticeable amount of time to compute, that |
|
1241 | reported if it is more than 0.1s. | |
1241 | time is purely due to the compilation: |
|
1242 | ||
|
1243 | In the example below, the actual exponentiation is done by Python | |||
|
1244 | at compilation time, so while the expression can take a noticeable | |||
|
1245 | amount of time to compute, that time is purely due to the | |||
|
1246 | compilation:: | |||
1242 |
|
1247 | |||
1243 | In [5]: %time 3**9999; |
|
1248 | In [5]: %time 3**9999; | |
1244 | CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s |
|
1249 | CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s | |
@@ -1249,7 +1254,6 b' class ExecutionMagics(Magics):' | |||||
1249 | Wall time: 0.00 s |
|
1254 | Wall time: 0.00 s | |
1250 | Compiler : 0.78 s |
|
1255 | Compiler : 0.78 s | |
1251 |
|
|
1256 | """ | |
1252 |
|
||||
1253 | # fail immediately if the given expression can't be compiled |
|
1257 | # fail immediately if the given expression can't be compiled | |
1254 |
|
1258 | |||
1255 | if line and cell: |
|
1259 | if line and cell: |
@@ -184,14 +184,12 b' class HistoryMagics(Magics):' | |||||
184 | n = 10 if limit is None else limit |
|
184 | n = 10 if limit is None else limit | |
185 | hist = history_manager.get_tail(n, raw=raw, output=get_output) |
|
185 | hist = history_manager.get_tail(n, raw=raw, output=get_output) | |
186 | else: |
|
186 | else: | |
187 | if args.range: # Get history by ranges |
|
|||
188 |
|
|
187 | if args.pattern: | |
189 |
|
|
188 | range_pattern = "*" + " ".join(args.pattern) + "*" | |
190 |
|
|
189 | print_nums = True | |
191 |
|
|
190 | hist = history_manager.get_range_by_str( | |
192 | raw, get_output) |
|
191 | " ".join(args.range), raw, get_output | |
193 | else: # Just get history for the current session |
|
192 | ) | |
194 | hist = history_manager.get_range(raw=raw, output=get_output) |
|
|||
195 |
|
193 | |||
196 | # We could be displaying the entire history, so let's not try to pull |
|
194 | # We could be displaying the entire history, so let's not try to pull | |
197 | # it into a list in memory. Anything that needs more space will just |
|
195 | # it into a list in memory. Anything that needs more space will just | |
@@ -282,6 +280,7 b' class HistoryMagics(Magics):' | |||||
282 | return |
|
280 | return | |
283 | else: |
|
281 | else: | |
284 | self.shell.set_next_input(cmd.rstrip()) |
|
282 | self.shell.set_next_input(cmd.rstrip()) | |
|
283 | return | |||
285 | print("Couldn't evaluate or find in history:", arg) |
|
284 | print("Couldn't evaluate or find in history:", arg) | |
286 |
|
285 | |||
287 | @line_magic |
|
286 | @line_magic | |
@@ -301,6 +300,14 b' class HistoryMagics(Magics):' | |||||
301 | opts, args = self.parse_options(parameter_s, 'l:g:', mode='string') |
|
300 | opts, args = self.parse_options(parameter_s, 'l:g:', mode='string') | |
302 | if "l" in opts: # Last n lines |
|
301 | if "l" in opts: # Last n lines | |
303 | n = int(opts['l']) |
|
302 | n = int(opts['l']) | |
|
303 | ||||
|
304 | if n == 0: | |||
|
305 | print("Requested 0 last lines - nothing to run") | |||
|
306 | return | |||
|
307 | elif n < 0: | |||
|
308 | print("Number of lines to rerun cannot be negative") | |||
|
309 | return | |||
|
310 | ||||
304 | hist = self.shell.history_manager.get_tail(n) |
|
311 | hist = self.shell.history_manager.get_tail(n) | |
305 | elif "g" in opts: # Search |
|
312 | elif "g" in opts: # Search | |
306 | p = "*"+opts['g']+"*" |
|
313 | p = "*"+opts['g']+"*" |
@@ -302,33 +302,36 b' class OSMagics(Magics):' | |||||
302 | """Change the current working directory. |
|
302 | """Change the current working directory. | |
303 |
|
303 | |||
304 | This command automatically maintains an internal list of directories |
|
304 | This command automatically maintains an internal list of directories | |
305 | you visit during your IPython session, in the variable _dh. The |
|
305 | you visit during your IPython session, in the variable ``_dh``. The | |
306 |
command %dhist shows this history nicely formatted. You can |
|
306 | command :magic:`%dhist` shows this history nicely formatted. You can | |
307 |
|
|
307 | also do ``cd -<tab>`` to see directory history conveniently. | |
308 |
|
||||
309 | Usage: |
|
308 | Usage: | |
310 |
|
309 | |||
311 | cd 'dir': changes to directory 'dir'. |
|
310 | - ``cd 'dir'``: changes to directory 'dir'. | |
312 |
|
311 | - ``cd -``: changes to the last visited directory. | ||
313 |
cd -: changes to the |
|
312 | - ``cd -<n>``: changes to the n-th directory in the directory history. | |
314 |
|
313 | - ``cd --foo``: change to directory that matches 'foo' in history | ||
315 | cd -<n>: changes to the n-th directory in the directory history. |
|
314 | - ``cd -b <bookmark_name>``: jump to a bookmark set by %bookmark | |
|
315 | - Hitting a tab key after ``cd -b`` allows you to tab-complete | |||
|
316 | bookmark names. | |||
316 |
|
317 | |||
317 | cd --foo: change to directory that matches 'foo' in history |
|
318 | .. note:: | |
|
319 | ``cd <bookmark_name>`` is enough if there is no directory | |||
|
320 | ``<bookmark_name>``, but a bookmark with the name exists. | |||
318 |
|
321 | |||
319 | cd -b <bookmark_name>: jump to a bookmark set by %bookmark |
|
|||
320 | (note: cd <bookmark_name> is enough if there is no |
|
|||
321 | directory <bookmark_name>, but a bookmark with the name exists.) |
|
|||
322 | 'cd -b <tab>' allows you to tab-complete bookmark names. |
|
|||
323 |
|
322 | |||
324 | Options: |
|
323 | Options: | |
325 |
|
324 | |||
326 |
-q |
|
325 | -q Be quiet. Do not print the working directory after the | |
327 | executed. By default IPython's cd command does print this directory, |
|
326 | cd command is executed. By default IPython's cd | |
328 | since the default prompts do not display path information. |
|
327 | command does print this directory, since the default | |
|
328 | prompts do not display path information. | |||
|
329 | ||||
|
330 | .. note:: | |||
|
331 | Note that ``!cd`` doesn't work for this purpose because the shell | |||
|
332 | where ``!command`` runs is immediately discarded after executing | |||
|
333 | 'command'. | |||
329 |
|
334 | |||
330 | Note that !cd doesn't work for this purpose because the shell where |
|
|||
331 | !command runs is immediately discarded after executing 'command'. |
|
|||
332 |
|
335 | |||
333 | Examples |
|
336 | Examples | |
334 | -------- |
|
337 | -------- | |
@@ -436,11 +439,11 b' class OSMagics(Magics):' | |||||
436 |
|
439 | |||
437 | Usage:\\ |
|
440 | Usage:\\ | |
438 |
|
441 | |||
439 | %env: lists all environment variables/values |
|
442 | :``%env``: lists all environment variables/values | |
440 | %env var: get value for var |
|
443 | :``%env var``: get value for var | |
441 | %env var val: set value for var |
|
444 | :``%env var val``: set value for var | |
442 | %env var=val: set value for var |
|
445 | :``%env var=val``: set value for var | |
443 | %env var=$val: set value for var, using python expansion if possible |
|
446 | :``%env var=$val``: set value for var, using python expansion if possible | |
444 | """ |
|
447 | """ | |
445 | if parameter_s.strip(): |
|
448 | if parameter_s.strip(): | |
446 | split = '=' if '=' in parameter_s else ' ' |
|
449 | split = '=' if '=' in parameter_s else ' ' | |
@@ -803,17 +806,16 b' class OSMagics(Magics):' | |||||
803 | to be Python source and will show it with syntax highlighting. |
|
806 | to be Python source and will show it with syntax highlighting. | |
804 |
|
807 | |||
805 | This magic command can either take a local filename, an url, |
|
808 | This magic command can either take a local filename, an url, | |
806 |
an history range (see %history) or a macro as argument |
|
809 | an history range (see %history) or a macro as argument. | |
|
810 | ||||
|
811 | If no parameter is given, prints out history of current session up to | |||
|
812 | this point. :: | |||
807 |
|
813 | |||
808 | %pycat myscript.py |
|
814 | %pycat myscript.py | |
809 | %pycat 7-27 |
|
815 | %pycat 7-27 | |
810 | %pycat myMacro |
|
816 | %pycat myMacro | |
811 | %pycat http://www.example.com/myscript.py |
|
817 | %pycat http://www.example.com/myscript.py | |
812 | """ |
|
818 | """ | |
813 | if not parameter_s: |
|
|||
814 | raise UsageError('Missing filename, URL, input history range, ' |
|
|||
815 | 'or macro.') |
|
|||
816 |
|
||||
817 |
try |
|
819 | try: | |
818 | cont = self.shell.find_user_code(parameter_s, skip_encoding_cookie=False) |
|
820 | cont = self.shell.find_user_code(parameter_s, skip_encoding_cookie=False) | |
819 | except (ValueError, IOError): |
|
821 | except (ValueError, IOError): |
@@ -66,7 +66,14 b' class PackagingMagics(Magics):' | |||||
66 | Usage: |
|
66 | Usage: | |
67 | %pip install [pkgs] |
|
67 | %pip install [pkgs] | |
68 | """ |
|
68 | """ | |
69 | self.shell.system(' '.join([sys.executable, '-m', 'pip', line])) |
|
69 | python = sys.executable | |
|
70 | if sys.platform == "win32": | |||
|
71 | python = '"' + python + '"' | |||
|
72 | else: | |||
|
73 | python = shlex.quote(python) | |||
|
74 | ||||
|
75 | self.shell.system(" ".join([python, "-m", "pip", line])) | |||
|
76 | ||||
70 | print("Note: you may need to restart the kernel to use updated packages.") |
|
77 | print("Note: you may need to restart the kernel to use updated packages.") | |
71 |
|
78 | |||
72 | @line_magic |
|
79 | @line_magic |
@@ -88,7 +88,7 b' class PylabMagics(Magics):' | |||||
88 | You can list the available backends using the -l/--list option:: |
|
88 | You can list the available backends using the -l/--list option:: | |
89 |
|
89 | |||
90 | In [4]: %matplotlib --list |
|
90 | In [4]: %matplotlib --list | |
91 | Available matplotlib backends: ['osx', 'qt4', 'qt5', 'gtk3', 'notebook', 'wx', 'qt', 'nbagg', |
|
91 | Available matplotlib backends: ['osx', 'qt4', 'qt5', 'gtk3', 'gtk4', 'notebook', 'wx', 'qt', 'nbagg', | |
92 | 'gtk', 'tk', 'inline'] |
|
92 | 'gtk', 'tk', 'inline'] | |
93 | """ |
|
93 | """ | |
94 | args = magic_arguments.parse_argstring(self.matplotlib, line) |
|
94 | args = magic_arguments.parse_argstring(self.matplotlib, line) |
@@ -199,6 +199,7 b' class ScriptMagics(Magics):' | |||||
199 | _handle_stream(process.stderr, args.err, sys.stderr) |
|
199 | _handle_stream(process.stderr, args.err, sys.stderr) | |
200 | ) |
|
200 | ) | |
201 | await asyncio.wait([stdout_task, stderr_task]) |
|
201 | await asyncio.wait([stdout_task, stderr_task]) | |
|
202 | await process.wait() | |||
202 |
|
203 | |||
203 | if sys.platform.startswith("win"): |
|
204 | if sys.platform.startswith("win"): | |
204 | asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy()) |
|
205 | asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy()) | |
@@ -212,7 +213,7 b' class ScriptMagics(Magics):' | |||||
212 | *cmd, |
|
213 | *cmd, | |
213 | stdout=asyncio.subprocess.PIPE, |
|
214 | stdout=asyncio.subprocess.PIPE, | |
214 | stderr=asyncio.subprocess.PIPE, |
|
215 | stderr=asyncio.subprocess.PIPE, | |
215 | stdin=asyncio.subprocess.PIPE |
|
216 | stdin=asyncio.subprocess.PIPE, | |
216 | ) |
|
217 | ) | |
217 | ) |
|
218 | ) | |
218 | except OSError as e: |
|
219 | except OSError as e: | |
@@ -264,7 +265,11 b' class ScriptMagics(Magics):' | |||||
264 | print("Error while terminating subprocess (pid=%i): %s" % (p.pid, e)) |
|
265 | print("Error while terminating subprocess (pid=%i): %s" % (p.pid, e)) | |
265 | return |
|
266 | return | |
266 | if args.raise_error and p.returncode!=0: |
|
267 | if args.raise_error and p.returncode!=0: | |
267 | raise CalledProcessError(p.returncode, cell) |
|
268 | # If we get here and p.returncode is still None, we must have | |
|
269 | # killed it but not yet seen its return code. We don't wait for it, | |||
|
270 | # in case it's stuck in uninterruptible sleep. -9 = SIGKILL | |||
|
271 | rc = p.returncode or -9 | |||
|
272 | raise CalledProcessError(rc, cell) | |||
268 |
|
273 | |||
269 | def _run_script(self, p, cell, to_close): |
|
274 | def _run_script(self, p, cell, to_close): | |
270 | """callback for running the script in the background""" |
|
275 | """callback for running the script in the background""" |
@@ -5,29 +5,33 b'' | |||||
5 | # Distributed under the terms of the Modified BSD License. |
|
5 | # Distributed under the terms of the Modified BSD License. | |
6 |
|
6 | |||
7 | from io import BytesIO |
|
7 | from io import BytesIO | |
|
8 | import warnings | |||
8 |
|
9 | |||
9 | from IPython.core.display import _pngxy |
|
10 | from IPython.core.display import _pngxy | |
10 | from IPython.utils.decorators import flag_calls |
|
11 | from IPython.utils.decorators import flag_calls | |
11 |
|
12 | |||
12 | # If user specifies a GUI, that dictates the backend, otherwise we read the |
|
13 | # If user specifies a GUI, that dictates the backend, otherwise we read the | |
13 | # user's mpl default from the mpl rc structure |
|
14 | # user's mpl default from the mpl rc structure | |
14 |
backends = { |
|
15 | backends = { | |
15 | 'gtk': 'GTKAgg', |
|
16 | "tk": "TkAgg", | |
16 |
|
|
17 | "gtk": "GTKAgg", | |
17 | 'wx': 'WXAgg', |
|
18 | "gtk3": "GTK3Agg", | |
18 | 'qt4': 'Qt4Agg', |
|
19 | "gtk4": "GTK4Agg", | |
19 | 'qt5': 'Qt5Agg', |
|
20 | "wx": "WXAgg", | |
20 | 'qt': 'Qt5Agg', |
|
21 | "qt4": "Qt4Agg", | |
21 | 'osx': 'MacOSX', |
|
22 | "qt5": "Qt5Agg", | |
22 | 'nbagg': 'nbAgg', |
|
23 | "qt6": "QtAgg", | |
23 | 'notebook': 'nbAgg', |
|
24 | "qt": "Qt5Agg", | |
24 | 'agg': 'agg', |
|
25 | "osx": "MacOSX", | |
25 | 'svg': 'svg', |
|
26 | "nbagg": "nbAgg", | |
26 | 'pdf': 'pdf', |
|
27 | "notebook": "nbAgg", | |
27 | 'ps': 'ps', |
|
28 | "agg": "agg", | |
28 | 'inline': 'module://ipykernel.pylab.backend_inline', |
|
29 | "svg": "svg", | |
29 | 'ipympl': 'module://ipympl.backend_nbagg', |
|
30 | "pdf": "pdf", | |
30 | 'widget': 'module://ipympl.backend_nbagg', |
|
31 | "ps": "ps", | |
|
32 | "inline": "module://matplotlib_inline.backend_inline", | |||
|
33 | "ipympl": "module://ipympl.backend_nbagg", | |||
|
34 | "widget": "module://ipympl.backend_nbagg", | |||
31 | } |
|
35 | } | |
32 |
|
36 | |||
33 | # We also need a reverse backends2guis mapping that will properly choose which |
|
37 | # We also need a reverse backends2guis mapping that will properly choose which | |
@@ -39,17 +43,18 b' backend2gui = dict(zip(backends.values(), backends.keys()))' | |||||
39 | backend2gui['Qt4Agg'] = 'qt' |
|
43 | backend2gui['Qt4Agg'] = 'qt' | |
40 | # In the reverse mapping, there are a few extra valid matplotlib backends that |
|
44 | # In the reverse mapping, there are a few extra valid matplotlib backends that | |
41 | # map to the same GUI support |
|
45 | # map to the same GUI support | |
42 |
backend2gui[ |
|
46 | backend2gui["GTK"] = backend2gui["GTKCairo"] = "gtk" | |
43 |
backend2gui[ |
|
47 | backend2gui["GTK3Cairo"] = "gtk3" | |
44 | backend2gui['WX'] = 'wx' |
|
48 | backend2gui["GTK4Cairo"] = "gtk4" | |
45 |
backend2gui[ |
|
49 | backend2gui["WX"] = "wx" | |
|
50 | backend2gui["CocoaAgg"] = "osx" | |||
46 | # And some backends that don't need GUI integration |
|
51 | # And some backends that don't need GUI integration | |
47 |
del backend2gui[ |
|
52 | del backend2gui["nbAgg"] | |
48 |
del backend2gui[ |
|
53 | del backend2gui["agg"] | |
49 |
del backend2gui[ |
|
54 | del backend2gui["svg"] | |
50 |
del backend2gui[ |
|
55 | del backend2gui["pdf"] | |
51 |
del backend2gui[ |
|
56 | del backend2gui["ps"] | |
52 |
del backend2gui[ |
|
57 | del backend2gui["module://matplotlib_inline.backend_inline"] | |
53 |
|
58 | |||
54 | #----------------------------------------------------------------------------- |
|
59 | #----------------------------------------------------------------------------- | |
55 | # Matplotlib utilities |
|
60 | # Matplotlib utilities | |
@@ -274,7 +279,7 b' def find_gui_and_backend(gui=None, gui_select=None):' | |||||
274 | Returns |
|
279 | Returns | |
275 | ------- |
|
280 | ------- | |
276 | A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg', |
|
281 | A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg', | |
277 |
'WXAgg','Qt4Agg','module:// |
|
282 | 'WXAgg','Qt4Agg','module://matplotlib_inline.backend_inline','agg'). | |
278 | """ |
|
283 | """ | |
279 |
|
284 | |||
280 | import matplotlib |
|
285 | import matplotlib | |
@@ -361,7 +366,12 b' def import_pylab(user_ns, import_all=True):' | |||||
361 |
|
366 | |||
362 |
|
367 | |||
363 | def configure_inline_support(shell, backend): |
|
368 | def configure_inline_support(shell, backend): | |
364 | """Configure an IPython shell object for matplotlib use. |
|
369 | """ | |
|
370 | .. deprecated: 7.23 | |||
|
371 | ||||
|
372 | use `matplotlib_inline.backend_inline.configure_inline_support()` | |||
|
373 | ||||
|
374 | Configure an IPython shell object for matplotlib use. | |||
365 |
|
375 | |||
366 | Parameters |
|
376 | Parameters | |
367 | ---------- |
|
377 | ---------- | |
@@ -369,51 +379,15 b' def configure_inline_support(shell, backend):' | |||||
369 |
|
379 | |||
370 | backend : matplotlib backend |
|
380 | backend : matplotlib backend | |
371 | """ |
|
381 | """ | |
372 | # If using our svg payload backend, register the post-execution |
|
382 | warnings.warn( | |
373 | # function that will pick up the results for display. This can only be |
|
383 | "`configure_inline_support` is deprecated since IPython 7.23, directly " | |
374 | # done with access to the real shell object. |
|
384 | "use `matplotlib_inline.backend_inline.configure_inline_support()`", | |
|
385 | DeprecationWarning, | |||
|
386 | stacklevel=2, | |||
|
387 | ) | |||
375 |
|
388 | |||
376 | # Note: if we can't load the inline backend, then there's no point |
|
389 | from matplotlib_inline.backend_inline import ( | |
377 | # continuing (such as in terminal-only shells in environments without |
|
390 | configure_inline_support as configure_inline_support_orig, | |
378 | # zeromq available). |
|
391 | ) | |
379 | try: |
|
|||
380 | from ipykernel.pylab.backend_inline import InlineBackend |
|
|||
381 | except ImportError: |
|
|||
382 | return |
|
|||
383 | import matplotlib |
|
|||
384 |
|
392 | |||
385 | cfg = InlineBackend.instance(parent=shell) |
|
393 | configure_inline_support_orig(shell, backend) | |
386 | cfg.shell = shell |
|
|||
387 | if cfg not in shell.configurables: |
|
|||
388 | shell.configurables.append(cfg) |
|
|||
389 |
|
||||
390 | if backend == backends['inline']: |
|
|||
391 | from ipykernel.pylab.backend_inline import flush_figures |
|
|||
392 | shell.events.register('post_execute', flush_figures) |
|
|||
393 |
|
||||
394 | # Save rcParams that will be overwrittern |
|
|||
395 | shell._saved_rcParams = {} |
|
|||
396 | for k in cfg.rc: |
|
|||
397 | shell._saved_rcParams[k] = matplotlib.rcParams[k] |
|
|||
398 | # load inline_rc |
|
|||
399 | matplotlib.rcParams.update(cfg.rc) |
|
|||
400 | new_backend_name = "inline" |
|
|||
401 | else: |
|
|||
402 | from ipykernel.pylab.backend_inline import flush_figures |
|
|||
403 | try: |
|
|||
404 | shell.events.unregister('post_execute', flush_figures) |
|
|||
405 | except ValueError: |
|
|||
406 | pass |
|
|||
407 | if hasattr(shell, '_saved_rcParams'): |
|
|||
408 | matplotlib.rcParams.update(shell._saved_rcParams) |
|
|||
409 | del shell._saved_rcParams |
|
|||
410 | new_backend_name = "other" |
|
|||
411 |
|
||||
412 | # only enable the formats once -> don't change the enabled formats (which the user may |
|
|||
413 | # has changed) when getting another "%matplotlib inline" call. |
|
|||
414 | # See https://github.com/ipython/ipykernel/issues/29 |
|
|||
415 | cur_backend = getattr(configure_inline_support, "current_backend", "unset") |
|
|||
416 | if new_backend_name != cur_backend: |
|
|||
417 | # Setup the default figure format |
|
|||
418 | select_figure_formats(shell, cfg.figure_formats, **cfg.print_figure_kwargs) |
|
|||
419 | configure_inline_support.current_backend = new_backend_name |
|
@@ -1,6 +1,6 b'' | |||||
1 | from IPython.utils.capture import capture_output |
|
1 | from IPython.utils.capture import capture_output | |
2 |
|
2 | |||
3 | import nose.tools as nt |
|
3 | import pytest | |
4 |
|
4 | |||
5 | def test_alias_lifecycle(): |
|
5 | def test_alias_lifecycle(): | |
6 | name = 'test_alias1' |
|
6 | name = 'test_alias1' | |
@@ -9,8 +9,8 b' def test_alias_lifecycle():' | |||||
9 | am.clear_aliases() |
|
9 | am.clear_aliases() | |
10 | am.define_alias(name, cmd) |
|
10 | am.define_alias(name, cmd) | |
11 | assert am.is_alias(name) |
|
11 | assert am.is_alias(name) | |
12 |
|
|
12 | assert am.retrieve_alias(name) == cmd | |
13 |
|
|
13 | assert (name, cmd) in am.aliases | |
14 |
|
14 | |||
15 | # Test running the alias |
|
15 | # Test running the alias | |
16 | orig_system = _ip.system |
|
16 | orig_system = _ip.system | |
@@ -19,16 +19,16 b' def test_alias_lifecycle():' | |||||
19 | try: |
|
19 | try: | |
20 | _ip.run_cell('%{}'.format(name)) |
|
20 | _ip.run_cell('%{}'.format(name)) | |
21 | result = [c.strip() for c in result] |
|
21 | result = [c.strip() for c in result] | |
22 |
|
|
22 | assert result == [cmd] | |
23 | finally: |
|
23 | finally: | |
24 | _ip.system = orig_system |
|
24 | _ip.system = orig_system | |
25 |
|
25 | |||
26 | # Test removing the alias |
|
26 | # Test removing the alias | |
27 | am.undefine_alias(name) |
|
27 | am.undefine_alias(name) | |
28 | assert not am.is_alias(name) |
|
28 | assert not am.is_alias(name) | |
29 |
with |
|
29 | with pytest.raises(ValueError): | |
30 | am.retrieve_alias(name) |
|
30 | am.retrieve_alias(name) | |
31 |
|
|
31 | assert (name, cmd) not in am.aliases | |
32 |
|
32 | |||
33 |
|
33 | |||
34 | def test_alias_args_error(): |
|
34 | def test_alias_args_error(): | |
@@ -38,7 +38,8 b' def test_alias_args_error():' | |||||
38 | with capture_output() as cap: |
|
38 | with capture_output() as cap: | |
39 | _ip.run_cell('parts 1') |
|
39 | _ip.run_cell('parts 1') | |
40 |
|
40 | |||
41 |
|
|
41 | assert cap.stderr.split(":")[0] == "UsageError" | |
|
42 | ||||
42 |
|
43 | |||
43 | def test_alias_args_commented(): |
|
44 | def test_alias_args_commented(): | |
44 | """Check that alias correctly ignores 'commented out' args""" |
|
45 | """Check that alias correctly ignores 'commented out' args""" | |
@@ -62,4 +63,4 b' def test_alias_args_commented_nargs():' | |||||
62 | assert am.is_alias(alias_name) |
|
63 | assert am.is_alias(alias_name) | |
63 |
|
64 | |||
64 | thealias = am.get_alias(alias_name) |
|
65 | thealias = am.get_alias(alias_name) | |
65 |
|
|
66 | assert thealias.nargs == 1 |
@@ -18,7 +18,7 b' import linecache' | |||||
18 | import sys |
|
18 | import sys | |
19 |
|
19 | |||
20 | # Third-party imports |
|
20 | # Third-party imports | |
21 | import nose.tools as nt |
|
21 | import pytest | |
22 |
|
22 | |||
23 | # Our own imports |
|
23 | # Our own imports | |
24 | from IPython.core import compilerop |
|
24 | from IPython.core import compilerop | |
@@ -30,13 +30,13 b' from IPython.core import compilerop' | |||||
30 | def test_code_name(): |
|
30 | def test_code_name(): | |
31 | code = 'x=1' |
|
31 | code = 'x=1' | |
32 | name = compilerop.code_name(code) |
|
32 | name = compilerop.code_name(code) | |
33 |
|
|
33 | assert name.startswith("<ipython-input-0") | |
34 |
|
34 | |||
35 |
|
35 | |||
36 | def test_code_name2(): |
|
36 | def test_code_name2(): | |
37 | code = 'x=1' |
|
37 | code = 'x=1' | |
38 | name = compilerop.code_name(code, 9) |
|
38 | name = compilerop.code_name(code, 9) | |
39 |
|
|
39 | assert name.startswith("<ipython-input-9") | |
40 |
|
40 | |||
41 |
|
41 | |||
42 | def test_cache(): |
|
42 | def test_cache(): | |
@@ -45,18 +45,18 b' def test_cache():' | |||||
45 | cp = compilerop.CachingCompiler() |
|
45 | cp = compilerop.CachingCompiler() | |
46 | ncache = len(linecache.cache) |
|
46 | ncache = len(linecache.cache) | |
47 | cp.cache('x=1') |
|
47 | cp.cache('x=1') | |
48 |
|
|
48 | assert len(linecache.cache) > ncache | |
49 |
|
49 | |||
50 | def test_proper_default_encoding(): |
|
50 | def test_proper_default_encoding(): | |
51 | # Check we're in a proper Python 2 environment (some imports, such |
|
51 | # Check we're in a proper Python 2 environment (some imports, such | |
52 | # as GTK, can change the default encoding, which can hide bugs.) |
|
52 | # as GTK, can change the default encoding, which can hide bugs.) | |
53 |
|
|
53 | assert sys.getdefaultencoding() == "utf-8" | |
54 |
|
54 | |||
55 | def test_cache_unicode(): |
|
55 | def test_cache_unicode(): | |
56 | cp = compilerop.CachingCompiler() |
|
56 | cp = compilerop.CachingCompiler() | |
57 | ncache = len(linecache.cache) |
|
57 | ncache = len(linecache.cache) | |
58 | cp.cache(u"t = 'žćčšđ'") |
|
58 | cp.cache(u"t = 'žćčšđ'") | |
59 |
|
|
59 | assert len(linecache.cache) > ncache | |
60 |
|
60 | |||
61 | def test_compiler_check_cache(): |
|
61 | def test_compiler_check_cache(): | |
62 | """Test the compiler properly manages the cache. |
|
62 | """Test the compiler properly manages the cache. |
@@ -157,6 +157,11 b' def test_bad_module_all():' | |||||
157 | nt.assert_in('puppies', results) |
|
157 | nt.assert_in('puppies', results) | |
158 | for r in results: |
|
158 | for r in results: | |
159 | nt.assert_is_instance(r, str) |
|
159 | nt.assert_is_instance(r, str) | |
|
160 | ||||
|
161 | # bad_all doesn't contain submodules, but this completion | |||
|
162 | # should finish without raising an exception: | |||
|
163 | results = module_completion("import bad_all.") | |||
|
164 | nt.assert_equal(results, []) | |||
160 | finally: |
|
165 | finally: | |
161 | sys.path.remove(testsdir) |
|
166 | sys.path.remove(testsdir) | |
162 |
|
167 | |||
@@ -176,3 +181,14 b' def test_module_without_init():' | |||||
176 | assert s == [] |
|
181 | assert s == [] | |
177 | finally: |
|
182 | finally: | |
178 | sys.path.remove(tmpdir) |
|
183 | sys.path.remove(tmpdir) | |
|
184 | ||||
|
185 | ||||
|
186 | def test_valid_exported_submodules(): | |||
|
187 | """ | |||
|
188 | Test checking exported (__all__) objects are submodules | |||
|
189 | """ | |||
|
190 | results = module_completion("import os.pa") | |||
|
191 | # ensure we get a valid submodule: | |||
|
192 | nt.assert_in("os.path", results) | |||
|
193 | # ensure we don't get objects that aren't submodules: | |||
|
194 | nt.assert_not_in("os.pathconf", results) |
@@ -321,3 +321,70 b' f()' | |||||
321 | child.expect("ipdb>") |
|
321 | child.expect("ipdb>") | |
322 |
|
322 | |||
323 | child.close() |
|
323 | child.close() | |
|
324 | ||||
|
325 | ||||
|
326 | @skip_win32 | |||
|
327 | def test_where_erase_value(): | |||
|
328 | """Test that `where` does not access f_locals and erase values.""" | |||
|
329 | import pexpect | |||
|
330 | ||||
|
331 | env = os.environ.copy() | |||
|
332 | env["IPY_TEST_SIMPLE_PROMPT"] = "1" | |||
|
333 | ||||
|
334 | child = pexpect.spawn( | |||
|
335 | sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env | |||
|
336 | ) | |||
|
337 | child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE | |||
|
338 | ||||
|
339 | child.expect("IPython") | |||
|
340 | child.expect("\n") | |||
|
341 | child.expect_exact("In [1]") | |||
|
342 | ||||
|
343 | block = dedent( | |||
|
344 | """ | |||
|
345 | def simple_f(): | |||
|
346 | myvar = 1 | |||
|
347 | print(myvar) | |||
|
348 | 1/0 | |||
|
349 | print(myvar) | |||
|
350 | simple_f() """ | |||
|
351 | ) | |||
|
352 | ||||
|
353 | for line in block.splitlines(): | |||
|
354 | child.sendline(line) | |||
|
355 | child.expect_exact(line) | |||
|
356 | child.expect_exact("ZeroDivisionError") | |||
|
357 | child.expect_exact("In [2]:") | |||
|
358 | ||||
|
359 | child.sendline("%debug") | |||
|
360 | ||||
|
361 | ## | |||
|
362 | child.expect("ipdb>") | |||
|
363 | ||||
|
364 | child.sendline("myvar") | |||
|
365 | child.expect("1") | |||
|
366 | ||||
|
367 | ## | |||
|
368 | child.expect("ipdb>") | |||
|
369 | ||||
|
370 | child.sendline("myvar = 2") | |||
|
371 | ||||
|
372 | ## | |||
|
373 | child.expect_exact("ipdb>") | |||
|
374 | ||||
|
375 | child.sendline("myvar") | |||
|
376 | ||||
|
377 | child.expect_exact("2") | |||
|
378 | ||||
|
379 | ## | |||
|
380 | child.expect("ipdb>") | |||
|
381 | child.sendline("where") | |||
|
382 | ||||
|
383 | ## | |||
|
384 | child.expect("ipdb>") | |||
|
385 | child.sendline("myvar") | |||
|
386 | ||||
|
387 | child.expect_exact("2") | |||
|
388 | child.expect("ipdb>") | |||
|
389 | ||||
|
390 | child.close() |
@@ -135,7 +135,7 b' def test_image_filename_defaults():' | |||||
135 | nt.assert_is_none(img._repr_jpeg_()) |
|
135 | nt.assert_is_none(img._repr_jpeg_()) | |
136 |
|
136 | |||
137 | def _get_inline_config(): |
|
137 | def _get_inline_config(): | |
138 |
from |
|
138 | from matplotlib_inline.config import InlineBackend | |
139 | return InlineBackend.instance() |
|
139 | return InlineBackend.instance() | |
140 |
|
140 | |||
141 |
|
141 | |||
@@ -183,7 +183,7 b' def test_set_matplotlib_formats_kwargs():' | |||||
183 | ip = get_ipython() |
|
183 | ip = get_ipython() | |
184 | cfg = _get_inline_config() |
|
184 | cfg = _get_inline_config() | |
185 | cfg.print_figure_kwargs.update(dict(foo='bar')) |
|
185 | cfg.print_figure_kwargs.update(dict(foo='bar')) | |
186 |
kwargs = dict( |
|
186 | kwargs = dict(dpi=150) | |
187 | display.set_matplotlib_formats('png', **kwargs) |
|
187 | display.set_matplotlib_formats('png', **kwargs) | |
188 | formatter = ip.display_formatter.formatters['image/png'] |
|
188 | formatter = ip.display_formatter.formatters['image/png'] | |
189 | f = formatter.lookup_by_type(Figure) |
|
189 | f = formatter.lookup_by_type(Figure) | |
@@ -457,6 +457,7 b' def test_display_handle():' | |||||
457 | 'update': True, |
|
457 | 'update': True, | |
458 | }) |
|
458 | }) | |
459 |
|
459 | |||
|
460 | ||||
460 | def test_image_alt_tag(): |
|
461 | def test_image_alt_tag(): | |
461 | """Simple test for display.Image(args, alt=x,)""" |
|
462 | """Simple test for display.Image(args, alt=x,)""" | |
462 | thisurl = "http://example.com/image.png" |
|
463 | thisurl = "http://example.com/image.png" | |
@@ -480,3 +481,8 b' def test_image_alt_tag():' | |||||
480 | nt.assert_equal(img.alt, "an image") |
|
481 | nt.assert_equal(img.alt, "an image") | |
481 | _, md = img._repr_png_() |
|
482 | _, md = img._repr_png_() | |
482 | nt.assert_equal(md["alt"], "an image") |
|
483 | nt.assert_equal(md["alt"], "an image") | |
|
484 | ||||
|
485 | ||||
|
486 | @nt.raises(FileNotFoundError) | |||
|
487 | def test_image_bad_filename_raises_proper_exception(): | |||
|
488 | display.Image("/this/file/does/not/exist/")._repr_png_() |
@@ -48,16 +48,16 b' def foo_printer(obj, pp, cycle):' | |||||
48 | def test_pretty(): |
|
48 | def test_pretty(): | |
49 | f = PlainTextFormatter() |
|
49 | f = PlainTextFormatter() | |
50 | f.for_type(A, foo_printer) |
|
50 | f.for_type(A, foo_printer) | |
51 |
|
|
51 | assert f(A()) == "foo" | |
52 |
|
|
52 | assert f(B()) == "B()" | |
53 |
|
|
53 | assert f(GoodPretty()) == "foo" | |
54 | # Just don't raise an exception for the following: |
|
54 | # Just don't raise an exception for the following: | |
55 | f(BadPretty()) |
|
55 | f(BadPretty()) | |
56 |
|
56 | |||
57 | f.pprint = False |
|
57 | f.pprint = False | |
58 |
|
|
58 | assert f(A()) == "A()" | |
59 |
|
|
59 | assert f(B()) == "B()" | |
60 |
|
|
60 | assert f(GoodPretty()) == "GoodPretty()" | |
61 |
|
61 | |||
62 |
|
62 | |||
63 | def test_deferred(): |
|
63 | def test_deferred(): | |
@@ -66,29 +66,30 b' def test_deferred():' | |||||
66 | def test_precision(): |
|
66 | def test_precision(): | |
67 | """test various values for float_precision.""" |
|
67 | """test various values for float_precision.""" | |
68 | f = PlainTextFormatter() |
|
68 | f = PlainTextFormatter() | |
69 |
|
|
69 | assert f(pi) == repr(pi) | |
70 | f.float_precision = 0 |
|
70 | f.float_precision = 0 | |
71 | if numpy: |
|
71 | if numpy: | |
72 | po = numpy.get_printoptions() |
|
72 | po = numpy.get_printoptions() | |
73 |
|
|
73 | assert po["precision"] == 0 | |
74 |
|
|
74 | assert f(pi) == "3" | |
75 | f.float_precision = 2 |
|
75 | f.float_precision = 2 | |
76 | if numpy: |
|
76 | if numpy: | |
77 | po = numpy.get_printoptions() |
|
77 | po = numpy.get_printoptions() | |
78 |
|
|
78 | assert po["precision"] == 2 | |
79 |
|
|
79 | assert f(pi) == "3.14" | |
80 |
f.float_precision = |
|
80 | f.float_precision = "%g" | |
81 | if numpy: |
|
81 | if numpy: | |
82 | po = numpy.get_printoptions() |
|
82 | po = numpy.get_printoptions() | |
83 |
|
|
83 | assert po["precision"] == 2 | |
84 |
|
|
84 | assert f(pi) == "3.14159" | |
85 |
f.float_precision = |
|
85 | f.float_precision = "%e" | |
86 |
|
|
86 | assert f(pi) == "3.141593e+00" | |
87 |
f.float_precision = |
|
87 | f.float_precision = "" | |
88 | if numpy: |
|
88 | if numpy: | |
89 | po = numpy.get_printoptions() |
|
89 | po = numpy.get_printoptions() | |
90 |
|
|
90 | assert po["precision"] == 8 | |
91 |
|
|
91 | assert f(pi) == repr(pi) | |
|
92 | ||||
92 |
|
93 | |||
93 | def test_bad_precision(): |
|
94 | def test_bad_precision(): | |
94 | """test various invalid values for float_precision.""" |
|
95 | """test various invalid values for float_precision.""" | |
@@ -260,8 +261,9 b' def test_nowarn_notimplemented():' | |||||
260 | with capture_output() as captured: |
|
261 | with capture_output() as captured: | |
261 | result = f(h) |
|
262 | result = f(h) | |
262 | nt.assert_is(result, None) |
|
263 | nt.assert_is(result, None) | |
263 |
|
|
264 | assert "" == captured.stderr | |
264 |
|
|
265 | assert "" == captured.stdout | |
|
266 | ||||
265 |
|
267 | |||
266 | def test_warn_error_for_type(): |
|
268 | def test_warn_error_for_type(): | |
267 | f = HTMLFormatter() |
|
269 | f = HTMLFormatter() | |
@@ -307,7 +309,8 b' class MakePDF(object):' | |||||
307 | def test_pdf_formatter(): |
|
309 | def test_pdf_formatter(): | |
308 | pdf = MakePDF() |
|
310 | pdf = MakePDF() | |
309 | f = PDFFormatter() |
|
311 | f = PDFFormatter() | |
310 |
|
|
312 | assert f(pdf) == "PDF" | |
|
313 | ||||
311 |
|
314 | |||
312 | def test_print_method_bound(): |
|
315 | def test_print_method_bound(): | |
313 | f = HTMLFormatter() |
|
316 | f = HTMLFormatter() | |
@@ -321,8 +324,9 b' def test_print_method_bound():' | |||||
321 |
|
324 | |||
322 | with capture_output() as captured: |
|
325 | with capture_output() as captured: | |
323 | result = f(MyHTML()) |
|
326 | result = f(MyHTML()) | |
324 |
|
|
327 | assert result == "hello" | |
325 |
|
|
328 | assert captured.stderr == "" | |
|
329 | ||||
326 |
|
330 | |||
327 | def test_print_method_weird(): |
|
331 | def test_print_method_weird(): | |
328 |
|
332 | |||
@@ -333,7 +337,7 b' def test_print_method_weird():' | |||||
333 | f = HTMLFormatter() |
|
337 | f = HTMLFormatter() | |
334 |
|
338 | |||
335 | text_hat = TextMagicHat() |
|
339 | text_hat = TextMagicHat() | |
336 |
|
|
340 | assert text_hat._repr_html_ == "_repr_html_" | |
337 | with capture_output() as captured: |
|
341 | with capture_output() as captured: | |
338 | result = f(text_hat) |
|
342 | result = f(text_hat) | |
339 |
|
343 | |||
@@ -348,7 +352,7 b' def test_print_method_weird():' | |||||
348 | with capture_output() as captured: |
|
352 | with capture_output() as captured: | |
349 | result = f(call_hat) |
|
353 | result = f(call_hat) | |
350 |
|
354 | |||
351 |
|
|
355 | assert result is None | |
352 |
|
356 | |||
353 | class BadReprArgs(object): |
|
357 | class BadReprArgs(object): | |
354 | def _repr_html_(self, extra, args): |
|
358 | def _repr_html_(self, extra, args): | |
@@ -369,24 +373,25 b' def test_format_config():' | |||||
369 | with capture_output() as captured: |
|
373 | with capture_output() as captured: | |
370 | result = f(cfg) |
|
374 | result = f(cfg) | |
371 | nt.assert_is(result, None) |
|
375 | nt.assert_is(result, None) | |
372 |
|
|
376 | assert captured.stderr == "" | |
373 |
|
377 | |||
374 | with capture_output() as captured: |
|
378 | with capture_output() as captured: | |
375 | result = f(Config) |
|
379 | result = f(Config) | |
376 | nt.assert_is(result, None) |
|
380 | nt.assert_is(result, None) | |
377 |
|
|
381 | assert captured.stderr == "" | |
|
382 | ||||
378 |
|
383 | |||
379 | def test_pretty_max_seq_length(): |
|
384 | def test_pretty_max_seq_length(): | |
380 | f = PlainTextFormatter(max_seq_length=1) |
|
385 | f = PlainTextFormatter(max_seq_length=1) | |
381 | lis = list(range(3)) |
|
386 | lis = list(range(3)) | |
382 | text = f(lis) |
|
387 | text = f(lis) | |
383 |
|
|
388 | assert text == "[0, ...]" | |
384 | f.max_seq_length = 0 |
|
389 | f.max_seq_length = 0 | |
385 | text = f(lis) |
|
390 | text = f(lis) | |
386 |
|
|
391 | assert text == "[0, 1, 2]" | |
387 | text = f(list(range(1024))) |
|
392 | text = f(list(range(1024))) | |
388 | lines = text.splitlines() |
|
393 | lines = text.splitlines() | |
389 |
|
|
394 | assert len(lines) == 1024 | |
390 |
|
395 | |||
391 |
|
396 | |||
392 | def test_ipython_display_formatter(): |
|
397 | def test_ipython_display_formatter(): | |
@@ -411,14 +416,14 b' def test_ipython_display_formatter():' | |||||
411 | no = NotSelfDisplaying() |
|
416 | no = NotSelfDisplaying() | |
412 |
|
417 | |||
413 | d, md = f.format(no) |
|
418 | d, md = f.format(no) | |
414 |
|
|
419 | assert d == {"text/plain": repr(no)} | |
415 |
|
|
420 | assert md == {} | |
416 |
|
|
421 | assert catcher == [] | |
417 |
|
422 | |||
418 | d, md = f.format(yes) |
|
423 | d, md = f.format(yes) | |
419 |
|
|
424 | assert d == {} | |
420 |
|
|
425 | assert md == {} | |
421 |
|
|
426 | assert catcher == [yes] | |
422 |
|
427 | |||
423 | f.ipython_display_formatter.enabled = save_enabled |
|
428 | f.ipython_display_formatter.enabled = save_enabled | |
424 |
|
429 | |||
@@ -431,8 +436,8 b' def test_json_as_string_deprecated():' | |||||
431 | f = JSONFormatter() |
|
436 | f = JSONFormatter() | |
432 | with warnings.catch_warnings(record=True) as w: |
|
437 | with warnings.catch_warnings(record=True) as w: | |
433 | d = f(JSONString()) |
|
438 | d = f(JSONString()) | |
434 |
|
|
439 | assert d == {} | |
435 |
|
|
440 | assert len(w) == 1 | |
436 |
|
441 | |||
437 |
|
442 | |||
438 | def test_repr_mime(): |
|
443 | def test_repr_mime(): | |
@@ -459,18 +464,21 b' def test_repr_mime():' | |||||
459 | d, md = f.format(obj) |
|
464 | d, md = f.format(obj) | |
460 | html_f.enabled = save_enabled |
|
465 | html_f.enabled = save_enabled | |
461 |
|
466 | |||
462 | nt.assert_equal(sorted(d), ['application/json+test.v2', |
|
467 | assert sorted(d) == [ | |
463 | 'image/png', |
|
468 | "application/json+test.v2", | |
464 | 'plain/text', |
|
469 | "image/png", | |
465 | 'text/html', |
|
470 | "plain/text", | |
466 | 'text/plain']) |
|
471 | "text/html", | |
467 | nt.assert_equal(md, {}) |
|
472 | "text/plain", | |
|
473 | ] | |||
|
474 | assert md == {} | |||
468 |
|
475 | |||
469 |
d, md = f.format(obj, include={ |
|
476 | d, md = f.format(obj, include={"image/png"}) | |
470 |
|
|
477 | assert list(d.keys()) == [ | |
471 | 'Include should filter out even things from repr_mimebundle') |
|
478 | "image/png" | |
472 | nt.assert_equal(d['image/png'], 'i-overwrite', '_repr_mimebundle_ take precedence') |
|
479 | ], "Include should filter out even things from repr_mimebundle" | |
473 |
|
480 | |||
|
481 | assert d["image/png"] == "i-overwrite", "_repr_mimebundle_ take precedence" | |||
474 |
|
482 | |||
475 |
|
483 | |||
476 | def test_pass_correct_include_exclude(): |
|
484 | def test_pass_correct_include_exclude(): | |
@@ -514,13 +522,14 b' def test_repr_mime_meta():' | |||||
514 | f = get_ipython().display_formatter |
|
522 | f = get_ipython().display_formatter | |
515 | obj = HasReprMimeMeta() |
|
523 | obj = HasReprMimeMeta() | |
516 | d, md = f.format(obj) |
|
524 | d, md = f.format(obj) | |
517 |
|
|
525 | assert sorted(d) == ["image/png", "text/plain"] | |
518 |
|
|
526 | assert md == { | |
519 |
|
|
527 | "image/png": { | |
520 |
|
|
528 | "width": 5, | |
521 |
|
|
529 | "height": 10, | |
522 | } |
|
530 | } | |
523 |
} |
|
531 | } | |
|
532 | ||||
524 |
|
533 | |||
525 | def test_repr_mime_failure(): |
|
534 | def test_repr_mime_failure(): | |
526 | class BadReprMime(object): |
|
535 | class BadReprMime(object): | |
@@ -530,4 +539,4 b' def test_repr_mime_failure():' | |||||
530 | f = get_ipython().display_formatter |
|
539 | f = get_ipython().display_formatter | |
531 | obj = BadReprMime() |
|
540 | obj = BadReprMime() | |
532 | d, md = f.format(obj) |
|
541 | d, md = f.format(obj) | |
533 |
|
|
542 | assert "text/plain" in d |
@@ -160,6 +160,14 b' def test_extract_hist_ranges():' | |||||
160 | actual = list(extract_hist_ranges(instr)) |
|
160 | actual = list(extract_hist_ranges(instr)) | |
161 | nt.assert_equal(actual, expected) |
|
161 | nt.assert_equal(actual, expected) | |
162 |
|
162 | |||
|
163 | ||||
|
164 | def test_extract_hist_ranges_empty_str(): | |||
|
165 | instr = "" | |||
|
166 | expected = [(0, 1, None)] # 0 == current session, None == to end | |||
|
167 | actual = list(extract_hist_ranges(instr)) | |||
|
168 | nt.assert_equal(actual, expected) | |||
|
169 | ||||
|
170 | ||||
163 | def test_magic_rerun(): |
|
171 | def test_magic_rerun(): | |
164 | """Simple test for %rerun (no args -> rerun last line)""" |
|
172 | """Simple test for %rerun (no args -> rerun last line)""" | |
165 | ip = get_ipython() |
|
173 | ip = get_ipython() |
@@ -255,18 +255,18 b' def test_find_assign_op_dedent():' | |||||
255 |
|
255 | |||
256 | def test_check_complete(): |
|
256 | def test_check_complete(): | |
257 | cc = ipt2.TransformerManager().check_complete |
|
257 | cc = ipt2.TransformerManager().check_complete | |
258 |
nt.assert_equal(cc("a = 1"), ( |
|
258 | nt.assert_equal(cc("a = 1"), ("complete", None)) | |
259 |
nt.assert_equal(cc("for a in range(5):"), ( |
|
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:"), ( |
|
260 | nt.assert_equal(cc("for a in range(5):\n if a > 0:"), ("incomplete", 8)) | |
261 |
nt.assert_equal(cc("raise = 2"), ( |
|
261 | nt.assert_equal(cc("raise = 2"), ("invalid", None)) | |
262 |
nt.assert_equal(cc("a = [1,\n2,"), ( |
|
262 | nt.assert_equal(cc("a = [1,\n2,"), ("incomplete", 0)) | |
263 |
nt.assert_equal(cc(")"), ( |
|
263 | nt.assert_equal(cc("(\n))"), ("incomplete", 0)) | |
264 |
nt.assert_equal(cc("\\\r\n"), ( |
|
264 | nt.assert_equal(cc("\\\r\n"), ("incomplete", 0)) | |
265 |
nt.assert_equal(cc("a = '''\n hi"), ( |
|
265 | nt.assert_equal(cc("a = '''\n hi"), ("incomplete", 3)) | |
266 |
nt.assert_equal(cc("def a():\n x=1\n global x"), ( |
|
266 | nt.assert_equal(cc("def a():\n x=1\n global x"), ("invalid", None)) | |
267 |
nt.assert_equal(cc("a \\ "), ( |
|
267 | nt.assert_equal(cc("a \\ "), ("invalid", None)) # Nothing allowed after backslash | |
268 |
nt.assert_equal(cc("1\\\n+2"), ( |
|
268 | nt.assert_equal(cc("1\\\n+2"), ("complete", None)) | |
269 |
nt.assert_equal(cc("exit"), ( |
|
269 | nt.assert_equal(cc("exit"), ("complete", None)) | |
270 |
|
270 | |||
271 | example = dedent(""" |
|
271 | example = dedent(""" | |
272 | if True: |
|
272 | if True: | |
@@ -297,6 +297,24 b' def test_check_complete_II():' | |||||
297 | nt.assert_equal(cc('''def foo():\n """'''), ('incomplete', 4)) |
|
297 | nt.assert_equal(cc('''def foo():\n """'''), ('incomplete', 4)) | |
298 |
|
298 | |||
299 |
|
299 | |||
|
300 | def test_check_complete_invalidates_sunken_brackets(): | |||
|
301 | """ | |||
|
302 | Test that a single line with more closing brackets than the opening ones is | |||
|
303 | interpretted as invalid | |||
|
304 | """ | |||
|
305 | cc = ipt2.TransformerManager().check_complete | |||
|
306 | nt.assert_equal(cc(")"), ("invalid", None)) | |||
|
307 | nt.assert_equal(cc("]"), ("invalid", None)) | |||
|
308 | nt.assert_equal(cc("}"), ("invalid", None)) | |||
|
309 | nt.assert_equal(cc(")("), ("invalid", None)) | |||
|
310 | nt.assert_equal(cc("]["), ("invalid", None)) | |||
|
311 | nt.assert_equal(cc("}{"), ("invalid", None)) | |||
|
312 | nt.assert_equal(cc("]()("), ("invalid", None)) | |||
|
313 | nt.assert_equal(cc("())("), ("invalid", None)) | |||
|
314 | nt.assert_equal(cc(")[]("), ("invalid", None)) | |||
|
315 | nt.assert_equal(cc("()]("), ("invalid", None)) | |||
|
316 | ||||
|
317 | ||||
300 | def test_null_cleanup_transformer(): |
|
318 | def test_null_cleanup_transformer(): | |
301 | manager = ipt2.TransformerManager() |
|
319 | manager = ipt2.TransformerManager() | |
302 | manager.cleanup_transforms.insert(0, null_cleanup_transformer) |
|
320 | manager.cleanup_transforms.insert(0, null_cleanup_transformer) |
@@ -3,7 +3,7 b'' | |||||
3 | Line-based transformers are the simpler ones; token-based transformers are |
|
3 | Line-based transformers are the simpler ones; token-based transformers are | |
4 | more complex. See test_inputtransformer2 for tests for token-based transformers. |
|
4 | more complex. See test_inputtransformer2 for tests for token-based transformers. | |
5 | """ |
|
5 | """ | |
6 | import nose.tools as nt |
|
6 | import pytest | |
7 |
|
7 | |||
8 | from IPython.core import inputtransformer2 as ipt2 |
|
8 | from IPython.core import inputtransformer2 as ipt2 | |
9 |
|
9 | |||
@@ -17,8 +17,9 b" get_ipython().run_cell_magic('foo', 'arg', 'body 1\\\\nbody 2\\\\n')" | |||||
17 |
|
17 | |||
18 | def test_cell_magic(): |
|
18 | def test_cell_magic(): | |
19 | for sample, expected in [CELL_MAGIC]: |
|
19 | for sample, expected in [CELL_MAGIC]: | |
20 |
|
|
20 | assert ipt2.cell_magic(sample.splitlines(keepends=True)) == expected.splitlines( | |
21 | expected.splitlines(keepends=True)) |
|
21 | keepends=True | |
|
22 | ) | |||
22 |
|
23 | |||
23 | CLASSIC_PROMPT = ("""\ |
|
24 | CLASSIC_PROMPT = ("""\ | |
24 | >>> for a in range(5): |
|
25 | >>> for a in range(5): | |
@@ -40,8 +41,9 b' for a in range(5):' | |||||
40 |
|
41 | |||
41 | def test_classic_prompt(): |
|
42 | def test_classic_prompt(): | |
42 | for sample, expected in [CLASSIC_PROMPT, CLASSIC_PROMPT_L2]: |
|
43 | for sample, expected in [CLASSIC_PROMPT, CLASSIC_PROMPT_L2]: | |
43 |
|
|
44 | assert ipt2.classic_prompt( | |
44 |
|
|
45 | sample.splitlines(keepends=True) | |
|
46 | ) == expected.splitlines(keepends=True) | |||
45 |
|
47 | |||
46 | IPYTHON_PROMPT = ("""\ |
|
48 | IPYTHON_PROMPT = ("""\ | |
47 | In [1]: for a in range(5): |
|
49 | In [1]: for a in range(5): | |
@@ -61,10 +63,49 b' for a in range(5):' | |||||
61 | print(a ** 2) |
|
63 | print(a ** 2) | |
62 | """) |
|
64 | """) | |
63 |
|
65 | |||
|
66 | ||||
|
67 | IPYTHON_PROMPT_VI_INS = ( | |||
|
68 | """\ | |||
|
69 | [ins] In [11]: def a(): | |||
|
70 | ...: 123 | |||
|
71 | ...: | |||
|
72 | ...: 123 | |||
|
73 | """, | |||
|
74 | """\ | |||
|
75 | def a(): | |||
|
76 | 123 | |||
|
77 | ||||
|
78 | 123 | |||
|
79 | """, | |||
|
80 | ) | |||
|
81 | ||||
|
82 | IPYTHON_PROMPT_VI_NAV = ( | |||
|
83 | """\ | |||
|
84 | [nav] In [11]: def a(): | |||
|
85 | ...: 123 | |||
|
86 | ...: | |||
|
87 | ...: 123 | |||
|
88 | """, | |||
|
89 | """\ | |||
|
90 | def a(): | |||
|
91 | 123 | |||
|
92 | ||||
|
93 | 123 | |||
|
94 | """, | |||
|
95 | ) | |||
|
96 | ||||
|
97 | ||||
64 | def test_ipython_prompt(): |
|
98 | def test_ipython_prompt(): | |
65 |
for sample, expected in [ |
|
99 | for sample, expected in [ | |
66 | nt.assert_equal(ipt2.ipython_prompt(sample.splitlines(keepends=True)), |
|
100 | IPYTHON_PROMPT, | |
67 | expected.splitlines(keepends=True)) |
|
101 | IPYTHON_PROMPT_L2, | |
|
102 | IPYTHON_PROMPT_VI_INS, | |||
|
103 | IPYTHON_PROMPT_VI_NAV, | |||
|
104 | ]: | |||
|
105 | assert ipt2.ipython_prompt( | |||
|
106 | sample.splitlines(keepends=True) | |||
|
107 | ) == expected.splitlines(keepends=True) | |||
|
108 | ||||
68 |
|
109 | |||
69 | INDENT_SPACES = ("""\ |
|
110 | INDENT_SPACES = ("""\ | |
70 | if True: |
|
111 | if True: | |
@@ -84,8 +125,9 b' if True:' | |||||
84 |
|
125 | |||
85 | def test_leading_indent(): |
|
126 | def test_leading_indent(): | |
86 | for sample, expected in [INDENT_SPACES, INDENT_TABS]: |
|
127 | for sample, expected in [INDENT_SPACES, INDENT_TABS]: | |
87 |
|
|
128 | assert ipt2.leading_indent( | |
88 |
|
|
129 | sample.splitlines(keepends=True) | |
|
130 | ) == expected.splitlines(keepends=True) | |||
89 |
|
131 | |||
90 | LEADING_EMPTY_LINES = ("""\ |
|
132 | LEADING_EMPTY_LINES = ("""\ | |
91 | \t |
|
133 | \t | |
@@ -111,9 +153,9 b' ONLY_EMPTY_LINES = ("""\\' | |||||
111 |
|
153 | |||
112 | def test_leading_empty_lines(): |
|
154 | def test_leading_empty_lines(): | |
113 | for sample, expected in [LEADING_EMPTY_LINES, ONLY_EMPTY_LINES]: |
|
155 | for sample, expected in [LEADING_EMPTY_LINES, ONLY_EMPTY_LINES]: | |
114 | nt.assert_equal( |
|
156 | assert ipt2.leading_empty_lines( | |
115 |
|
|
157 | sample.splitlines(keepends=True) | |
116 |
|
|
158 | ) == expected.splitlines(keepends=True) | |
117 |
|
159 | |||
118 | CRLF_MAGIC = ([ |
|
160 | CRLF_MAGIC = ([ | |
119 | "%%ls\r\n" |
|
161 | "%%ls\r\n" | |
@@ -123,4 +165,4 b' CRLF_MAGIC = ([' | |||||
123 |
|
165 | |||
124 | def test_crlf_magic(): |
|
166 | def test_crlf_magic(): | |
125 | for sample, expected in [CRLF_MAGIC]: |
|
167 | for sample, expected in [CRLF_MAGIC]: | |
126 |
|
|
168 | assert ipt2.cell_magic(sample) == expected |
@@ -449,6 +449,25 b' class InteractiveShellTestCase(unittest.TestCase):' | |||||
449 | # Reset the custom exception hook |
|
449 | # Reset the custom exception hook | |
450 | ip.set_custom_exc((), None) |
|
450 | ip.set_custom_exc((), None) | |
451 |
|
451 | |||
|
452 | @mock.patch("builtins.print") | |||
|
453 | def test_showtraceback_with_surrogates(self, mocked_print): | |||
|
454 | values = [] | |||
|
455 | ||||
|
456 | def mock_print_func(value, sep=" ", end="\n", file=sys.stdout, flush=False): | |||
|
457 | values.append(value) | |||
|
458 | if value == chr(0xD8FF): | |||
|
459 | raise UnicodeEncodeError("utf-8", chr(0xD8FF), 0, 1, "") | |||
|
460 | ||||
|
461 | # mock builtins.print | |||
|
462 | mocked_print.side_effect = mock_print_func | |||
|
463 | ||||
|
464 | # ip._showtraceback() is replaced in globalipapp.py. | |||
|
465 | # Call original method to test. | |||
|
466 | interactiveshell.InteractiveShell._showtraceback(ip, None, None, chr(0xD8FF)) | |||
|
467 | ||||
|
468 | self.assertEqual(mocked_print.call_count, 2) | |||
|
469 | self.assertEqual(values, [chr(0xD8FF), "\\ud8ff"]) | |||
|
470 | ||||
452 | def test_mktempfile(self): |
|
471 | def test_mktempfile(self): | |
453 | filename = ip.mktempfile() |
|
472 | filename = ip.mktempfile() | |
454 | # Check that we can open the file again on Windows |
|
473 | # Check that we can open the file again on Windows | |
@@ -581,9 +600,16 b' class TestSystemRaw(ExitCodeChecks):' | |||||
581 | try: |
|
600 | try: | |
582 | self.system("sleep 1 # wont happen") |
|
601 | self.system("sleep 1 # wont happen") | |
583 | except KeyboardInterrupt: |
|
602 | except KeyboardInterrupt: | |
584 | self.fail("system call should intercept " |
|
603 | self.fail( | |
585 | "keyboard interrupt from subprocess.call") |
|
604 | "system call should intercept " | |
586 | self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGINT) |
|
605 | "keyboard interrupt from subprocess.call" | |
|
606 | ) | |||
|
607 | self.assertEqual(ip.user_ns["_exit_code"], -signal.SIGINT) | |||
|
608 | ||||
|
609 | def test_magic_warnings(self): | |||
|
610 | for magic_cmd in ("ls", "pip", "conda", "cd"): | |||
|
611 | with self.assertWarnsRegex(Warning, "You executed the system command"): | |||
|
612 | ip.system_raw(magic_cmd) | |||
587 |
|
613 | |||
588 | # TODO: Exit codes are currently ignored on Windows. |
|
614 | # TODO: Exit codes are currently ignored on Windows. | |
589 | class TestSystemPipedExitCode(ExitCodeChecks): |
|
615 | class TestSystemPipedExitCode(ExitCodeChecks): |
@@ -5,7 +5,7 b'' | |||||
5 | #----------------------------------------------------------------------------- |
|
5 | #----------------------------------------------------------------------------- | |
6 |
|
6 | |||
7 | # third party |
|
7 | # third party | |
8 | import nose.tools as nt |
|
8 | import pytest | |
9 |
|
9 | |||
10 | # our own packages |
|
10 | # our own packages | |
11 |
|
11 | |||
@@ -31,8 +31,8 b' def test_reset():' | |||||
31 |
|
31 | |||
32 | # Finally, check that all namespaces have only as many variables as we |
|
32 | # Finally, check that all namespaces have only as many variables as we | |
33 | # expect to find in them: |
|
33 | # expect to find in them: | |
34 |
|
|
34 | assert len(ip.user_ns) == nvars_user_ns | |
35 |
|
|
35 | assert len(ip.user_ns_hidden) == nvars_hidden | |
36 |
|
36 | |||
37 |
|
37 | |||
38 | # Tests for reporting of exceptions in various modes, handling of SystemExit, |
|
38 | # Tests for reporting of exceptions in various modes, handling of SystemExit, | |
@@ -117,93 +117,103 b' ZeroDivisionError Traceback (most recent call last)' | |||||
117 | ZeroDivisionError: ... |
|
117 | ZeroDivisionError: ... | |
118 |
|
|
118 | """ | |
119 |
|
119 | |||
120 | def doctest_tb_sysexit(): |
|
|||
121 | """ |
|
|||
122 | In [17]: %xmode plain |
|
|||
123 | Exception reporting mode: Plain |
|
|||
124 |
|
||||
125 | In [18]: %run simpleerr.py exit |
|
|||
126 | An exception has occurred, use %tb to see the full traceback. |
|
|||
127 | SystemExit: (1, 'Mode = exit') |
|
|||
128 |
|
||||
129 | In [19]: %run simpleerr.py exit 2 |
|
|||
130 | An exception has occurred, use %tb to see the full traceback. |
|
|||
131 | SystemExit: (2, 'Mode = exit') |
|
|||
132 |
|
||||
133 | In [20]: %tb |
|
|||
134 | Traceback (most recent call last): |
|
|||
135 | File ... in <module> |
|
|||
136 | bar(mode) |
|
|||
137 | File ... line 22, in bar |
|
|||
138 | sysexit(stat, mode) |
|
|||
139 | File ... line 11, in sysexit |
|
|||
140 | raise SystemExit(stat, 'Mode = %s' % mode) |
|
|||
141 | SystemExit: (2, 'Mode = exit') |
|
|||
142 |
|
||||
143 | In [21]: %xmode context |
|
|||
144 | Exception reporting mode: Context |
|
|||
145 |
|
||||
146 | In [22]: %tb |
|
|||
147 | --------------------------------------------------------------------------- |
|
|||
148 | SystemExit Traceback (most recent call last) |
|
|||
149 | <BLANKLINE> |
|
|||
150 | ...<module> |
|
|||
151 | 29 except IndexError: |
|
|||
152 | 30 mode = 'div' |
|
|||
153 | ---> 32 bar(mode) |
|
|||
154 | <BLANKLINE> |
|
|||
155 | ...bar(mode) |
|
|||
156 | 20 except: |
|
|||
157 | 21 stat = 1 |
|
|||
158 | ---> 22 sysexit(stat, mode) |
|
|||
159 | 23 else: |
|
|||
160 | 24 raise ValueError('Unknown mode') |
|
|||
161 | <BLANKLINE> |
|
|||
162 | ...sysexit(stat, mode) |
|
|||
163 | 10 def sysexit(stat, mode): |
|
|||
164 | ---> 11 raise SystemExit(stat, 'Mode = %s' % mode) |
|
|||
165 | <BLANKLINE> |
|
|||
166 | SystemExit: (2, 'Mode = exit') |
|
|||
167 |
|
||||
168 | In [23]: %xmode verbose |
|
|||
169 | Exception reporting mode: Verbose |
|
|||
170 |
|
120 | |||
171 | In [24]: %tb |
|
121 | # TODO : Marc 2021 – this seem to fail due | |
172 | --------------------------------------------------------------------------- |
|
122 | # to upstream changes in CI for whatever reason. | |
173 | SystemExit Traceback (most recent call last) |
|
123 | # Commenting for now, to revive someday (maybe?) | |
174 | <BLANKLINE> |
|
124 | # nose won't work in 3.10 anyway and we'll have to disable iptest. | |
175 | ... in <module> |
|
125 | # thus this likely need to bemigrated to pytest. | |
176 | 29 except IndexError: |
|
126 | ||
177 | 30 mode = 'div' |
|
127 | ||
178 | ---> 32 bar(mode) |
|
128 | # def doctest_tb_sysexit(): | |
179 | mode = 'exit' |
|
129 | # """ | |
180 | <BLANKLINE> |
|
130 | # In [17]: %xmode plain | |
181 | ... in bar(mode='exit') |
|
131 | # Exception reporting mode: Plain | |
182 | 20 except: |
|
132 | # | |
183 | 21 stat = 1 |
|
133 | # In [18]: %run simpleerr.py exit | |
184 | ---> 22 sysexit(stat, mode) |
|
134 | # An exception has occurred, use %tb to see the full traceback. | |
185 | mode = 'exit' |
|
135 | # SystemExit: (1, 'Mode = exit') | |
186 | stat = 2 |
|
136 | # | |
187 | 23 else: |
|
137 | # In [19]: %run simpleerr.py exit 2 | |
188 | 24 raise ValueError('Unknown mode') |
|
138 | # An exception has occurred, use %tb to see the full traceback. | |
189 | <BLANKLINE> |
|
139 | # SystemExit: (2, 'Mode = exit') | |
190 | ... in sysexit(stat=2, mode='exit') |
|
140 | # | |
191 | 10 def sysexit(stat, mode): |
|
141 | # In [20]: %tb | |
192 | ---> 11 raise SystemExit(stat, 'Mode = %s' % mode) |
|
142 | # Traceback (most recent call last): | |
193 | stat = 2 |
|
143 | # File ... in <module> | |
194 | mode = 'exit' |
|
144 | # bar(mode) | |
195 | <BLANKLINE> |
|
145 | # File ... line 22, in bar | |
196 | SystemExit: (2, 'Mode = exit') |
|
146 | # sysexit(stat, mode) | |
197 | """ |
|
147 | # File ... line 11, in sysexit | |
|
148 | # raise SystemExit(stat, 'Mode = %s' % mode) | |||
|
149 | # SystemExit: (2, 'Mode = exit') | |||
|
150 | # | |||
|
151 | # In [21]: %xmode context | |||
|
152 | # Exception reporting mode: Context | |||
|
153 | # | |||
|
154 | # In [22]: %tb | |||
|
155 | # --------------------------------------------------------------------------- | |||
|
156 | # SystemExit Traceback (most recent call last) | |||
|
157 | # <BLANKLINE> | |||
|
158 | # ...<module> | |||
|
159 | # 29 except IndexError: | |||
|
160 | # 30 mode = 'div' | |||
|
161 | # ---> 32 bar(mode) | |||
|
162 | # <BLANKLINE> | |||
|
163 | # ...bar(mode) | |||
|
164 | # 20 except: | |||
|
165 | # 21 stat = 1 | |||
|
166 | # ---> 22 sysexit(stat, mode) | |||
|
167 | # 23 else: | |||
|
168 | # 24 raise ValueError('Unknown mode') | |||
|
169 | # <BLANKLINE> | |||
|
170 | # ...sysexit(stat, mode) | |||
|
171 | # 10 def sysexit(stat, mode): | |||
|
172 | # ---> 11 raise SystemExit(stat, 'Mode = %s' % mode) | |||
|
173 | # <BLANKLINE> | |||
|
174 | # SystemExit: (2, 'Mode = exit') | |||
|
175 | # | |||
|
176 | # In [23]: %xmode verbose | |||
|
177 | # Exception reporting mode: Verbose | |||
|
178 | # | |||
|
179 | # In [24]: %tb | |||
|
180 | # --------------------------------------------------------------------------- | |||
|
181 | # SystemExit Traceback (most recent call last) | |||
|
182 | # <BLANKLINE> | |||
|
183 | # ... in <module> | |||
|
184 | # 29 except IndexError: | |||
|
185 | # 30 mode = 'div' | |||
|
186 | # ---> 32 bar(mode) | |||
|
187 | # mode = 'exit' | |||
|
188 | # <BLANKLINE> | |||
|
189 | # ... in bar(mode='exit') | |||
|
190 | # 20 except: | |||
|
191 | # 21 stat = 1 | |||
|
192 | # ---> 22 sysexit(stat, mode) | |||
|
193 | # mode = 'exit' | |||
|
194 | # stat = 2 | |||
|
195 | # 23 else: | |||
|
196 | # 24 raise ValueError('Unknown mode') | |||
|
197 | # <BLANKLINE> | |||
|
198 | # ... in sysexit(stat=2, mode='exit') | |||
|
199 | # 10 def sysexit(stat, mode): | |||
|
200 | # ---> 11 raise SystemExit(stat, 'Mode = %s' % mode) | |||
|
201 | # stat = 2 | |||
|
202 | # mode = 'exit' | |||
|
203 | # <BLANKLINE> | |||
|
204 | # SystemExit: (2, 'Mode = exit') | |||
|
205 | # """ | |||
198 |
|
206 | |||
199 |
|
207 | |||
200 | def test_run_cell(): |
|
208 | def test_run_cell(): | |
201 | import textwrap |
|
209 | import textwrap | |
202 | ip.run_cell('a = 10\na+=1') |
|
|||
203 | ip.run_cell('assert a == 11\nassert 1') |
|
|||
204 |
|
210 | |||
205 | nt.assert_equal(ip.user_ns['a'], 11) |
|
211 | ip.run_cell("a = 10\na+=1") | |
206 | complex = textwrap.dedent(""" |
|
212 | ip.run_cell("assert a == 11\nassert 1") | |
|
213 | ||||
|
214 | assert ip.user_ns["a"] == 11 | |||
|
215 | complex = textwrap.dedent( | |||
|
216 | """ | |||
207 |
|
|
217 | if 1: | |
208 | print "hello" |
|
218 | print "hello" | |
209 |
|
|
219 | if 1: | |
@@ -225,7 +235,7 b' def test_run_cell():' | |||||
225 |
|
235 | |||
226 | def test_db(): |
|
236 | def test_db(): | |
227 | """Test the internal database used for variable persistence.""" |
|
237 | """Test the internal database used for variable persistence.""" | |
228 |
ip.db[ |
|
238 | ip.db["__unittest_"] = 12 | |
229 |
|
|
239 | assert ip.db["__unittest_"] == 12 | |
230 |
del ip.db[ |
|
240 | del ip.db["__unittest_"] | |
231 |
assert |
|
241 | assert "__unittest_" not in ip.db |
@@ -2,8 +2,8 b'' | |||||
2 | """Test IPython.core.logger""" |
|
2 | """Test IPython.core.logger""" | |
3 |
|
3 | |||
4 | import os.path |
|
4 | import os.path | |
|
5 | import pytest | |||
5 |
|
6 | |||
6 | import nose.tools as nt |
|
|||
7 | from IPython.utils.tempdir import TemporaryDirectory |
|
7 | from IPython.utils.tempdir import TemporaryDirectory | |
8 |
|
8 | |||
9 | def test_logstart_inaccessible_file(): |
|
9 | def test_logstart_inaccessible_file(): | |
@@ -12,7 +12,7 b' def test_logstart_inaccessible_file():' | |||||
12 | except IOError: |
|
12 | except IOError: | |
13 | pass |
|
13 | pass | |
14 | else: |
|
14 | else: | |
15 |
|
|
15 | assert False # The try block should never pass. | |
16 |
|
16 | |||
17 | try: |
|
17 | try: | |
18 | _ip.run_cell("a=1") # Check it doesn't try to log this |
|
18 | _ip.run_cell("a=1") # Check it doesn't try to log this |
@@ -472,6 +472,23 b' def test_parse_options():' | |||||
472 | nt.assert_equal(m.parse_options(u'foo', '')[1], u'foo') |
|
472 | nt.assert_equal(m.parse_options(u'foo', '')[1], u'foo') | |
473 |
|
473 | |||
474 |
|
474 | |||
|
475 | def test_parse_options_preserve_non_option_string(): | |||
|
476 | """Test to assert preservation of non-option part of magic-block, while parsing magic options.""" | |||
|
477 | m = DummyMagics(_ip) | |||
|
478 | opts, stmt = m.parse_options( | |||
|
479 | " -n1 -r 13 _ = 314 + foo", "n:r:", preserve_non_opts=True | |||
|
480 | ) | |||
|
481 | nt.assert_equal(opts, {"n": "1", "r": "13"}) | |||
|
482 | nt.assert_equal(stmt, "_ = 314 + foo") | |||
|
483 | ||||
|
484 | ||||
|
485 | def test_run_magic_preserve_code_block(): | |||
|
486 | """Test to assert preservation of non-option part of magic-block, while running magic.""" | |||
|
487 | _ip.user_ns["spaces"] = [] | |||
|
488 | _ip.magic("timeit -n1 -r1 spaces.append([s.count(' ') for s in ['document']])") | |||
|
489 | assert _ip.user_ns["spaces"] == [[0]] | |||
|
490 | ||||
|
491 | ||||
475 | def test_dirops(): |
|
492 | def test_dirops(): | |
476 | """Test various directory handling operations.""" |
|
493 | """Test various directory handling operations.""" | |
477 | # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/') |
|
494 | # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/') | |
@@ -1072,6 +1089,29 b' def test_save():' | |||||
1072 | nt.assert_in("coding: utf-8", content) |
|
1089 | nt.assert_in("coding: utf-8", content) | |
1073 |
|
1090 | |||
1074 |
|
1091 | |||
|
1092 | def test_save_with_no_args(): | |||
|
1093 | ip = get_ipython() | |||
|
1094 | ip.history_manager.reset() # Clear any existing history. | |||
|
1095 | cmds = [u"a=1", u"def b():\n return a**2", u"print(a, b())", "%save"] | |||
|
1096 | for i, cmd in enumerate(cmds, start=1): | |||
|
1097 | ip.history_manager.store_inputs(i, cmd) | |||
|
1098 | ||||
|
1099 | with TemporaryDirectory() as tmpdir: | |||
|
1100 | path = os.path.join(tmpdir, "testsave.py") | |||
|
1101 | ip.run_line_magic("save", path) | |||
|
1102 | content = Path(path).read_text() | |||
|
1103 | expected_content = dedent( | |||
|
1104 | """\ | |||
|
1105 | # coding: utf-8 | |||
|
1106 | a=1 | |||
|
1107 | def b(): | |||
|
1108 | return a**2 | |||
|
1109 | print(a, b()) | |||
|
1110 | """ | |||
|
1111 | ) | |||
|
1112 | nt.assert_equal(content, expected_content) | |||
|
1113 | ||||
|
1114 | ||||
1075 | def test_store(): |
|
1115 | def test_store(): | |
1076 | """Test %store.""" |
|
1116 | """Test %store.""" | |
1077 | ip = get_ipython() |
|
1117 | ip = get_ipython() |
@@ -7,7 +7,7 b'' | |||||
7 | #----------------------------------------------------------------------------- |
|
7 | #----------------------------------------------------------------------------- | |
8 |
|
8 | |||
9 | import argparse |
|
9 | import argparse | |
10 | from nose.tools import assert_equal |
|
10 | import pytest | |
11 |
|
11 | |||
12 | from IPython.core.magic_arguments import (argument, argument_group, kwds, |
|
12 | from IPython.core.magic_arguments import (argument, argument_group, kwds, | |
13 | magic_arguments, parse_argstring, real_name) |
|
13 | magic_arguments, parse_argstring, real_name) | |
@@ -74,45 +74,62 b' def foo(self, args):' | |||||
74 |
|
74 | |||
75 |
|
75 | |||
76 | def test_magic_arguments(): |
|
76 | def test_magic_arguments(): | |
77 | assert_equal(magic_foo1.__doc__, '::\n\n %foo1 [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n') |
|
77 | assert ( | |
78 | assert_equal(getattr(magic_foo1, 'argcmd_name', None), None) |
|
78 | magic_foo1.__doc__ | |
79 | assert_equal(real_name(magic_foo1), 'foo1') |
|
79 | == "::\n\n %foo1 [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n" | |
80 | assert_equal(magic_foo1(None, ''), argparse.Namespace(foo=None)) |
|
80 | ) | |
81 |
assert |
|
81 | assert getattr(magic_foo1, "argcmd_name", None) == None | |
82 |
|
82 | assert real_name(magic_foo1) == "foo1" | ||
83 | assert_equal(magic_foo2.__doc__, '::\n\n %foo2\n\n A docstring.\n') |
|
83 | assert magic_foo1(None, "") == argparse.Namespace(foo=None) | |
84 |
assert |
|
84 | assert hasattr(magic_foo1, "has_arguments") | |
85 | assert_equal(real_name(magic_foo2), 'foo2') |
|
85 | ||
86 | assert_equal(magic_foo2(None, ''), argparse.Namespace()) |
|
86 | assert magic_foo2.__doc__ == "::\n\n %foo2\n\n A docstring.\n" | |
87 |
assert |
|
87 | assert getattr(magic_foo2, "argcmd_name", None) == None | |
88 |
|
88 | assert real_name(magic_foo2) == "foo2" | ||
89 | assert_equal(magic_foo3.__doc__, '::\n\n %foo3 [-f FOO] [-b BAR] [-z BAZ]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n\nGroup:\n -b BAR, --bar BAR a grouped argument\n\nSecond Group:\n -z BAZ, --baz BAZ another grouped argument\n') |
|
89 | assert magic_foo2(None, "") == argparse.Namespace() | |
90 |
assert |
|
90 | assert hasattr(magic_foo2, "has_arguments") | |
91 | assert_equal(real_name(magic_foo3), 'foo3') |
|
91 | ||
92 | assert_equal(magic_foo3(None, ''), |
|
92 | assert ( | |
93 | argparse.Namespace(bar=None, baz=None, foo=None)) |
|
93 | magic_foo3.__doc__ | |
94 | assert hasattr(magic_foo3, 'has_arguments') |
|
94 | == "::\n\n %foo3 [-f FOO] [-b BAR] [-z BAZ]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n\nGroup:\n -b BAR, --bar BAR a grouped argument\n\nSecond Group:\n -z BAZ, --baz BAZ another grouped argument\n" | |
95 |
|
95 | ) | ||
96 | assert_equal(magic_foo4.__doc__, '::\n\n %foo4 [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n') |
|
96 | assert getattr(magic_foo3, "argcmd_name", None) == None | |
97 | assert_equal(getattr(magic_foo4, 'argcmd_name', None), None) |
|
97 | assert real_name(magic_foo3) == "foo3" | |
98 | assert_equal(real_name(magic_foo4), 'foo4') |
|
98 | assert magic_foo3(None, "") == argparse.Namespace(bar=None, baz=None, foo=None) | |
99 | assert_equal(magic_foo4(None, ''), argparse.Namespace()) |
|
99 | assert hasattr(magic_foo3, "has_arguments") | |
100 | assert hasattr(magic_foo4, 'has_arguments') |
|
100 | ||
101 |
|
101 | assert ( | ||
102 | assert_equal(magic_foo5.__doc__, '::\n\n %frobnicate [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n') |
|
102 | magic_foo4.__doc__ | |
103 | assert_equal(getattr(magic_foo5, 'argcmd_name', None), 'frobnicate') |
|
103 | == "::\n\n %foo4 [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n" | |
104 | assert_equal(real_name(magic_foo5), 'frobnicate') |
|
104 | ) | |
105 | assert_equal(magic_foo5(None, ''), argparse.Namespace(foo=None)) |
|
105 | assert getattr(magic_foo4, "argcmd_name", None) == None | |
106 | assert hasattr(magic_foo5, 'has_arguments') |
|
106 | assert real_name(magic_foo4) == "foo4" | |
107 |
|
107 | assert magic_foo4(None, "") == argparse.Namespace() | ||
108 | assert_equal(magic_magic_foo.__doc__, '::\n\n %magic_foo [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n') |
|
108 | assert hasattr(magic_foo4, "has_arguments") | |
109 | assert_equal(getattr(magic_magic_foo, 'argcmd_name', None), None) |
|
109 | ||
110 | assert_equal(real_name(magic_magic_foo), 'magic_foo') |
|
110 | assert ( | |
111 | assert_equal(magic_magic_foo(None, ''), argparse.Namespace(foo=None)) |
|
111 | magic_foo5.__doc__ | |
112 | assert hasattr(magic_magic_foo, 'has_arguments') |
|
112 | == "::\n\n %frobnicate [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n" | |
113 |
|
113 | ) | ||
114 | assert_equal(foo.__doc__, '::\n\n %foo [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n') |
|
114 | assert getattr(magic_foo5, "argcmd_name", None) == "frobnicate" | |
115 | assert_equal(getattr(foo, 'argcmd_name', None), None) |
|
115 | assert real_name(magic_foo5) == "frobnicate" | |
116 | assert_equal(real_name(foo), 'foo') |
|
116 | assert magic_foo5(None, "") == argparse.Namespace(foo=None) | |
117 | assert_equal(foo(None, ''), argparse.Namespace(foo=None)) |
|
117 | assert hasattr(magic_foo5, "has_arguments") | |
118 | assert hasattr(foo, 'has_arguments') |
|
118 | ||
|
119 | assert ( | |||
|
120 | magic_magic_foo.__doc__ | |||
|
121 | == "::\n\n %magic_foo [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n" | |||
|
122 | ) | |||
|
123 | assert getattr(magic_magic_foo, "argcmd_name", None) == None | |||
|
124 | assert real_name(magic_magic_foo) == "magic_foo" | |||
|
125 | assert magic_magic_foo(None, "") == argparse.Namespace(foo=None) | |||
|
126 | assert hasattr(magic_magic_foo, "has_arguments") | |||
|
127 | ||||
|
128 | assert ( | |||
|
129 | foo.__doc__ | |||
|
130 | == "::\n\n %foo [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n" | |||
|
131 | ) | |||
|
132 | assert getattr(foo, "argcmd_name", None) == None | |||
|
133 | assert real_name(foo) == "foo" | |||
|
134 | assert foo(None, "") == argparse.Namespace(foo=None) | |||
|
135 | assert hasattr(foo, "has_arguments") |
@@ -56,7 +56,7 b' def pyfile(fname):' | |||||
56 |
|
56 | |||
57 |
|
57 | |||
58 | def match_pyfiles(f1, f2): |
|
58 | def match_pyfiles(f1, f2): | |
59 |
|
|
59 | assert pyfile(f1) == pyfile(f2) | |
60 |
|
60 | |||
61 |
|
61 | |||
62 | def test_find_file(): |
|
62 | def test_find_file(): | |
@@ -76,7 +76,7 b' def test_find_file_decorated1():' | |||||
76 | "My docstring" |
|
76 | "My docstring" | |
77 |
|
77 | |||
78 | match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__)) |
|
78 | match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__)) | |
79 |
|
|
79 | assert f.__doc__ == "My docstring" | |
80 |
|
80 | |||
81 |
|
81 | |||
82 | def test_find_file_decorated2(): |
|
82 | def test_find_file_decorated2(): | |
@@ -92,7 +92,7 b' def test_find_file_decorated2():' | |||||
92 | "My docstring 2" |
|
92 | "My docstring 2" | |
93 |
|
93 | |||
94 | match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__)) |
|
94 | match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__)) | |
95 |
|
|
95 | assert f.__doc__ == "My docstring 2" | |
96 |
|
96 | |||
97 |
|
97 | |||
98 | def test_find_file_magic(): |
|
98 | def test_find_file_magic(): | |
@@ -167,41 +167,46 b' class SerialLiar(object):' | |||||
167 |
|
167 | |||
168 | def test_info(): |
|
168 | def test_info(): | |
169 | "Check that Inspector.info fills out various fields as expected." |
|
169 | "Check that Inspector.info fills out various fields as expected." | |
170 |
i = inspector.info(Call, oname= |
|
170 | i = inspector.info(Call, oname="Call") | |
171 |
|
|
171 | assert i["type_name"] == "type" | |
172 | expted_class = str(type(type)) # <class 'type'> (Python 3) or <type 'type'> |
|
172 | expted_class = str(type(type)) # <class 'type'> (Python 3) or <type 'type'> | |
173 |
|
|
173 | assert i["base_class"] == expted_class | |
174 | nt.assert_regex(i['string_form'], "<class 'IPython.core.tests.test_oinspect.Call'( at 0x[0-9a-f]{1,9})?>") |
|
174 | nt.assert_regex( | |
|
175 | i["string_form"], | |||
|
176 | "<class 'IPython.core.tests.test_oinspect.Call'( at 0x[0-9a-f]{1,9})?>", | |||
|
177 | ) | |||
175 | fname = __file__ |
|
178 | fname = __file__ | |
176 | if fname.endswith(".pyc"): |
|
179 | if fname.endswith(".pyc"): | |
177 | fname = fname[:-1] |
|
180 | fname = fname[:-1] | |
178 | # case-insensitive comparison needed on some filesystems |
|
181 | # case-insensitive comparison needed on some filesystems | |
179 | # e.g. Windows: |
|
182 | # e.g. Windows: | |
180 |
|
|
183 | assert i["file"].lower() == compress_user(fname).lower() | |
181 |
|
|
184 | assert i["definition"] == None | |
182 |
|
|
185 | assert i["docstring"] == Call.__doc__ | |
183 |
|
|
186 | assert i["source"] == None | |
184 |
nt.assert_true(i[ |
|
187 | nt.assert_true(i["isclass"]) | |
185 |
|
|
188 | assert i["init_definition"] == "Call(x, y=1)" | |
186 |
|
|
189 | assert i["init_docstring"] == Call.__init__.__doc__ | |
187 |
|
190 | |||
188 | i = inspector.info(Call, detail_level=1) |
|
191 | i = inspector.info(Call, detail_level=1) | |
189 |
nt.assert_not_equal(i[ |
|
192 | nt.assert_not_equal(i["source"], None) | |
190 |
|
|
193 | assert i["docstring"] == None | |
191 |
|
194 | |||
192 | c = Call(1) |
|
195 | c = Call(1) | |
193 | c.__doc__ = "Modified instance docstring" |
|
196 | c.__doc__ = "Modified instance docstring" | |
194 | i = inspector.info(c) |
|
197 | i = inspector.info(c) | |
195 |
|
|
198 | assert i["type_name"] == "Call" | |
196 |
|
|
199 | assert i["docstring"] == "Modified instance docstring" | |
197 |
|
|
200 | assert i["class_docstring"] == Call.__doc__ | |
198 |
|
|
201 | assert i["init_docstring"] == Call.__init__.__doc__ | |
199 |
|
|
202 | assert i["call_docstring"] == Call.__call__.__doc__ | |
|
203 | ||||
200 |
|
204 | |||
201 | def test_class_signature(): |
|
205 | def test_class_signature(): | |
202 |
info = inspector.info(HasSignature, |
|
206 | info = inspector.info(HasSignature, "HasSignature") | |
203 |
|
|
207 | assert info["init_definition"] == "HasSignature(test)" | |
204 |
|
|
208 | assert info["init_docstring"] == HasSignature.__init__.__doc__ | |
|
209 | ||||
205 |
|
210 | |||
206 | def test_info_awkward(): |
|
211 | def test_info_awkward(): | |
207 | # Just test that this doesn't throw an error. |
|
212 | # Just test that this doesn't throw an error. | |
@@ -231,8 +236,9 b' def f_kwarg(pos, *, kwonly):' | |||||
231 | pass |
|
236 | pass | |
232 |
|
237 | |||
233 | def test_definition_kwonlyargs(): |
|
238 | def test_definition_kwonlyargs(): | |
234 |
i = inspector.info(f_kwarg, oname= |
|
239 | i = inspector.info(f_kwarg, oname="f_kwarg") # analysis:ignore | |
235 |
|
|
240 | assert i["definition"] == "f_kwarg(pos, *, kwonly)" | |
|
241 | ||||
236 |
|
242 | |||
237 | def test_getdoc(): |
|
243 | def test_getdoc(): | |
238 | class A(object): |
|
244 | class A(object): | |
@@ -253,9 +259,9 b' def test_getdoc():' | |||||
253 | b = B() |
|
259 | b = B() | |
254 | c = C() |
|
260 | c = C() | |
255 |
|
261 | |||
256 |
|
|
262 | assert oinspect.getdoc(a) == "standard docstring" | |
257 |
|
|
263 | assert oinspect.getdoc(b) == "custom docstring" | |
258 |
|
|
264 | assert oinspect.getdoc(c) == "standard docstring" | |
259 |
|
265 | |||
260 |
|
266 | |||
261 | def test_empty_property_has_no_source(): |
|
267 | def test_empty_property_has_no_source(): | |
@@ -299,15 +305,17 b' def test_property_docstring_is_in_info_for_detail_level_0():' | |||||
299 | """This is `foobar` property.""" |
|
305 | """This is `foobar` property.""" | |
300 | pass |
|
306 | pass | |
301 |
|
307 | |||
302 |
ip.user_ns[ |
|
308 | ip.user_ns["a_obj"] = A() | |
303 |
|
|
309 | assert ( | |
304 |
|
|
310 | "This is `foobar` property." | |
305 |
ip.object_inspect( |
|
311 | == ip.object_inspect("a_obj.foobar", detail_level=0)["docstring"] | |
|
312 | ) | |||
306 |
|
313 | |||
307 |
ip.user_ns[ |
|
314 | ip.user_ns["a_cls"] = A | |
308 |
|
|
315 | assert ( | |
309 |
|
|
316 | "This is `foobar` property." | |
310 |
ip.object_inspect( |
|
317 | == ip.object_inspect("a_cls.foobar", detail_level=0)["docstring"] | |
|
318 | ) | |||
311 |
|
319 | |||
312 |
|
320 | |||
313 | def test_pdef(): |
|
321 | def test_pdef(): | |
@@ -404,7 +412,7 b' def test_render_signature_short():' | |||||
404 | signature(short_fun), |
|
412 | signature(short_fun), | |
405 | short_fun.__name__, |
|
413 | short_fun.__name__, | |
406 | ) |
|
414 | ) | |
407 |
|
|
415 | assert sig == "short_fun(a=1)" | |
408 |
|
416 | |||
409 |
|
417 | |||
410 | def test_render_signature_long(): |
|
418 | def test_render_signature_long(): |
@@ -3,7 +3,7 b'' | |||||
3 | #----------------------------------------------------------------------------- |
|
3 | #----------------------------------------------------------------------------- | |
4 | # Imports |
|
4 | # Imports | |
5 | #----------------------------------------------------------------------------- |
|
5 | #----------------------------------------------------------------------------- | |
6 | import nose.tools as nt |
|
6 | import pytest | |
7 |
|
7 | |||
8 | from IPython.core.prefilter import AutocallChecker |
|
8 | from IPython.core.prefilter import AutocallChecker | |
9 |
|
9 | |||
@@ -19,7 +19,7 b' def test_prefilter():' | |||||
19 | ] |
|
19 | ] | |
20 |
|
20 | |||
21 | for raw, correct in pairs: |
|
21 | for raw, correct in pairs: | |
22 |
|
|
22 | assert ip.prefilter(raw) == correct | |
23 |
|
23 | |||
24 | def test_prefilter_shadowed(): |
|
24 | def test_prefilter_shadowed(): | |
25 | def dummy_magic(line): pass |
|
25 | def dummy_magic(line): pass | |
@@ -32,16 +32,16 b' def test_prefilter_shadowed():' | |||||
32 | # These should not be transformed - they are shadowed by other names |
|
32 | # These should not be transformed - they are shadowed by other names | |
33 | for name in ['if', 'zip', 'get_ipython']: # keyword, builtin, global |
|
33 | for name in ['if', 'zip', 'get_ipython']: # keyword, builtin, global | |
34 | ip.register_magic_function(dummy_magic, magic_name=name) |
|
34 | ip.register_magic_function(dummy_magic, magic_name=name) | |
35 |
res = ip.prefilter(name+ |
|
35 | res = ip.prefilter(name + " foo") | |
36 |
|
|
36 | assert res == name + " foo" | |
37 |
del ip.magics_manager.magics[ |
|
37 | del ip.magics_manager.magics["line"][name] | |
38 |
|
38 | |||
39 | # These should be transformed |
|
39 | # These should be transformed | |
40 | for name in ['fi', 'piz', 'nohtypi_teg']: |
|
40 | for name in ['fi', 'piz', 'nohtypi_teg']: | |
41 | ip.register_magic_function(dummy_magic, magic_name=name) |
|
41 | ip.register_magic_function(dummy_magic, magic_name=name) | |
42 |
res = ip.prefilter(name+ |
|
42 | res = ip.prefilter(name + " foo") | |
43 |
|
|
43 | assert res != name + " foo" | |
44 |
del ip.magics_manager.magics[ |
|
44 | del ip.magics_manager.magics["line"][name] | |
45 |
|
45 | |||
46 | finally: |
|
46 | finally: | |
47 | ip.automagic = prev_automagic_state |
|
47 | ip.automagic = prev_automagic_state | |
@@ -52,9 +52,9 b' def test_autocall_binops():' | |||||
52 | f = lambda x: x |
|
52 | f = lambda x: x | |
53 | ip.user_ns['f'] = f |
|
53 | ip.user_ns['f'] = f | |
54 | try: |
|
54 | try: | |
55 |
|
|
55 | assert ip.prefilter("f 1") == "f(1)" | |
56 |
for t in [ |
|
56 | for t in ["f +1", "f -1"]: | |
57 |
|
|
57 | assert ip.prefilter(t) == t | |
58 |
|
58 | |||
59 | # Run tests again with a more permissive exclude_regexp, which will |
|
59 | # Run tests again with a more permissive exclude_regexp, which will | |
60 | # allow transformation of binary operations ('f -1' -> 'f(-1)'). |
|
60 | # allow transformation of binary operations ('f -1' -> 'f(-1)'). | |
@@ -66,8 +66,8 b' def test_autocall_binops():' | |||||
66 | ac.exclude_regexp = r'^[,&^\|\*/]|^is |^not |^in |^and |^or ' |
|
66 | ac.exclude_regexp = r'^[,&^\|\*/]|^is |^not |^in |^and |^or ' | |
67 | pm.sort_checkers() |
|
67 | pm.sort_checkers() | |
68 |
|
68 | |||
69 |
|
|
69 | assert ip.prefilter("f -1") == "f(-1)" | |
70 |
|
|
70 | assert ip.prefilter("f +1") == "f(+1)" | |
71 | finally: |
|
71 | finally: | |
72 | pm.unregister_checker(ac) |
|
72 | pm.unregister_checker(ac) | |
73 | finally: |
|
73 | finally: | |
@@ -88,7 +88,7 b' def test_issue_114():' | |||||
88 | try: |
|
88 | try: | |
89 | for mgk in ip.magics_manager.lsmagic()['line']: |
|
89 | for mgk in ip.magics_manager.lsmagic()['line']: | |
90 | raw = template % mgk |
|
90 | raw = template % mgk | |
91 |
|
|
91 | assert ip.prefilter(raw) == raw | |
92 | finally: |
|
92 | finally: | |
93 | ip.prefilter_manager.multi_line_specials = msp |
|
93 | ip.prefilter_manager.multi_line_specials = msp | |
94 |
|
94 | |||
@@ -121,7 +121,7 b' def test_autocall_should_support_unicode():' | |||||
121 | ip.magic('autocall 2') |
|
121 | ip.magic('autocall 2') | |
122 | ip.user_ns['π'] = lambda x: x |
|
122 | ip.user_ns['π'] = lambda x: x | |
123 | try: |
|
123 | try: | |
124 |
|
|
124 | assert ip.prefilter("π 3") == "π(3)" | |
125 | finally: |
|
125 | finally: | |
126 | ip.magic('autocall 0') |
|
126 | ip.magic('autocall 0') | |
127 | del ip.user_ns['π'] |
|
127 | del ip.user_ns['π'] |
@@ -15,6 +15,7 b' from nose import SkipTest' | |||||
15 | import nose.tools as nt |
|
15 | import nose.tools as nt | |
16 |
|
16 | |||
17 | from matplotlib import pyplot as plt |
|
17 | from matplotlib import pyplot as plt | |
|
18 | import matplotlib_inline | |||
18 | import numpy as np |
|
19 | import numpy as np | |
19 |
|
20 | |||
20 | from IPython.core.getipython import get_ipython |
|
21 | from IPython.core.getipython import get_ipython | |
@@ -146,8 +147,16 b' def test_import_pylab():' | |||||
146 | nt.assert_true('plt' in ns) |
|
147 | nt.assert_true('plt' in ns) | |
147 | nt.assert_equal(ns['np'], np) |
|
148 | nt.assert_equal(ns['np'], np) | |
148 |
|
149 | |||
|
150 | from traitlets.config import Config | |||
|
151 | ||||
|
152 | ||||
149 | class TestPylabSwitch(object): |
|
153 | class TestPylabSwitch(object): | |
150 | class Shell(InteractiveShell): |
|
154 | class Shell(InteractiveShell): | |
|
155 | def init_history(self): | |||
|
156 | """Sets up the command history, and starts regular autosaves.""" | |||
|
157 | self.config.HistoryManager.hist_file = ":memory:" | |||
|
158 | super().init_history() | |||
|
159 | ||||
151 | def enable_gui(self, gui): |
|
160 | def enable_gui(self, gui): | |
152 | pass |
|
161 | pass | |
153 |
|
162 | |||
@@ -167,18 +176,21 b' class TestPylabSwitch(object):' | |||||
167 | pt.activate_matplotlib = act_mpl |
|
176 | pt.activate_matplotlib = act_mpl | |
168 | self._save_ip = pt.import_pylab |
|
177 | self._save_ip = pt.import_pylab | |
169 | pt.import_pylab = lambda *a,**kw:None |
|
178 | pt.import_pylab = lambda *a,**kw:None | |
170 | self._save_cis = pt.configure_inline_support |
|
179 | self._save_cis = matplotlib_inline.backend_inline.configure_inline_support | |
171 |
pt.configure_inline_support = |
|
180 | matplotlib_inline.backend_inline.configure_inline_support = ( | |
|
181 | lambda *a, **kw: None | |||
|
182 | ) | |||
172 |
|
183 | |||
173 | def teardown(self): |
|
184 | def teardown(self): | |
174 | pt.activate_matplotlib = self._save_am |
|
185 | pt.activate_matplotlib = self._save_am | |
175 | pt.import_pylab = self._save_ip |
|
186 | pt.import_pylab = self._save_ip | |
176 | pt.configure_inline_support = self._save_cis |
|
187 | matplotlib_inline.backend_inline.configure_inline_support = self._save_cis | |
177 | import matplotlib |
|
188 | import matplotlib | |
178 | matplotlib.rcParams = self._saved_rcParams |
|
189 | matplotlib.rcParams = self._saved_rcParams | |
179 | matplotlib.rcParamsOrig = self._saved_rcParamsOrig |
|
190 | matplotlib.rcParamsOrig = self._saved_rcParamsOrig | |
180 |
|
191 | |||
181 | def test_qt(self): |
|
192 | def test_qt(self): | |
|
193 | ||||
182 | s = self.Shell() |
|
194 | s = self.Shell() | |
183 | gui, backend = s.enable_matplotlib(None) |
|
195 | gui, backend = s.enable_matplotlib(None) | |
184 | nt.assert_equal(gui, 'qt') |
|
196 | nt.assert_equal(gui, 'qt') |
@@ -169,6 +169,31 b' def _format_traceback_lines(lines, Colors, has_colors, lvals):' | |||||
169 | return res |
|
169 | return res | |
170 |
|
170 | |||
171 |
|
171 | |||
|
172 | def _format_filename(file, ColorFilename, ColorNormal): | |||
|
173 | """ | |||
|
174 | Format filename lines with `In [n]` if it's the nth code cell or `File *.py` if it's a module. | |||
|
175 | ||||
|
176 | Parameters | |||
|
177 | ---------- | |||
|
178 | file : str | |||
|
179 | ColorFilename | |||
|
180 | ColorScheme's filename coloring to be used. | |||
|
181 | ColorNormal | |||
|
182 | ColorScheme's normal coloring to be used. | |||
|
183 | """ | |||
|
184 | ipinst = get_ipython() | |||
|
185 | ||||
|
186 | if ipinst is not None and file in ipinst.compile._filename_map: | |||
|
187 | file = "[%s]" % ipinst.compile._filename_map[file] | |||
|
188 | tpl_link = "In %s%%s%s" % (ColorFilename, ColorNormal) | |||
|
189 | else: | |||
|
190 | file = util_path.compress_user( | |||
|
191 | py3compat.cast_unicode(file, util_path.fs_encoding) | |||
|
192 | ) | |||
|
193 | tpl_link = "File %s%%s%s" % (ColorFilename, ColorNormal) | |||
|
194 | ||||
|
195 | return tpl_link % file | |||
|
196 | ||||
172 | #--------------------------------------------------------------------------- |
|
197 | #--------------------------------------------------------------------------- | |
173 | # Module classes |
|
198 | # Module classes | |
174 | class TBTools(colorable.Colorable): |
|
199 | class TBTools(colorable.Colorable): | |
@@ -414,21 +439,31 b' class ListTB(TBTools):' | |||||
414 | Colors = self.Colors |
|
439 | Colors = self.Colors | |
415 | list = [] |
|
440 | list = [] | |
416 | for filename, lineno, name, line in extracted_list[:-1]: |
|
441 | for filename, lineno, name, line in extracted_list[:-1]: | |
417 |
item = |
|
442 | item = " %s, line %s%d%s, in %s%s%s\n" % ( | |
418 |
|
|
443 | _format_filename(filename, Colors.filename, Colors.Normal), | |
419 |
|
|
444 | Colors.lineno, | |
420 | Colors.name, name, Colors.Normal) |
|
445 | lineno, | |
|
446 | Colors.Normal, | |||
|
447 | Colors.name, | |||
|
448 | name, | |||
|
449 | Colors.Normal, | |||
|
450 | ) | |||
421 | if line: |
|
451 | if line: | |
422 | item += ' %s\n' % line.strip() |
|
452 | item += ' %s\n' % line.strip() | |
423 | list.append(item) |
|
453 | list.append(item) | |
424 | # Emphasize the last entry |
|
454 | # Emphasize the last entry | |
425 | filename, lineno, name, line = extracted_list[-1] |
|
455 | filename, lineno, name, line = extracted_list[-1] | |
426 |
item = |
|
456 | item = "%s %s, line %s%d%s, in %s%s%s%s\n" % ( | |
427 |
|
|
457 | Colors.normalEm, | |
428 |
|
|
458 | _format_filename(filename, Colors.filenameEm, Colors.normalEm), | |
429 |
|
|
459 | Colors.linenoEm, | |
430 | Colors.nameEm, name, Colors.normalEm, |
|
460 | lineno, | |
431 |
|
|
461 | Colors.normalEm, | |
|
462 | Colors.nameEm, | |||
|
463 | name, | |||
|
464 | Colors.normalEm, | |||
|
465 | Colors.Normal, | |||
|
466 | ) | |||
432 | if line: |
|
467 | if line: | |
433 | item += '%s %s%s\n' % (Colors.line, line.strip(), |
|
468 | item += '%s %s%s\n' % (Colors.line, line.strip(), | |
434 | Colors.Normal) |
|
469 | Colors.Normal) | |
@@ -463,13 +498,21 b' class ListTB(TBTools):' | |||||
463 | lineno = value.lineno |
|
498 | lineno = value.lineno | |
464 | textline = linecache.getline(value.filename, value.lineno) |
|
499 | textline = linecache.getline(value.filename, value.lineno) | |
465 | else: |
|
500 | else: | |
466 |
lineno = |
|
501 | lineno = "unknown" | |
467 |
textline = |
|
502 | textline = "" | |
468 | list.append('%s File %s"%s"%s, line %s%s%s\n' % \ |
|
503 | list.append( | |
469 | (Colors.normalEm, |
|
504 | "%s %s, line %s%s%s\n" | |
470 | Colors.filenameEm, py3compat.cast_unicode(value.filename), Colors.normalEm, |
|
505 | % ( | |
471 |
|
|
506 | Colors.normalEm, | |
472 | if textline == '': |
|
507 | _format_filename( | |
|
508 | value.filename, Colors.filenameEm, Colors.normalEm | |||
|
509 | ), | |||
|
510 | Colors.linenoEm, | |||
|
511 | lineno, | |||
|
512 | Colors.Normal, | |||
|
513 | ) | |||
|
514 | ) | |||
|
515 | if textline == "": | |||
473 | textline = py3compat.cast_unicode(value.text, "utf-8") |
|
516 | textline = py3compat.cast_unicode(value.text, "utf-8") | |
474 |
|
517 | |||
475 | if textline is not None: |
|
518 | if textline is not None: | |
@@ -581,25 +624,19 b' class VerboseTB(TBTools):' | |||||
581 | Colors = self.Colors # just a shorthand + quicker name lookup |
|
624 | Colors = self.Colors # just a shorthand + quicker name lookup | |
582 | ColorsNormal = Colors.Normal # used a lot |
|
625 | ColorsNormal = Colors.Normal # used a lot | |
583 |
|
626 | |||
584 |
|
||||
585 |
|
||||
586 | if isinstance(frame_info, stack_data.RepeatedFrames): |
|
627 | if isinstance(frame_info, stack_data.RepeatedFrames): | |
587 | return ' %s[... skipping similar frames: %s]%s\n' % ( |
|
628 | return ' %s[... skipping similar frames: %s]%s\n' % ( | |
588 | Colors.excName, frame_info.description, ColorsNormal) |
|
629 | Colors.excName, frame_info.description, ColorsNormal) | |
589 |
|
630 | |||
590 | indent = ' ' * INDENT_SIZE |
|
631 | indent = ' ' * INDENT_SIZE | |
591 | em_normal = '%s\n%s%s' % (Colors.valEm, indent, ColorsNormal) |
|
632 | em_normal = '%s\n%s%s' % (Colors.valEm, indent, ColorsNormal) | |
592 | tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal) |
|
|||
593 | tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm, |
|
633 | tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm, | |
594 | ColorsNormal) |
|
634 | ColorsNormal) | |
595 | tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \ |
|
635 | tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \ | |
596 | (Colors.vName, Colors.valEm, ColorsNormal) |
|
636 | (Colors.vName, Colors.valEm, ColorsNormal) | |
597 | tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal) |
|
|||
598 | tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal) |
|
637 | tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal) | |
599 |
|
638 | |||
600 | file = frame_info.filename |
|
639 | link = _format_filename(frame_info.filename, Colors.filenameEm, ColorsNormal) | |
601 | file = py3compat.cast_unicode(file, util_path.fs_encoding) |
|
|||
602 | link = tpl_link % util_path.compress_user(file) |
|
|||
603 | args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame) |
|
640 | args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame) | |
604 |
|
641 | |||
605 | func = frame_info.executing.code_qualname() |
|
642 | func = frame_info.executing.code_qualname() | |
@@ -634,12 +671,20 b' class VerboseTB(TBTools):' | |||||
634 | lvals = '' |
|
671 | lvals = '' | |
635 | lvals_list = [] |
|
672 | lvals_list = [] | |
636 | if self.include_vars: |
|
673 | if self.include_vars: | |
637 | for var in frame_info.variables_in_executing_piece: |
|
674 | try: | |
|
675 | # we likely want to fix stackdata at some point, but | |||
|
676 | # still need a workaround. | |||
|
677 | fibp = frame_info.variables_in_executing_piece | |||
|
678 | for var in fibp: | |||
638 | lvals_list.append(tpl_name_val % (var.name, repr(var.value))) |
|
679 | lvals_list.append(tpl_name_val % (var.name, repr(var.value))) | |
|
680 | except Exception: | |||
|
681 | lvals_list.append( | |||
|
682 | "Exception trying to inspect frame. No more locals available." | |||
|
683 | ) | |||
639 | if lvals_list: |
|
684 | if lvals_list: | |
640 | lvals = '%s%s' % (indent, em_normal.join(lvals_list)) |
|
685 | lvals = '%s%s' % (indent, em_normal.join(lvals_list)) | |
641 |
|
686 | |||
642 |
result = |
|
687 | result = "%s, %s\n" % (link, call) | |
643 |
|
688 | |||
644 | result += ''.join(_format_traceback_lines(frame_info.lines, Colors, self.has_colors, lvals)) |
|
689 | result += ''.join(_format_traceback_lines(frame_info.lines, Colors, self.has_colors, lvals)) | |
645 | return result |
|
690 | return result |
@@ -48,6 +48,11 b' The following magic commands are provided:' | |||||
48 | Reload all modules (except those excluded by ``%aimport``) every |
|
48 | Reload all modules (except those excluded by ``%aimport``) every | |
49 | time before executing the Python code typed. |
|
49 | time before executing the Python code typed. | |
50 |
|
50 | |||
|
51 | ``%autoreload 3`` | |||
|
52 | ||||
|
53 | Reload all modules AND autoload newly added objects | |||
|
54 | every time before executing the Python code typed. | |||
|
55 | ||||
51 | ``%aimport`` |
|
56 | ``%aimport`` | |
52 |
|
57 | |||
53 | List modules which are to be automatically imported or not to be imported. |
|
58 | List modules which are to be automatically imported or not to be imported. | |
@@ -124,14 +129,18 b' from imp import reload' | |||||
124 | # Autoreload functionality |
|
129 | # Autoreload functionality | |
125 | #------------------------------------------------------------------------------ |
|
130 | # ------------------------------------------------------------------------------ | |
126 |
|
131 | |||
127 | class ModuleReloader(object): |
|
132 | ||
|
133 | class ModuleReloader: | |||
128 | enabled = False |
|
134 | enabled = False | |
129 | """Whether this reloader is enabled""" |
|
135 | """Whether this reloader is enabled""" | |
130 |
|
136 | |||
131 | check_all = True |
|
137 | check_all = True | |
132 | """Autoreload all modules, not just those listed in 'modules'""" |
|
138 | """Autoreload all modules, not just those listed in 'modules'""" | |
133 |
|
139 | |||
134 | def __init__(self): |
|
140 | autoload_obj = False | |
|
141 | """Autoreload all modules AND autoload all new objects""" | |||
|
142 | ||||
|
143 | def __init__(self, shell=None): | |||
135 | # Modules that failed to reload: {module: mtime-on-failed-reload, ...} |
|
144 | # Modules that failed to reload: {module: mtime-on-failed-reload, ...} | |
136 | self.failed = {} |
|
145 | self.failed = {} | |
137 | # Modules specially marked as autoreloadable. |
|
146 | # Modules specially marked as autoreloadable. | |
@@ -142,6 +151,7 b' class ModuleReloader(object):' | |||||
142 | self.old_objects = {} |
|
151 | self.old_objects = {} | |
143 | # Module modification timestamps |
|
152 | # Module modification timestamps | |
144 | self.modules_mtimes = {} |
|
153 | self.modules_mtimes = {} | |
|
154 | self.shell = shell | |||
145 |
|
155 | |||
146 | # Cache module modification times |
|
156 | # Cache module modification times | |
147 | self.check(check_all=True, do_reload=False) |
|
157 | self.check(check_all=True, do_reload=False) | |
@@ -176,22 +186,22 b' class ModuleReloader(object):' | |||||
176 | self.mark_module_reloadable(module_name) |
|
186 | self.mark_module_reloadable(module_name) | |
177 |
|
187 | |||
178 | import_module(module_name) |
|
188 | import_module(module_name) | |
179 |
top_name = module_name.split( |
|
189 | top_name = module_name.split(".")[0] | |
180 | top_module = sys.modules[top_name] |
|
190 | top_module = sys.modules[top_name] | |
181 | return top_module, top_name |
|
191 | return top_module, top_name | |
182 |
|
192 | |||
183 | def filename_and_mtime(self, module): |
|
193 | def filename_and_mtime(self, module): | |
184 |
if not hasattr(module, |
|
194 | if not hasattr(module, "__file__") or module.__file__ is None: | |
185 | return None, None |
|
195 | return None, None | |
186 |
|
196 | |||
187 |
if getattr(module, |
|
197 | if getattr(module, "__name__", None) in [None, "__mp_main__", "__main__"]: | |
188 | # we cannot reload(__main__) or reload(__mp_main__) |
|
198 | # we cannot reload(__main__) or reload(__mp_main__) | |
189 | return None, None |
|
199 | return None, None | |
190 |
|
200 | |||
191 | filename = module.__file__ |
|
201 | filename = module.__file__ | |
192 | path, ext = os.path.splitext(filename) |
|
202 | path, ext = os.path.splitext(filename) | |
193 |
|
203 | |||
194 |
if ext.lower() == |
|
204 | if ext.lower() == ".py": | |
195 | py_filename = filename |
|
205 | py_filename = filename | |
196 | else: |
|
206 | else: | |
197 | try: |
|
207 | try: | |
@@ -242,21 +252,35 b' class ModuleReloader(object):' | |||||
242 | # If we've reached this point, we should try to reload the module |
|
252 | # If we've reached this point, we should try to reload the module | |
243 | if do_reload: |
|
253 | if do_reload: | |
244 | try: |
|
254 | try: | |
|
255 | if self.autoload_obj: | |||
|
256 | superreload(m, reload, self.old_objects, self.shell) | |||
|
257 | else: | |||
245 | superreload(m, reload, self.old_objects) |
|
258 | superreload(m, reload, self.old_objects) | |
246 | if py_filename in self.failed: |
|
259 | if py_filename in self.failed: | |
247 | del self.failed[py_filename] |
|
260 | del self.failed[py_filename] | |
248 | except: |
|
261 | except: | |
249 | print("[autoreload of %s failed: %s]" % ( |
|
262 | print( | |
250 | modname, traceback.format_exc(10)), file=sys.stderr) |
|
263 | "[autoreload of {} failed: {}]".format( | |
|
264 | modname, traceback.format_exc(10) | |||
|
265 | ), | |||
|
266 | file=sys.stderr, | |||
|
267 | ) | |||
251 | self.failed[py_filename] = pymtime |
|
268 | self.failed[py_filename] = pymtime | |
252 |
|
269 | |||
|
270 | ||||
253 | #------------------------------------------------------------------------------ |
|
271 | # ------------------------------------------------------------------------------ | |
254 | # superreload |
|
272 | # superreload | |
255 | #------------------------------------------------------------------------------ |
|
273 | # ------------------------------------------------------------------------------ | |
256 |
|
274 | |||
257 |
|
275 | |||
258 | func_attrs = ['__code__', '__defaults__', '__doc__', |
|
276 | func_attrs = [ | |
259 | '__closure__', '__globals__', '__dict__'] |
|
277 | "__code__", | |
|
278 | "__defaults__", | |||
|
279 | "__doc__", | |||
|
280 | "__closure__", | |||
|
281 | "__globals__", | |||
|
282 | "__dict__", | |||
|
283 | ] | |||
260 |
|
284 | |||
261 |
|
285 | |||
262 | def update_function(old, new): |
|
286 | def update_function(old, new): | |
@@ -299,7 +323,8 b' def update_class(old, new):' | |||||
299 | pass |
|
323 | pass | |
300 | continue |
|
324 | continue | |
301 |
|
325 | |||
302 |
if update_generic(old_obj, new_obj): |
|
326 | if update_generic(old_obj, new_obj): | |
|
327 | continue | |||
303 |
|
328 | |||
304 | try: |
|
329 | try: | |
305 | setattr(old, key, getattr(new, key)) |
|
330 | setattr(old, key, getattr(new, key)) | |
@@ -329,16 +354,18 b' def isinstance2(a, b, typ):' | |||||
329 |
|
354 | |||
330 |
|
355 | |||
331 | UPDATE_RULES = [ |
|
356 | UPDATE_RULES = [ | |
332 | (lambda a, b: isinstance2(a, b, type), |
|
357 | (lambda a, b: isinstance2(a, b, type), update_class), | |
333 | update_class), |
|
358 | (lambda a, b: isinstance2(a, b, types.FunctionType), update_function), | |
334 |
(lambda a, b: isinstance2(a, b, |
|
359 | (lambda a, b: isinstance2(a, b, property), update_property), | |
335 | update_function), |
|
|||
336 | (lambda a, b: isinstance2(a, b, property), |
|
|||
337 | update_property), |
|
|||
338 | ] |
|
360 | ] | |
339 | UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.MethodType), |
|
361 | UPDATE_RULES.extend( | |
340 | lambda a, b: update_function(a.__func__, b.__func__)), |
|
362 | [ | |
341 | ]) |
|
363 | ( | |
|
364 | lambda a, b: isinstance2(a, b, types.MethodType), | |||
|
365 | lambda a, b: update_function(a.__func__, b.__func__), | |||
|
366 | ), | |||
|
367 | ] | |||
|
368 | ) | |||
342 |
|
369 | |||
343 |
|
370 | |||
344 | def update_generic(a, b): |
|
371 | def update_generic(a, b): | |
@@ -349,14 +376,45 b' def update_generic(a, b):' | |||||
349 | return False |
|
376 | return False | |
350 |
|
377 | |||
351 |
|
378 | |||
352 |
class StrongRef |
|
379 | class StrongRef: | |
353 | def __init__(self, obj): |
|
380 | def __init__(self, obj): | |
354 | self.obj = obj |
|
381 | self.obj = obj | |
|
382 | ||||
355 | def __call__(self): |
|
383 | def __call__(self): | |
356 | return self.obj |
|
384 | return self.obj | |
357 |
|
385 | |||
358 |
|
386 | |||
359 | def superreload(module, reload=reload, old_objects=None): |
|
387 | mod_attrs = [ | |
|
388 | "__name__", | |||
|
389 | "__doc__", | |||
|
390 | "__package__", | |||
|
391 | "__loader__", | |||
|
392 | "__spec__", | |||
|
393 | "__file__", | |||
|
394 | "__cached__", | |||
|
395 | "__builtins__", | |||
|
396 | ] | |||
|
397 | ||||
|
398 | ||||
|
399 | def append_obj(module, d, name, obj, autoload=False): | |||
|
400 | in_module = hasattr(obj, "__module__") and obj.__module__ == module.__name__ | |||
|
401 | if autoload: | |||
|
402 | # check needed for module global built-ins | |||
|
403 | if not in_module and name in mod_attrs: | |||
|
404 | return False | |||
|
405 | else: | |||
|
406 | if not in_module: | |||
|
407 | return False | |||
|
408 | ||||
|
409 | key = (module.__name__, name) | |||
|
410 | try: | |||
|
411 | d.setdefault(key, []).append(weakref.ref(obj)) | |||
|
412 | except TypeError: | |||
|
413 | pass | |||
|
414 | return True | |||
|
415 | ||||
|
416 | ||||
|
417 | def superreload(module, reload=reload, old_objects=None, shell=None): | |||
360 | """Enhanced version of the builtin reload function. |
|
418 | """Enhanced version of the builtin reload function. | |
361 |
|
419 | |||
362 | superreload remembers objects previously in the module, and |
|
420 | superreload remembers objects previously in the module, and | |
@@ -371,7 +429,7 b' def superreload(module, reload=reload, old_objects=None):' | |||||
371 |
|
429 | |||
372 | # collect old objects in the module |
|
430 | # collect old objects in the module | |
373 | for name, obj in list(module.__dict__.items()): |
|
431 | for name, obj in list(module.__dict__.items()): | |
374 | if not hasattr(obj, '__module__') or obj.__module__ != module.__name__: |
|
432 | if not append_obj(module, old_objects, name, obj): | |
375 | continue |
|
433 | continue | |
376 | key = (module.__name__, name) |
|
434 | key = (module.__name__, name) | |
377 | try: |
|
435 | try: | |
@@ -385,8 +443,8 b' def superreload(module, reload=reload, old_objects=None):' | |||||
385 | old_dict = module.__dict__.copy() |
|
443 | old_dict = module.__dict__.copy() | |
386 | old_name = module.__name__ |
|
444 | old_name = module.__name__ | |
387 | module.__dict__.clear() |
|
445 | module.__dict__.clear() | |
388 |
module.__dict__[ |
|
446 | module.__dict__["__name__"] = old_name | |
389 |
module.__dict__[ |
|
447 | module.__dict__["__loader__"] = old_dict["__loader__"] | |
390 | except (TypeError, AttributeError, KeyError): |
|
448 | except (TypeError, AttributeError, KeyError): | |
391 | pass |
|
449 | pass | |
392 |
|
450 | |||
@@ -400,12 +458,21 b' def superreload(module, reload=reload, old_objects=None):' | |||||
400 | # iterate over all objects and update functions & classes |
|
458 | # iterate over all objects and update functions & classes | |
401 | for name, new_obj in list(module.__dict__.items()): |
|
459 | for name, new_obj in list(module.__dict__.items()): | |
402 | key = (module.__name__, name) |
|
460 | key = (module.__name__, name) | |
403 |
if key not in old_objects: |
|
461 | if key not in old_objects: | |
|
462 | # here 'shell' acts both as a flag and as an output var | |||
|
463 | if ( | |||
|
464 | shell is None | |||
|
465 | or name == "Enum" | |||
|
466 | or not append_obj(module, old_objects, name, new_obj, True) | |||
|
467 | ): | |||
|
468 | continue | |||
|
469 | shell.user_ns[name] = new_obj | |||
404 |
|
470 | |||
405 | new_refs = [] |
|
471 | new_refs = [] | |
406 | for old_ref in old_objects[key]: |
|
472 | for old_ref in old_objects[key]: | |
407 | old_obj = old_ref() |
|
473 | old_obj = old_ref() | |
408 |
if old_obj is None: |
|
474 | if old_obj is None: | |
|
475 | continue | |||
409 | new_refs.append(old_ref) |
|
476 | new_refs.append(old_ref) | |
410 | update_generic(old_obj, new_obj) |
|
477 | update_generic(old_obj, new_obj) | |
411 |
|
478 | |||
@@ -416,22 +483,25 b' def superreload(module, reload=reload, old_objects=None):' | |||||
416 |
|
483 | |||
417 | return module |
|
484 | return module | |
418 |
|
485 | |||
|
486 | ||||
419 | #------------------------------------------------------------------------------ |
|
487 | # ------------------------------------------------------------------------------ | |
420 | # IPython connectivity |
|
488 | # IPython connectivity | |
421 | #------------------------------------------------------------------------------ |
|
489 | # ------------------------------------------------------------------------------ | |
422 |
|
490 | |||
423 | from IPython.core.magic import Magics, magics_class, line_magic |
|
491 | from IPython.core.magic import Magics, magics_class, line_magic | |
424 |
|
492 | |||
|
493 | ||||
425 | @magics_class |
|
494 | @magics_class | |
426 | class AutoreloadMagics(Magics): |
|
495 | class AutoreloadMagics(Magics): | |
427 | def __init__(self, *a, **kw): |
|
496 | def __init__(self, *a, **kw): | |
428 |
super( |
|
497 | super().__init__(*a, **kw) | |
429 | self._reloader = ModuleReloader() |
|
498 | self._reloader = ModuleReloader(self.shell) | |
430 | self._reloader.check_all = False |
|
499 | self._reloader.check_all = False | |
|
500 | self._reloader.autoload_obj = False | |||
431 | self.loaded_modules = set(sys.modules) |
|
501 | self.loaded_modules = set(sys.modules) | |
432 |
|
502 | |||
433 | @line_magic |
|
503 | @line_magic | |
434 |
def autoreload(self, parameter_s= |
|
504 | def autoreload(self, parameter_s=""): | |
435 | r"""%autoreload => Reload modules automatically |
|
505 | r"""%autoreload => Reload modules automatically | |
436 |
|
506 | |||
437 | %autoreload |
|
507 | %autoreload | |
@@ -475,19 +545,24 b' class AutoreloadMagics(Magics):' | |||||
475 | autoreloaded. |
|
545 | autoreloaded. | |
476 |
|
546 | |||
477 | """ |
|
547 | """ | |
478 |
if parameter_s == |
|
548 | if parameter_s == "": | |
479 | self._reloader.check(True) |
|
549 | self._reloader.check(True) | |
480 |
elif parameter_s == |
|
550 | elif parameter_s == "0": | |
481 | self._reloader.enabled = False |
|
551 | self._reloader.enabled = False | |
482 |
elif parameter_s == |
|
552 | elif parameter_s == "1": | |
483 | self._reloader.check_all = False |
|
553 | self._reloader.check_all = False | |
484 | self._reloader.enabled = True |
|
554 | self._reloader.enabled = True | |
485 |
elif parameter_s == |
|
555 | elif parameter_s == "2": | |
486 | self._reloader.check_all = True |
|
556 | self._reloader.check_all = True | |
487 | self._reloader.enabled = True |
|
557 | self._reloader.enabled = True | |
|
558 | self._reloader.enabled = True | |||
|
559 | elif parameter_s == "3": | |||
|
560 | self._reloader.check_all = True | |||
|
561 | self._reloader.enabled = True | |||
|
562 | self._reloader.autoload_obj = True | |||
488 |
|
563 | |||
489 | @line_magic |
|
564 | @line_magic | |
490 |
def aimport(self, parameter_s= |
|
565 | def aimport(self, parameter_s="", stream=None): | |
491 | """%aimport => Import modules for automatic reloading. |
|
566 | """%aimport => Import modules for automatic reloading. | |
492 |
|
567 | |||
493 | %aimport |
|
568 | %aimport | |
@@ -511,13 +586,13 b' class AutoreloadMagics(Magics):' | |||||
511 | if self._reloader.check_all: |
|
586 | if self._reloader.check_all: | |
512 | stream.write("Modules to reload:\nall-except-skipped\n") |
|
587 | stream.write("Modules to reload:\nall-except-skipped\n") | |
513 | else: |
|
588 | else: | |
514 |
stream.write("Modules to reload:\n%s\n" % |
|
589 | stream.write("Modules to reload:\n%s\n" % " ".join(to_reload)) | |
515 |
stream.write("\nModules to skip:\n%s\n" % |
|
590 | stream.write("\nModules to skip:\n%s\n" % " ".join(to_skip)) | |
516 |
elif modname.startswith( |
|
591 | elif modname.startswith("-"): | |
517 | modname = modname[1:] |
|
592 | modname = modname[1:] | |
518 | self._reloader.mark_module_skipped(modname) |
|
593 | self._reloader.mark_module_skipped(modname) | |
519 | else: |
|
594 | else: | |
520 |
for _module in |
|
595 | for _module in [_.strip() for _ in modname.split(",")]: | |
521 | top_module, top_name = self._reloader.aimport_module(_module) |
|
596 | top_module, top_name = self._reloader.aimport_module(_module) | |
522 |
|
597 | |||
523 | # Inject module to user namespace |
|
598 | # Inject module to user namespace | |
@@ -531,8 +606,7 b' class AutoreloadMagics(Magics):' | |||||
531 | pass |
|
606 | pass | |
532 |
|
607 | |||
533 | def post_execute_hook(self): |
|
608 | def post_execute_hook(self): | |
534 | """Cache the modification times of any modules imported in this execution |
|
609 | """Cache the modification times of any modules imported in this execution""" | |
535 | """ |
|
|||
536 | newly_loaded_modules = set(sys.modules) - self.loaded_modules |
|
610 | newly_loaded_modules = set(sys.modules) - self.loaded_modules | |
537 | for modname in newly_loaded_modules: |
|
611 | for modname in newly_loaded_modules: | |
538 | _, pymtime = self._reloader.filename_and_mtime(sys.modules[modname]) |
|
612 | _, pymtime = self._reloader.filename_and_mtime(sys.modules[modname]) | |
@@ -546,5 +620,5 b' def load_ipython_extension(ip):' | |||||
546 | """Load the extension in IPython.""" |
|
620 | """Load the extension in IPython.""" | |
547 | auto_reload = AutoreloadMagics(ip) |
|
621 | auto_reload = AutoreloadMagics(ip) | |
548 | ip.register_magics(auto_reload) |
|
622 | ip.register_magics(auto_reload) | |
549 |
ip.events.register( |
|
623 | ip.events.register("pre_run_cell", auto_reload.pre_run_cell) | |
550 |
ip.events.register( |
|
624 | ip.events.register("post_execute", auto_reload.post_execute_hook) |
@@ -35,20 +35,20 b' from IPython.core.events import EventManager, pre_run_cell' | |||||
35 |
|
35 | |||
36 | noop = lambda *a, **kw: None |
|
36 | noop = lambda *a, **kw: None | |
37 |
|
37 | |||
38 | class FakeShell: |
|
|||
39 |
|
38 | |||
|
39 | class FakeShell: | |||
40 | def __init__(self): |
|
40 | def __init__(self): | |
41 | self.ns = {} |
|
41 | self.ns = {} | |
42 | self.user_ns = self.ns |
|
42 | self.user_ns = self.ns | |
43 | self.user_ns_hidden = {} |
|
43 | self.user_ns_hidden = {} | |
44 |
self.events = EventManager(self, { |
|
44 | self.events = EventManager(self, {"pre_run_cell", pre_run_cell}) | |
45 | self.auto_magics = AutoreloadMagics(shell=self) |
|
45 | self.auto_magics = AutoreloadMagics(shell=self) | |
46 |
self.events.register( |
|
46 | self.events.register("pre_run_cell", self.auto_magics.pre_run_cell) | |
47 |
|
47 | |||
48 | register_magics = set_hook = noop |
|
48 | register_magics = set_hook = noop | |
49 |
|
49 | |||
50 | def run_code(self, code): |
|
50 | def run_code(self, code): | |
51 |
self.events.trigger( |
|
51 | self.events.trigger("pre_run_cell") | |
52 | exec(code, self.user_ns) |
|
52 | exec(code, self.user_ns) | |
53 | self.auto_magics.post_execute_hook() |
|
53 | self.auto_magics.post_execute_hook() | |
54 |
|
54 | |||
@@ -111,20 +111,22 b' class Fixture(TestCase):' | |||||
111 | time.sleep(1.05) |
|
111 | time.sleep(1.05) | |
112 |
|
112 | |||
113 | # Write |
|
113 | # Write | |
114 |
with open(filename, |
|
114 | with open(filename, "w") as f: | |
115 | f.write(content) |
|
115 | f.write(content) | |
116 |
|
116 | |||
117 | def new_module(self, code): |
|
117 | def new_module(self, code): | |
118 | code = textwrap.dedent(code) |
|
118 | code = textwrap.dedent(code) | |
119 | mod_name, mod_fn = self.get_module() |
|
119 | mod_name, mod_fn = self.get_module() | |
120 |
with open(mod_fn, |
|
120 | with open(mod_fn, "w") as f: | |
121 | f.write(code) |
|
121 | f.write(code) | |
122 | return mod_name, mod_fn |
|
122 | return mod_name, mod_fn | |
123 |
|
123 | |||
|
124 | ||||
124 | #----------------------------------------------------------------------------- |
|
125 | # ----------------------------------------------------------------------------- | |
125 | # Test automatic reloading |
|
126 | # Test automatic reloading | |
126 | #----------------------------------------------------------------------------- |
|
127 | # ----------------------------------------------------------------------------- | |
127 |
|
128 | |||
|
129 | ||||
128 | def pickle_get_current_class(obj): |
|
130 | def pickle_get_current_class(obj): | |
129 | """ |
|
131 | """ | |
130 | Original issue comes from pickle; hence the name. |
|
132 | Original issue comes from pickle; hence the name. | |
@@ -136,25 +138,36 b' def pickle_get_current_class(obj):' | |||||
136 | obj2 = getattr(obj2, subpath) |
|
138 | obj2 = getattr(obj2, subpath) | |
137 | return obj2 |
|
139 | return obj2 | |
138 |
|
140 | |||
139 | class TestAutoreload(Fixture): |
|
|||
140 |
|
141 | |||
|
142 | class TestAutoreload(Fixture): | |||
141 | def test_reload_enums(self): |
|
143 | def test_reload_enums(self): | |
142 |
mod_name, mod_fn = self.new_module( |
|
144 | mod_name, mod_fn = self.new_module( | |
|
145 | textwrap.dedent( | |||
|
146 | """ | |||
143 |
|
|
147 | from enum import Enum | |
144 |
|
|
148 | class MyEnum(Enum): | |
145 | A = 'A' |
|
149 | A = 'A' | |
146 | B = 'B' |
|
150 | B = 'B' | |
147 |
|
|
151 | """ | |
|
152 | ) | |||
|
153 | ) | |||
148 | self.shell.magic_autoreload("2") |
|
154 | self.shell.magic_autoreload("2") | |
149 | self.shell.magic_aimport(mod_name) |
|
155 | self.shell.magic_aimport(mod_name) | |
150 |
self.write_file( |
|
156 | self.write_file( | |
|
157 | mod_fn, | |||
|
158 | textwrap.dedent( | |||
|
159 | """ | |||
151 |
|
|
160 | from enum import Enum | |
152 |
|
|
161 | class MyEnum(Enum): | |
153 | A = 'A' |
|
162 | A = 'A' | |
154 | B = 'B' |
|
163 | B = 'B' | |
155 | C = 'C' |
|
164 | C = 'C' | |
156 |
|
|
165 | """ | |
157 | with tt.AssertNotPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'): |
|
166 | ), | |
|
167 | ) | |||
|
168 | with tt.AssertNotPrints( | |||
|
169 | ("[autoreload of %s failed:" % mod_name), channel="stderr" | |||
|
170 | ): | |||
158 | self.shell.run_code("pass") # trigger another reload |
|
171 | self.shell.run_code("pass") # trigger another reload | |
159 |
|
172 | |||
160 | def test_reload_class_type(self): |
|
173 | def test_reload_class_type(self): | |
@@ -195,7 +208,9 b' class TestAutoreload(Fixture):' | |||||
195 |
|
208 | |||
196 | def test_reload_class_attributes(self): |
|
209 | def test_reload_class_attributes(self): | |
197 | self.shell.magic_autoreload("2") |
|
210 | self.shell.magic_autoreload("2") | |
198 |
mod_name, mod_fn = self.new_module( |
|
211 | mod_name, mod_fn = self.new_module( | |
|
212 | textwrap.dedent( | |||
|
213 | """ | |||
199 |
|
|
214 | class MyClass: | |
200 |
|
|
215 | ||
201 |
|
|
216 | def __init__(self, a=10): | |
@@ -241,16 +256,99 b' class TestAutoreload(Fixture):' | |||||
241 |
|
256 | |||
242 | self.shell.run_code("second = MyClass(5)") |
|
257 | self.shell.run_code("second = MyClass(5)") | |
243 |
|
258 | |||
244 |
for object_name in { |
|
259 | for object_name in {"first", "second"}: | |
245 |
self.shell.run_code("{object_name}.power(5)" |
|
260 | self.shell.run_code(f"{object_name}.power(5)") | |
246 | with nt.assert_raises(AttributeError): |
|
261 | with nt.assert_raises(AttributeError): | |
247 |
self.shell.run_code("{object_name}.cube()" |
|
262 | self.shell.run_code(f"{object_name}.cube()") | |
248 | with nt.assert_raises(AttributeError): |
|
263 | with nt.assert_raises(AttributeError): | |
249 |
self.shell.run_code("{object_name}.square()" |
|
264 | self.shell.run_code(f"{object_name}.square()") | |
250 |
self.shell.run_code("{object_name}.b" |
|
265 | self.shell.run_code(f"{object_name}.b") | |
251 |
self.shell.run_code("{object_name}.a" |
|
266 | self.shell.run_code(f"{object_name}.a") | |
252 | with nt.assert_raises(AttributeError): |
|
267 | with nt.assert_raises(AttributeError): | |
253 |
self.shell.run_code("{object_name}.toto" |
|
268 | self.shell.run_code(f"{object_name}.toto") | |
|
269 | ||||
|
270 | def test_autoload_newly_added_objects(self): | |||
|
271 | self.shell.magic_autoreload("3") | |||
|
272 | mod_code = """ | |||
|
273 | def func1(): pass | |||
|
274 | """ | |||
|
275 | mod_name, mod_fn = self.new_module(textwrap.dedent(mod_code)) | |||
|
276 | self.shell.run_code(f"from {mod_name} import *") | |||
|
277 | self.shell.run_code("func1()") | |||
|
278 | with nt.assert_raises(NameError): | |||
|
279 | self.shell.run_code("func2()") | |||
|
280 | with nt.assert_raises(NameError): | |||
|
281 | self.shell.run_code("t = Test()") | |||
|
282 | with nt.assert_raises(NameError): | |||
|
283 | self.shell.run_code("number") | |||
|
284 | ||||
|
285 | # ----------- TEST NEW OBJ LOADED -------------------------- | |||
|
286 | ||||
|
287 | new_code = """ | |||
|
288 | def func1(): pass | |||
|
289 | def func2(): pass | |||
|
290 | class Test: pass | |||
|
291 | number = 0 | |||
|
292 | from enum import Enum | |||
|
293 | class TestEnum(Enum): | |||
|
294 | A = 'a' | |||
|
295 | """ | |||
|
296 | self.write_file(mod_fn, textwrap.dedent(new_code)) | |||
|
297 | ||||
|
298 | # test function now exists in shell's namespace namespace | |||
|
299 | self.shell.run_code("func2()") | |||
|
300 | # test function now exists in module's dict | |||
|
301 | self.shell.run_code(f"import sys; sys.modules['{mod_name}'].func2()") | |||
|
302 | # test class now exists | |||
|
303 | self.shell.run_code("t = Test()") | |||
|
304 | # test global built-in var now exists | |||
|
305 | self.shell.run_code("number") | |||
|
306 | # test the enumerations gets loaded succesfully | |||
|
307 | self.shell.run_code("TestEnum.A") | |||
|
308 | ||||
|
309 | # ----------- TEST NEW OBJ CAN BE CHANGED -------------------- | |||
|
310 | ||||
|
311 | new_code = """ | |||
|
312 | def func1(): return 'changed' | |||
|
313 | def func2(): return 'changed' | |||
|
314 | class Test: | |||
|
315 | def new_func(self): | |||
|
316 | return 'changed' | |||
|
317 | number = 1 | |||
|
318 | from enum import Enum | |||
|
319 | class TestEnum(Enum): | |||
|
320 | A = 'a' | |||
|
321 | B = 'added' | |||
|
322 | """ | |||
|
323 | self.write_file(mod_fn, textwrap.dedent(new_code)) | |||
|
324 | self.shell.run_code("assert func1() == 'changed'") | |||
|
325 | self.shell.run_code("assert func2() == 'changed'") | |||
|
326 | self.shell.run_code("t = Test(); assert t.new_func() == 'changed'") | |||
|
327 | self.shell.run_code("assert number == 1") | |||
|
328 | self.shell.run_code("assert TestEnum.B.value == 'added'") | |||
|
329 | ||||
|
330 | # ----------- TEST IMPORT FROM MODULE -------------------------- | |||
|
331 | ||||
|
332 | new_mod_code = """ | |||
|
333 | from enum import Enum | |||
|
334 | class Ext(Enum): | |||
|
335 | A = 'ext' | |||
|
336 | def ext_func(): | |||
|
337 | return 'ext' | |||
|
338 | class ExtTest: | |||
|
339 | def meth(self): | |||
|
340 | return 'ext' | |||
|
341 | ext_int = 2 | |||
|
342 | """ | |||
|
343 | new_mod_name, new_mod_fn = self.new_module(textwrap.dedent(new_mod_code)) | |||
|
344 | current_mod_code = f""" | |||
|
345 | from {new_mod_name} import * | |||
|
346 | """ | |||
|
347 | self.write_file(mod_fn, textwrap.dedent(current_mod_code)) | |||
|
348 | self.shell.run_code("assert Ext.A.value == 'ext'") | |||
|
349 | self.shell.run_code("assert ext_func() == 'ext'") | |||
|
350 | self.shell.run_code("t = ExtTest(); assert t.meth() == 'ext'") | |||
|
351 | self.shell.run_code("assert ext_int == 2") | |||
254 |
|
352 | |||
255 | def _check_smoketest(self, use_aimport=True): |
|
353 | def _check_smoketest(self, use_aimport=True): | |
256 | """ |
|
354 | """ | |
@@ -258,7 +356,8 b' class TestAutoreload(Fixture):' | |||||
258 | '%autoreload 1' or '%autoreload 2' |
|
356 | '%autoreload 1' or '%autoreload 2' | |
259 | """ |
|
357 | """ | |
260 |
|
358 | |||
261 |
mod_name, mod_fn = self.new_module( |
|
359 | mod_name, mod_fn = self.new_module( | |
|
360 | """ | |||
262 |
|
|
361 | x = 9 | |
263 |
|
|
362 | ||
264 |
|
|
363 | z = 123 # this item will be deleted | |
@@ -281,7 +380,8 b' class Baz(object):' | |||||
281 | class Bar: # old-style class: weakref doesn't work for it on Python < 2.7 |
|
380 | class Bar: # old-style class: weakref doesn't work for it on Python < 2.7 | |
282 |
|
|
381 | def foo(self): | |
283 |
|
|
382 | return 1 | |
284 |
|
|
383 | """ | |
|
384 | ) | |||
285 |
|
385 | |||
286 | # |
|
386 | # | |
287 | # Import module, and mark for reloading |
|
387 | # Import module, and mark for reloading | |
@@ -300,8 +400,9 b" class Bar: # old-style class: weakref doesn't work for it on Python < 2.7" | |||||
300 | self.shell.run_code("import %s" % mod_name) |
|
400 | self.shell.run_code("import %s" % mod_name) | |
301 | stream = StringIO() |
|
401 | stream = StringIO() | |
302 | self.shell.magic_aimport("", stream=stream) |
|
402 | self.shell.magic_aimport("", stream=stream) | |
303 | nt.assert_true("Modules to reload:\nall-except-skipped" in |
|
403 | nt.assert_true( | |
304 |
|
|
404 | "Modules to reload:\nall-except-skipped" in stream.getvalue() | |
|
405 | ) | |||
305 | nt.assert_in(mod_name, self.shell.ns) |
|
406 | nt.assert_in(mod_name, self.shell.ns) | |
306 |
|
407 | |||
307 | mod = sys.modules[mod_name] |
|
408 | mod = sys.modules[mod_name] | |
@@ -336,20 +437,29 b" class Bar: # old-style class: weakref doesn't work for it on Python < 2.7" | |||||
336 | # Simulate a failed reload: no reload should occur and exactly |
|
437 | # Simulate a failed reload: no reload should occur and exactly | |
337 | # one error message should be printed |
|
438 | # one error message should be printed | |
338 | # |
|
439 | # | |
339 |
self.write_file( |
|
440 | self.write_file( | |
|
441 | mod_fn, | |||
|
442 | """ | |||
340 |
|
|
443 | a syntax error | |
341 |
|
|
444 | """, | |
|
445 | ) | |||
342 |
|
446 | |||
343 | with tt.AssertPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'): |
|
447 | with tt.AssertPrints( | |
|
448 | ("[autoreload of %s failed:" % mod_name), channel="stderr" | |||
|
449 | ): | |||
344 | self.shell.run_code("pass") # trigger reload |
|
450 | self.shell.run_code("pass") # trigger reload | |
345 | with tt.AssertNotPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'): |
|
451 | with tt.AssertNotPrints( | |
|
452 | ("[autoreload of %s failed:" % mod_name), channel="stderr" | |||
|
453 | ): | |||
346 | self.shell.run_code("pass") # trigger another reload |
|
454 | self.shell.run_code("pass") # trigger another reload | |
347 | check_module_contents() |
|
455 | check_module_contents() | |
348 |
|
456 | |||
349 | # |
|
457 | # | |
350 | # Rewrite module (this time reload should succeed) |
|
458 | # Rewrite module (this time reload should succeed) | |
351 | # |
|
459 | # | |
352 |
self.write_file( |
|
460 | self.write_file( | |
|
461 | mod_fn, | |||
|
462 | """ | |||
353 |
|
|
463 | x = 10 | |
354 |
|
|
464 | ||
355 |
|
|
465 | def foo(y): | |
@@ -367,11 +477,12 b' class Baz(object):' | |||||
367 |
|
|
477 | class Bar: # old-style class | |
368 |
|
|
478 | def foo(self): | |
369 |
|
|
479 | return 2 | |
370 |
|
|
480 | """, | |
|
481 | ) | |||
371 |
|
482 | |||
372 | def check_module_contents(): |
|
483 | def check_module_contents(): | |
373 | nt.assert_equal(mod.x, 10) |
|
484 | nt.assert_equal(mod.x, 10) | |
374 |
nt.assert_false(hasattr(mod, |
|
485 | nt.assert_false(hasattr(mod, "z")) | |
375 |
|
486 | |||
376 | nt.assert_equal(old_foo(0), 4) # superreload magic! |
|
487 | nt.assert_equal(old_foo(0), 4) # superreload magic! | |
377 | nt.assert_equal(mod.foo(0), 4) |
|
488 | nt.assert_equal(mod.foo(0), 4) | |
@@ -383,8 +494,8 b' class Bar: # old-style class' | |||||
383 | nt.assert_equal(old_obj.quux, 43) |
|
494 | nt.assert_equal(old_obj.quux, 43) | |
384 | nt.assert_equal(obj.quux, 43) |
|
495 | nt.assert_equal(obj.quux, 43) | |
385 |
|
496 | |||
386 |
nt.assert_false(hasattr(old_obj, |
|
497 | nt.assert_false(hasattr(old_obj, "zzz")) | |
387 |
nt.assert_false(hasattr(obj, |
|
498 | nt.assert_false(hasattr(obj, "zzz")) | |
388 |
|
499 | |||
389 | obj2 = mod.Bar() |
|
500 | obj2 = mod.Bar() | |
390 | nt.assert_equal(old_obj2.foo(), 2) |
|
501 | nt.assert_equal(old_obj2.foo(), 2) | |
@@ -408,17 +519,19 b' class Bar: # old-style class' | |||||
408 | self.shell.magic_aimport("-" + mod_name) |
|
519 | self.shell.magic_aimport("-" + mod_name) | |
409 | stream = StringIO() |
|
520 | stream = StringIO() | |
410 | self.shell.magic_aimport("", stream=stream) |
|
521 | self.shell.magic_aimport("", stream=stream) | |
411 | nt.assert_true(("Modules to skip:\n%s" % mod_name) in |
|
522 | nt.assert_true(("Modules to skip:\n%s" % mod_name) in stream.getvalue()) | |
412 | stream.getvalue()) |
|
|||
413 |
|
523 | |||
414 | # This should succeed, although no such module exists |
|
524 | # This should succeed, although no such module exists | |
415 | self.shell.magic_aimport("-tmpmod_as318989e89ds") |
|
525 | self.shell.magic_aimport("-tmpmod_as318989e89ds") | |
416 | else: |
|
526 | else: | |
417 | self.shell.magic_autoreload("0") |
|
527 | self.shell.magic_autoreload("0") | |
418 |
|
528 | |||
419 |
self.write_file( |
|
529 | self.write_file( | |
|
530 | mod_fn, | |||
|
531 | """ | |||
420 |
|
|
532 | x = -99 | |
421 |
|
|
533 | """, | |
|
534 | ) | |||
422 |
|
535 | |||
423 | self.shell.run_code("pass") # trigger reload |
|
536 | self.shell.run_code("pass") # trigger reload | |
424 | self.shell.run_code("pass") |
|
537 | self.shell.run_code("pass") | |
@@ -440,8 +553,3 b' x = -99' | |||||
440 |
|
553 | |||
441 | def test_smoketest_autoreload(self): |
|
554 | def test_smoketest_autoreload(self): | |
442 | self._check_smoketest(use_aimport=False) |
|
555 | self._check_smoketest(use_aimport=False) | |
443 |
|
||||
444 |
|
||||
445 |
|
||||
446 |
|
||||
447 |
|
@@ -32,15 +32,42 b' import os' | |||||
32 | import sys |
|
32 | import sys | |
33 |
|
33 | |||
34 | from IPython.utils.version import check_version |
|
34 | from IPython.utils.version import check_version | |
35 |
from IPython.external.qt_loaders import ( |
|
35 | from IPython.external.qt_loaders import ( | |
36 | QT_API_PYSIDE2, QT_API_PYQT, QT_API_PYQT5, |
|
36 | load_qt, | |
37 | QT_API_PYQTv1, QT_API_PYQT_DEFAULT) |
|
37 | loaded_api, | |
|
38 | enum_factory, | |||
|
39 | # QT6 | |||
|
40 | QT_API_PYQT6, | |||
|
41 | QT_API_PYSIDE6, | |||
|
42 | # QT5 | |||
|
43 | QT_API_PYQT5, | |||
|
44 | QT_API_PYSIDE2, | |||
|
45 | # QT4 | |||
|
46 | QT_API_PYQTv1, | |||
|
47 | QT_API_PYQT, | |||
|
48 | QT_API_PYSIDE, | |||
|
49 | # default | |||
|
50 | QT_API_PYQT_DEFAULT, | |||
|
51 | ) | |||
|
52 | ||||
|
53 | _qt_apis = ( | |||
|
54 | # QT6 | |||
|
55 | QT_API_PYQT6, | |||
|
56 | QT_API_PYSIDE6, | |||
|
57 | # QT5 | |||
|
58 | QT_API_PYQT5, | |||
|
59 | QT_API_PYSIDE2, | |||
|
60 | # QT4 | |||
|
61 | QT_API_PYQTv1, | |||
|
62 | QT_API_PYQT, | |||
|
63 | QT_API_PYSIDE, | |||
|
64 | # default | |||
|
65 | QT_API_PYQT_DEFAULT, | |||
|
66 | ) | |||
38 |
|
67 | |||
39 | _qt_apis = (QT_API_PYSIDE, QT_API_PYSIDE2, QT_API_PYQT, QT_API_PYQT5, QT_API_PYQTv1, |
|
|||
40 | QT_API_PYQT_DEFAULT) |
|
|||
41 |
|
68 | |||
42 | #Constraints placed on an imported matplotlib |
|
|||
43 | def matplotlib_options(mpl): |
|
69 | def matplotlib_options(mpl): | |
|
70 | """Constraints placed on an imported matplotlib.""" | |||
44 | if mpl is None: |
|
71 | if mpl is None: | |
45 | return |
|
72 | return | |
46 | backend = mpl.rcParams.get('backend', None) |
|
73 | backend = mpl.rcParams.get('backend', None) | |
@@ -66,9 +93,7 b' def matplotlib_options(mpl):' | |||||
66 | mpqt) |
|
93 | mpqt) | |
67 |
|
94 | |||
68 | def get_options(): |
|
95 | def get_options(): | |
69 | """Return a list of acceptable QT APIs, in decreasing order of |
|
96 | """Return a list of acceptable QT APIs, in decreasing order of preference.""" | |
70 | preference |
|
|||
71 | """ |
|
|||
72 | #already imported Qt somewhere. Use that |
|
97 | #already imported Qt somewhere. Use that | |
73 | loaded = loaded_api() |
|
98 | loaded = loaded_api() | |
74 | if loaded is not None: |
|
99 | if loaded is not None: | |
@@ -83,13 +108,22 b' def get_options():' | |||||
83 | qt_api = os.environ.get('QT_API', None) |
|
108 | qt_api = os.environ.get('QT_API', None) | |
84 | if qt_api is None: |
|
109 | if qt_api is None: | |
85 | #no ETS variable. Ask mpl, then use default fallback path |
|
110 | #no ETS variable. Ask mpl, then use default fallback path | |
86 |
return matplotlib_options(mpl) or [ |
|
111 | return matplotlib_options(mpl) or [ | |
87 | QT_API_PYQT5, QT_API_PYSIDE2] |
|
112 | QT_API_PYQT_DEFAULT, | |
|
113 | QT_API_PYQT6, | |||
|
114 | QT_API_PYSIDE6, | |||
|
115 | QT_API_PYQT5, | |||
|
116 | QT_API_PYSIDE2, | |||
|
117 | QT_API_PYQT, | |||
|
118 | QT_API_PYSIDE, | |||
|
119 | ] | |||
88 | elif qt_api not in _qt_apis: |
|
120 | elif qt_api not in _qt_apis: | |
89 | raise RuntimeError("Invalid Qt API %r, valid values are: %r" % |
|
121 | raise RuntimeError("Invalid Qt API %r, valid values are: %r" % | |
90 | (qt_api, ', '.join(_qt_apis))) |
|
122 | (qt_api, ', '.join(_qt_apis))) | |
91 | else: |
|
123 | else: | |
92 | return [qt_api] |
|
124 | return [qt_api] | |
93 |
|
125 | |||
|
126 | ||||
94 | api_opts = get_options() |
|
127 | api_opts = get_options() | |
95 | QtCore, QtGui, QtSvg, QT_API = load_qt(api_opts) |
|
128 | QtCore, QtGui, QtSvg, QT_API = load_qt(api_opts) | |
|
129 | enum_helper = enum_factory(QT_API, QtCore) |
@@ -10,25 +10,40 b' be accessed directly from the outside' | |||||
10 | """ |
|
10 | """ | |
11 | import sys |
|
11 | import sys | |
12 | import types |
|
12 | import types | |
13 | from functools import partial |
|
13 | from functools import partial, lru_cache | |
14 | from importlib import import_module |
|
14 | import operator | |
15 |
|
15 | |||
16 | from IPython.utils.version import check_version |
|
16 | from IPython.utils.version import check_version | |
17 |
|
17 | |||
18 | # Available APIs. |
|
18 | # ### Available APIs. | |
19 | QT_API_PYQT = 'pyqt' # Force version 2 |
|
19 | # Qt6 | |
|
20 | QT_API_PYQT6 = "pyqt6" | |||
|
21 | QT_API_PYSIDE6 = "pyside6" | |||
|
22 | ||||
|
23 | # Qt5 | |||
20 | QT_API_PYQT5 = 'pyqt5' |
|
24 | QT_API_PYQT5 = 'pyqt5' | |
21 | QT_API_PYQTv1 = 'pyqtv1' # Force version 2 |
|
|||
22 | QT_API_PYQT_DEFAULT = 'pyqtdefault' # use system default for version 1 vs. 2 |
|
|||
23 | QT_API_PYSIDE = 'pyside' |
|
|||
24 | QT_API_PYSIDE2 = 'pyside2' |
|
25 | QT_API_PYSIDE2 = 'pyside2' | |
25 |
|
26 | |||
26 | api_to_module = {QT_API_PYSIDE2: 'PySide2', |
|
27 | # Qt4 | |
27 | QT_API_PYSIDE: 'PySide', |
|
28 | QT_API_PYQT = "pyqt" # Force version 2 | |
28 | QT_API_PYQT: 'PyQt4', |
|
29 | QT_API_PYQTv1 = "pyqtv1" # Force version 2 | |
29 | QT_API_PYQTv1: 'PyQt4', |
|
30 | QT_API_PYSIDE = "pyside" | |
30 | QT_API_PYQT5: 'PyQt5', |
|
31 | ||
31 | QT_API_PYQT_DEFAULT: 'PyQt4', |
|
32 | QT_API_PYQT_DEFAULT = "pyqtdefault" # use system default for version 1 vs. 2 | |
|
33 | ||||
|
34 | api_to_module = { | |||
|
35 | # Qt6 | |||
|
36 | QT_API_PYQT6: "PyQt6", | |||
|
37 | QT_API_PYSIDE6: "PySide6", | |||
|
38 | # Qt5 | |||
|
39 | QT_API_PYQT5: "PyQt5", | |||
|
40 | QT_API_PYSIDE2: "PySide2", | |||
|
41 | # Qt4 | |||
|
42 | QT_API_PYSIDE: "PySide", | |||
|
43 | QT_API_PYQT: "PyQt4", | |||
|
44 | QT_API_PYQTv1: "PyQt4", | |||
|
45 | # default | |||
|
46 | QT_API_PYQT_DEFAULT: "PyQt6", | |||
32 | } |
|
47 | } | |
33 |
|
48 | |||
34 |
|
49 | |||
@@ -56,6 +71,7 b' class ImportDenier(object):' | |||||
56 | already imported an Incompatible QT Binding: %s |
|
71 | already imported an Incompatible QT Binding: %s | |
57 | """ % (fullname, loaded_api())) |
|
72 | """ % (fullname, loaded_api())) | |
58 |
|
73 | |||
|
74 | ||||
59 | ID = ImportDenier() |
|
75 | ID = ImportDenier() | |
60 | sys.meta_path.insert(0, ID) |
|
76 | sys.meta_path.insert(0, ID) | |
61 |
|
77 | |||
@@ -63,23 +79,11 b' sys.meta_path.insert(0, ID)' | |||||
63 | def commit_api(api): |
|
79 | def commit_api(api): | |
64 | """Commit to a particular API, and trigger ImportErrors on subsequent |
|
80 | """Commit to a particular API, and trigger ImportErrors on subsequent | |
65 | dangerous imports""" |
|
81 | dangerous imports""" | |
|
82 | modules = set(api_to_module.values()) | |||
66 |
|
83 | |||
67 | if api == QT_API_PYSIDE2: |
|
84 | modules.remove(api_to_module[api]) | |
68 | ID.forbid('PySide') |
|
85 | for mod in modules: | |
69 |
ID.forbid( |
|
86 | ID.forbid(mod) | |
70 | ID.forbid('PyQt5') |
|
|||
71 | elif api == QT_API_PYSIDE: |
|
|||
72 | ID.forbid('PySide2') |
|
|||
73 | ID.forbid('PyQt4') |
|
|||
74 | ID.forbid('PyQt5') |
|
|||
75 | elif api == QT_API_PYQT5: |
|
|||
76 | ID.forbid('PySide2') |
|
|||
77 | ID.forbid('PySide') |
|
|||
78 | ID.forbid('PyQt4') |
|
|||
79 | else: # There are three other possibilities, all representing PyQt4 |
|
|||
80 | ID.forbid('PyQt5') |
|
|||
81 | ID.forbid('PySide2') |
|
|||
82 | ID.forbid('PySide') |
|
|||
83 |
|
87 | |||
84 |
|
88 | |||
85 | def loaded_api(): |
|
89 | def loaded_api(): | |
@@ -90,19 +94,24 b' def loaded_api():' | |||||
90 |
|
94 | |||
91 | Returns |
|
95 | Returns | |
92 | ------- |
|
96 | ------- | |
93 |
None, 'pyside2', 'pyside', 'pyqt', 'pyqt5', |
|
97 | None, 'pyside6', 'pyqt6', 'pyside2', 'pyside', 'pyqt', 'pyqt5', 'pyqtv1' | |
94 | """ |
|
98 | """ | |
95 | if 'PyQt4.QtCore' in sys.modules: |
|
99 | if sys.modules.get("PyQt6.QtCore"): | |
|
100 | return QT_API_PYQT6 | |||
|
101 | elif sys.modules.get("PySide6.QtCore"): | |||
|
102 | return QT_API_PYSIDE6 | |||
|
103 | elif sys.modules.get("PyQt5.QtCore"): | |||
|
104 | return QT_API_PYQT5 | |||
|
105 | elif sys.modules.get("PySide2.QtCore"): | |||
|
106 | return QT_API_PYSIDE2 | |||
|
107 | elif sys.modules.get("PyQt4.QtCore"): | |||
96 | if qtapi_version() == 2: |
|
108 | if qtapi_version() == 2: | |
97 | return QT_API_PYQT |
|
109 | return QT_API_PYQT | |
98 | else: |
|
110 | else: | |
99 | return QT_API_PYQTv1 |
|
111 | return QT_API_PYQTv1 | |
100 | elif 'PySide.QtCore' in sys.modules: |
|
112 | elif sys.modules.get("PySide.QtCore"): | |
101 | return QT_API_PYSIDE |
|
113 | return QT_API_PYSIDE | |
102 | elif 'PySide2.QtCore' in sys.modules: |
|
114 | ||
103 | return QT_API_PYSIDE2 |
|
|||
104 | elif 'PyQt5.QtCore' in sys.modules: |
|
|||
105 | return QT_API_PYQT5 |
|
|||
106 | return None |
|
115 | return None | |
107 |
|
116 | |||
108 |
|
117 | |||
@@ -122,7 +131,7 b' def has_binding(api):' | |||||
122 | from importlib.util import find_spec |
|
131 | from importlib.util import find_spec | |
123 |
|
132 | |||
124 | required = ['QtCore', 'QtGui', 'QtSvg'] |
|
133 | required = ['QtCore', 'QtGui', 'QtSvg'] | |
125 | if api in (QT_API_PYQT5, QT_API_PYSIDE2): |
|
134 | if api in (QT_API_PYQT5, QT_API_PYSIDE2, QT_API_PYQT6, QT_API_PYSIDE6): | |
126 | # QT5 requires QtWidgets too |
|
135 | # QT5 requires QtWidgets too | |
127 | required.append('QtWidgets') |
|
136 | required.append('QtWidgets') | |
128 |
|
137 | |||
@@ -174,7 +183,7 b' def can_import(api):' | |||||
174 |
|
183 | |||
175 | current = loaded_api() |
|
184 | current = loaded_api() | |
176 | if api == QT_API_PYQT_DEFAULT: |
|
185 | if api == QT_API_PYQT_DEFAULT: | |
177 |
return current in [QT_API_PYQT |
|
186 | return current in [QT_API_PYQT6, None] | |
178 | else: |
|
187 | else: | |
179 | return current in [api, None] |
|
188 | return current in [api, None] | |
180 |
|
189 | |||
@@ -238,6 +247,28 b' def import_pyqt5():' | |||||
238 | return QtCore, QtGuiCompat, QtSvg, api |
|
247 | return QtCore, QtGuiCompat, QtSvg, api | |
239 |
|
248 | |||
240 |
|
249 | |||
|
250 | def import_pyqt6(): | |||
|
251 | """ | |||
|
252 | Import PyQt6 | |||
|
253 | ||||
|
254 | ImportErrors rasied within this function are non-recoverable | |||
|
255 | """ | |||
|
256 | ||||
|
257 | from PyQt6 import QtCore, QtSvg, QtWidgets, QtGui | |||
|
258 | ||||
|
259 | # Alias PyQt-specific functions for PySide compatibility. | |||
|
260 | QtCore.Signal = QtCore.pyqtSignal | |||
|
261 | QtCore.Slot = QtCore.pyqtSlot | |||
|
262 | ||||
|
263 | # Join QtGui and QtWidgets for Qt4 compatibility. | |||
|
264 | QtGuiCompat = types.ModuleType("QtGuiCompat") | |||
|
265 | QtGuiCompat.__dict__.update(QtGui.__dict__) | |||
|
266 | QtGuiCompat.__dict__.update(QtWidgets.__dict__) | |||
|
267 | ||||
|
268 | api = QT_API_PYQT6 | |||
|
269 | return QtCore, QtGuiCompat, QtSvg, api | |||
|
270 | ||||
|
271 | ||||
241 | def import_pyside(): |
|
272 | def import_pyside(): | |
242 | """ |
|
273 | """ | |
243 | Import PySide |
|
274 | Import PySide | |
@@ -264,6 +295,23 b' def import_pyside2():' | |||||
264 | return QtCore, QtGuiCompat, QtSvg, QT_API_PYSIDE2 |
|
295 | return QtCore, QtGuiCompat, QtSvg, QT_API_PYSIDE2 | |
265 |
|
296 | |||
266 |
|
297 | |||
|
298 | def import_pyside6(): | |||
|
299 | """ | |||
|
300 | Import PySide6 | |||
|
301 | ||||
|
302 | ImportErrors raised within this function are non-recoverable | |||
|
303 | """ | |||
|
304 | from PySide6 import QtGui, QtCore, QtSvg, QtWidgets, QtPrintSupport | |||
|
305 | ||||
|
306 | # Join QtGui and QtWidgets for Qt4 compatibility. | |||
|
307 | QtGuiCompat = types.ModuleType("QtGuiCompat") | |||
|
308 | QtGuiCompat.__dict__.update(QtGui.__dict__) | |||
|
309 | QtGuiCompat.__dict__.update(QtWidgets.__dict__) | |||
|
310 | QtGuiCompat.__dict__.update(QtPrintSupport.__dict__) | |||
|
311 | ||||
|
312 | return QtCore, QtGuiCompat, QtSvg, QT_API_PYSIDE6 | |||
|
313 | ||||
|
314 | ||||
267 | def load_qt(api_options): |
|
315 | def load_qt(api_options): | |
268 | """ |
|
316 | """ | |
269 | Attempt to import Qt, given a preference list |
|
317 | Attempt to import Qt, given a preference list | |
@@ -291,12 +339,18 b' def load_qt(api_options):' | |||||
291 | an incompatible library has already been installed) |
|
339 | an incompatible library has already been installed) | |
292 | """ |
|
340 | """ | |
293 | loaders = { |
|
341 | loaders = { | |
|
342 | # Qt6 | |||
|
343 | QT_API_PYQT6: import_pyqt6, | |||
|
344 | QT_API_PYSIDE6: import_pyside6, | |||
|
345 | # Qt5 | |||
|
346 | QT_API_PYQT5: import_pyqt5, | |||
294 |
|
|
347 | QT_API_PYSIDE2: import_pyside2, | |
|
348 | # Qt4 | |||
295 |
|
|
349 | QT_API_PYSIDE: import_pyside, | |
296 |
|
|
350 | QT_API_PYQT: import_pyqt4, | |
297 | QT_API_PYQT5: import_pyqt5, |
|
|||
298 |
|
|
351 | QT_API_PYQTv1: partial(import_pyqt4, version=1), | |
299 | QT_API_PYQT_DEFAULT: partial(import_pyqt4, version=None) |
|
352 | # default | |
|
353 | QT_API_PYQT_DEFAULT: import_pyqt6, | |||
300 | } |
|
354 | } | |
301 |
|
355 | |||
302 | for api in api_options: |
|
356 | for api in api_options: | |
@@ -332,3 +386,16 b' def load_qt(api_options):' | |||||
332 | has_binding(QT_API_PYSIDE), |
|
386 | has_binding(QT_API_PYSIDE), | |
333 | has_binding(QT_API_PYSIDE2), |
|
387 | has_binding(QT_API_PYSIDE2), | |
334 | api_options)) |
|
388 | api_options)) | |
|
389 | ||||
|
390 | ||||
|
391 | def enum_factory(QT_API, QtCore): | |||
|
392 | """Construct an enum helper to account for PyQt5 <-> PyQt6 changes.""" | |||
|
393 | ||||
|
394 | @lru_cache(None) | |||
|
395 | def _enum(name): | |||
|
396 | # foo.bar.Enum.Entry (PyQt6) <=> foo.bar.Entry (non-PyQt6). | |||
|
397 | return operator.attrgetter( | |||
|
398 | name if QT_API == QT_API_PYQT6 else name.rpartition(".")[0] | |||
|
399 | )(sys.modules[QtCore.__package__]) | |||
|
400 | ||||
|
401 | return _enum |
@@ -8,6 +8,8 b' from os import walk, sep, fsdecode' | |||||
8 |
|
8 | |||
9 | from IPython.core.display import DisplayObject, TextDisplayObject |
|
9 | from IPython.core.display import DisplayObject, TextDisplayObject | |
10 |
|
10 | |||
|
11 | from typing import Tuple, Iterable | |||
|
12 | ||||
11 | __all__ = ['Audio', 'IFrame', 'YouTubeVideo', 'VimeoVideo', 'ScribdDocument', |
|
13 | __all__ = ['Audio', 'IFrame', 'YouTubeVideo', 'VimeoVideo', 'ScribdDocument', | |
12 | 'FileLink', 'FileLinks', 'Code'] |
|
14 | 'FileLink', 'FileLinks', 'Code'] | |
13 |
|
15 | |||
@@ -159,7 +161,7 b' class Audio(DisplayObject):' | |||||
159 | return val |
|
161 | return val | |
160 |
|
162 | |||
161 | @staticmethod |
|
163 | @staticmethod | |
162 | def _validate_and_normalize_with_numpy(data, normalize): |
|
164 | def _validate_and_normalize_with_numpy(data, normalize) -> Tuple[bytes, int]: | |
163 | import numpy as np |
|
165 | import numpy as np | |
164 |
|
166 | |||
165 | data = np.array(data, dtype=float) |
|
167 | data = np.array(data, dtype=float) | |
@@ -178,8 +180,7 b' class Audio(DisplayObject):' | |||||
178 | max_abs_value = np.max(np.abs(data)) |
|
180 | max_abs_value = np.max(np.abs(data)) | |
179 | normalization_factor = Audio._get_normalization_factor(max_abs_value, normalize) |
|
181 | normalization_factor = Audio._get_normalization_factor(max_abs_value, normalize) | |
180 | scaled = data / normalization_factor * 32767 |
|
182 | scaled = data / normalization_factor * 32767 | |
181 |
return scaled.astype( |
|
183 | return scaled.astype("<h").tobytes(), nchan | |
182 |
|
||||
183 |
|
184 | |||
184 | @staticmethod |
|
185 | @staticmethod | |
185 | def _validate_and_normalize_without_numpy(data, normalize): |
|
186 | def _validate_and_normalize_without_numpy(data, normalize): | |
@@ -262,13 +263,18 b' class IFrame(object):' | |||||
262 | src="{src}{params}" |
|
263 | src="{src}{params}" | |
263 | frameborder="0" |
|
264 | frameborder="0" | |
264 | allowfullscreen |
|
265 | allowfullscreen | |
|
266 | {extras} | |||
265 | ></iframe> |
|
267 | ></iframe> | |
266 | """ |
|
268 | """ | |
267 |
|
269 | |||
268 | def __init__(self, src, width, height, **kwargs): |
|
270 | def __init__(self, src, width, height, extras: Iterable[str] = None, **kwargs): | |
|
271 | if extras is None: | |||
|
272 | extras = [] | |||
|
273 | ||||
269 | self.src = src |
|
274 | self.src = src | |
270 | self.width = width |
|
275 | self.width = width | |
271 | self.height = height |
|
276 | self.height = height | |
|
277 | self.extras = extras | |||
272 | self.params = kwargs |
|
278 | self.params = kwargs | |
273 |
|
279 | |||
274 | def _repr_html_(self): |
|
280 | def _repr_html_(self): | |
@@ -278,10 +284,14 b' class IFrame(object):' | |||||
278 | params = "?" + urlencode(self.params) |
|
284 | params = "?" + urlencode(self.params) | |
279 | else: |
|
285 | else: | |
280 | params = "" |
|
286 | params = "" | |
281 |
return self.iframe.format( |
|
287 | return self.iframe.format( | |
|
288 | src=self.src, | |||
282 |
|
|
289 | width=self.width, | |
283 |
|
|
290 | height=self.height, | |
284 |
|
|
291 | params=params, | |
|
292 | extras=" ".join(self.extras), | |||
|
293 | ) | |||
|
294 | ||||
285 |
|
295 | |||
286 | class YouTubeVideo(IFrame): |
|
296 | class YouTubeVideo(IFrame): | |
287 | """Class for embedding a YouTube Video in an IPython session, based on its video id. |
|
297 | """Class for embedding a YouTube Video in an IPython session, based on its video id. | |
@@ -309,9 +319,12 b' class YouTubeVideo(IFrame):' | |||||
309 | will be inserted in the document. |
|
319 | will be inserted in the document. | |
310 | """ |
|
320 | """ | |
311 |
|
321 | |||
312 | def __init__(self, id, width=400, height=300, **kwargs): |
|
322 | def __init__(self, id, width=400, height=300, allow_autoplay=False, **kwargs): | |
313 | self.id=id |
|
323 | self.id=id | |
314 | src = "https://www.youtube.com/embed/{0}".format(id) |
|
324 | src = "https://www.youtube.com/embed/{0}".format(id) | |
|
325 | if allow_autoplay: | |||
|
326 | extras = list(kwargs.get("extras", [])) + ['allow="autoplay"'] | |||
|
327 | kwargs.update(autoplay=1, extras=extras) | |||
315 | super(YouTubeVideo, self).__init__(src, width, height, **kwargs) |
|
328 | super(YouTubeVideo, self).__init__(src, width, height, **kwargs) | |
316 |
|
329 | |||
317 | def _repr_jpeg_(self): |
|
330 | def _repr_jpeg_(self): |
@@ -6,7 +6,6 b' Contributions are *very* welcome.' | |||||
6 | """ |
|
6 | """ | |
7 |
|
7 | |||
8 | import os |
|
8 | import os | |
9 | import pipes |
|
|||
10 | import shlex |
|
9 | import shlex | |
11 | import subprocess |
|
10 | import subprocess | |
12 | import sys |
|
11 | import sys | |
@@ -47,9 +46,9 b' def install_editor(template, wait=False):' | |||||
47 | def call_editor(self, filename, line=0): |
|
46 | def call_editor(self, filename, line=0): | |
48 | if line is None: |
|
47 | if line is None: | |
49 | line = 0 |
|
48 | line = 0 | |
50 |
cmd = template.format(filename= |
|
49 | cmd = template.format(filename=shlex.quote(filename), line=line) | |
51 | print(">", cmd) |
|
50 | print(">", cmd) | |
52 |
# |
|
51 | # shlex.quote doesn't work right on Windows, but it does after splitting | |
53 | if sys.platform.startswith('win'): |
|
52 | if sys.platform.startswith('win'): | |
54 | cmd = shlex.split(cmd) |
|
53 | cmd = shlex.split(cmd) | |
55 | proc = subprocess.Popen(cmd, shell=True) |
|
54 | proc = subprocess.Popen(cmd, shell=True) |
@@ -111,7 +111,8 b" def latex_to_png(s, encode=False, backend=None, wrap=False, color='Black'," | |||||
111 |
|
111 | |||
112 | def latex_to_png_mpl(s, wrap, color='Black', scale=1.0): |
|
112 | def latex_to_png_mpl(s, wrap, color='Black', scale=1.0): | |
113 | try: |
|
113 | try: | |
114 | from matplotlib import mathtext |
|
114 | from matplotlib import figure, font_manager, mathtext | |
|
115 | from matplotlib.backends import backend_agg | |||
115 | from pyparsing import ParseFatalException |
|
116 | from pyparsing import ParseFatalException | |
116 | except ImportError: |
|
117 | except ImportError: | |
117 | return None |
|
118 | return None | |
@@ -122,11 +123,18 b" def latex_to_png_mpl(s, wrap, color='Black', scale=1.0):" | |||||
122 | s = u'${0}$'.format(s) |
|
123 | s = u'${0}$'.format(s) | |
123 |
|
124 | |||
124 | try: |
|
125 | try: | |
125 | mt = mathtext.MathTextParser('bitmap') |
|
126 | prop = font_manager.FontProperties(size=12) | |
126 | f = BytesIO() |
|
|||
127 | dpi = 120*scale |
|
127 | dpi = 120 * scale | |
128 | mt.to_png(f, s, fontsize=12, dpi=dpi, color=color) |
|
128 | buffer = BytesIO() | |
129 | return f.getvalue() |
|
129 | ||
|
130 | # Adapted from mathtext.math_to_image | |||
|
131 | parser = mathtext.MathTextParser("path") | |||
|
132 | width, height, depth, _, _ = parser.parse(s, dpi=72, prop=prop) | |||
|
133 | fig = figure.Figure(figsize=(width / 72, height / 72)) | |||
|
134 | fig.text(0, depth / height, s, fontproperties=prop, color=color) | |||
|
135 | backend_agg.FigureCanvasAgg(fig) | |||
|
136 | fig.savefig(buffer, dpi=dpi, format="png", transparent=True) | |||
|
137 | return buffer.getvalue() | |||
130 | except (ValueError, RuntimeError, ParseFatalException): |
|
138 | except (ValueError, RuntimeError, ParseFatalException): | |
131 | return None |
|
139 | return None | |
132 |
|
140 |
@@ -626,7 +626,7 b' def _default_pprint(obj, p, cycle):' | |||||
626 | def _seq_pprinter_factory(start, end): |
|
626 | def _seq_pprinter_factory(start, end): | |
627 | """ |
|
627 | """ | |
628 | Factory that returns a pprint function useful for sequences. Used by |
|
628 | Factory that returns a pprint function useful for sequences. Used by | |
629 |
the default pprint for tuples |
|
629 | the default pprint for tuples and lists. | |
630 | """ |
|
630 | """ | |
631 | def inner(obj, p, cycle): |
|
631 | def inner(obj, p, cycle): | |
632 | if cycle: |
|
632 | if cycle: |
@@ -15,9 +15,6 b'' | |||||
15 | # Stdlib imports |
|
15 | # Stdlib imports | |
16 | import time |
|
16 | import time | |
17 |
|
17 | |||
18 | # Third-party imports |
|
|||
19 | import nose.tools as nt |
|
|||
20 |
|
||||
21 | # Our own imports |
|
18 | # Our own imports | |
22 | from IPython.lib import backgroundjobs as bg |
|
19 | from IPython.lib import backgroundjobs as bg | |
23 |
|
20 | |||
@@ -49,7 +46,7 b' def test_result():' | |||||
49 | jobs = bg.BackgroundJobManager() |
|
46 | jobs = bg.BackgroundJobManager() | |
50 | j = jobs.new(sleeper) |
|
47 | j = jobs.new(sleeper) | |
51 | j.join() |
|
48 | j.join() | |
52 |
|
|
49 | assert j.result["interval"] == t_short | |
53 |
|
50 | |||
54 |
|
51 | |||
55 | def test_flush(): |
|
52 | def test_flush(): | |
@@ -57,10 +54,10 b' def test_flush():' | |||||
57 | jobs = bg.BackgroundJobManager() |
|
54 | jobs = bg.BackgroundJobManager() | |
58 | j = jobs.new(sleeper) |
|
55 | j = jobs.new(sleeper) | |
59 | j.join() |
|
56 | j.join() | |
60 |
|
|
57 | assert len(jobs.completed) == 1 | |
61 |
|
|
58 | assert len(jobs.dead) == 0 | |
62 | jobs.flush() |
|
59 | jobs.flush() | |
63 |
|
|
60 | assert len(jobs.completed) == 0 | |
64 |
|
61 | |||
65 |
|
62 | |||
66 | def test_dead(): |
|
63 | def test_dead(): | |
@@ -68,10 +65,10 b' def test_dead():' | |||||
68 | jobs = bg.BackgroundJobManager() |
|
65 | jobs = bg.BackgroundJobManager() | |
69 | j = jobs.new(crasher) |
|
66 | j = jobs.new(crasher) | |
70 | j.join() |
|
67 | j.join() | |
71 |
|
|
68 | assert len(jobs.completed) == 0 | |
72 |
|
|
69 | assert len(jobs.dead) == 1 | |
73 | jobs.flush() |
|
70 | jobs.flush() | |
74 |
|
|
71 | assert len(jobs.dead) == 0 | |
75 |
|
72 | |||
76 |
|
73 | |||
77 | def test_longer(): |
|
74 | def test_longer(): | |
@@ -81,8 +78,8 b' def test_longer():' | |||||
81 | # job as running, but not so long that it makes the test suite noticeably |
|
78 | # job as running, but not so long that it makes the test suite noticeably | |
82 | # slower. |
|
79 | # slower. | |
83 | j = jobs.new(sleeper, 0.1) |
|
80 | j = jobs.new(sleeper, 0.1) | |
84 |
|
|
81 | assert len(jobs.running) == 1 | |
85 |
|
|
82 | assert len(jobs.completed) == 0 | |
86 | j.join() |
|
83 | j.join() | |
87 |
|
|
84 | assert len(jobs.running) == 0 | |
88 |
|
|
85 | assert len(jobs.completed) == 1 |
@@ -60,7 +60,8 b' def test_existing_path_FileLink():' | |||||
60 | fl = display.FileLink(tf.name) |
|
60 | fl = display.FileLink(tf.name) | |
61 | actual = fl._repr_html_() |
|
61 | actual = fl._repr_html_() | |
62 | expected = "<a href='%s' target='_blank'>%s</a><br>" % (tf.name,tf.name) |
|
62 | expected = "<a href='%s' target='_blank'>%s</a><br>" % (tf.name, tf.name) | |
63 |
|
|
63 | assert actual == expected | |
|
64 | ||||
64 |
|
65 | |||
65 | def test_existing_path_FileLink_repr(): |
|
66 | def test_existing_path_FileLink_repr(): | |
66 | """FileLink: Calling repr() functions as expected on existing filepath |
|
67 | """FileLink: Calling repr() functions as expected on existing filepath | |
@@ -69,7 +70,8 b' def test_existing_path_FileLink_repr():' | |||||
69 | fl = display.FileLink(tf.name) |
|
70 | fl = display.FileLink(tf.name) | |
70 | actual = repr(fl) |
|
71 | actual = repr(fl) | |
71 | expected = tf.name |
|
72 | expected = tf.name | |
72 |
|
|
73 | assert actual == expected | |
|
74 | ||||
73 |
|
75 | |||
74 | def test_error_on_directory_to_FileLink(): |
|
76 | def test_error_on_directory_to_FileLink(): | |
75 | """FileLink: Raises error when passed directory |
|
77 | """FileLink: Raises error when passed directory | |
@@ -111,7 +113,8 b' def test_existing_path_FileLinks():' | |||||
111 | (tf1.name.replace("\\","/"),split(tf1.name)[1])] |
|
113 | (tf1.name.replace("\\","/"),split(tf1.name)[1])] | |
112 | expected.sort() |
|
114 | expected.sort() | |
113 | # We compare the sorted list of links here as that's more reliable |
|
115 | # We compare the sorted list of links here as that's more reliable | |
114 |
|
|
116 | assert actual == expected | |
|
117 | ||||
115 |
|
118 | |||
116 | def test_existing_path_FileLinks_alt_formatter(): |
|
119 | def test_existing_path_FileLinks_alt_formatter(): | |
117 | """FileLinks: Calling _repr_html_ functions as expected w/ an alt formatter |
|
120 | """FileLinks: Calling _repr_html_ functions as expected w/ an alt formatter | |
@@ -128,7 +131,8 b' def test_existing_path_FileLinks_alt_formatter():' | |||||
128 | expected = ["hello","world"] |
|
131 | expected = ["hello","world"] | |
129 | expected.sort() |
|
132 | expected.sort() | |
130 | # We compare the sorted list of links here as that's more reliable |
|
133 | # We compare the sorted list of links here as that's more reliable | |
131 |
|
|
134 | assert actual == expected | |
|
135 | ||||
132 |
|
136 | |||
133 | def test_existing_path_FileLinks_repr(): |
|
137 | def test_existing_path_FileLinks_repr(): | |
134 | """FileLinks: Calling repr() functions as expected on existing directory """ |
|
138 | """FileLinks: Calling repr() functions as expected on existing directory """ | |
@@ -142,7 +146,8 b' def test_existing_path_FileLinks_repr():' | |||||
142 | expected = ['%s/' % td, ' %s' % split(tf1.name)[1],' %s' % split(tf2.name)[1]] |
|
146 | expected = ['%s/' % td, ' %s' % split(tf1.name)[1],' %s' % split(tf2.name)[1]] | |
143 | expected.sort() |
|
147 | expected.sort() | |
144 | # We compare the sorted list of links here as that's more reliable |
|
148 | # We compare the sorted list of links here as that's more reliable | |
145 |
|
|
149 | assert actual == expected | |
|
150 | ||||
146 |
|
151 | |||
147 | def test_existing_path_FileLinks_repr_alt_formatter(): |
|
152 | def test_existing_path_FileLinks_repr_alt_formatter(): | |
148 | """FileLinks: Calling repr() functions as expected w/ alt formatter |
|
153 | """FileLinks: Calling repr() functions as expected w/ alt formatter | |
@@ -159,7 +164,8 b' def test_existing_path_FileLinks_repr_alt_formatter():' | |||||
159 | expected = ["hello","world"] |
|
164 | expected = ["hello","world"] | |
160 | expected.sort() |
|
165 | expected.sort() | |
161 | # We compare the sorted list of links here as that's more reliable |
|
166 | # We compare the sorted list of links here as that's more reliable | |
162 |
|
|
167 | assert actual == expected | |
|
168 | ||||
163 |
|
169 | |||
164 | def test_error_on_file_to_FileLinks(): |
|
170 | def test_error_on_file_to_FileLinks(): | |
165 | """FileLinks: Raises error when passed file |
|
171 | """FileLinks: Raises error when passed file | |
@@ -178,11 +184,11 b' def test_recursive_FileLinks():' | |||||
178 | fl = display.FileLinks(td) |
|
184 | fl = display.FileLinks(td) | |
179 | actual = str(fl) |
|
185 | actual = str(fl) | |
180 | actual = actual.split('\n') |
|
186 | actual = actual.split('\n') | |
181 |
|
|
187 | assert len(actual) == 4, actual | |
182 | fl = display.FileLinks(td, recursive=False) |
|
188 | fl = display.FileLinks(td, recursive=False) | |
183 | actual = str(fl) |
|
189 | actual = str(fl) | |
184 | actual = actual.split('\n') |
|
190 | actual = actual.split('\n') | |
185 |
|
|
191 | assert len(actual) == 2, actual | |
186 |
|
192 | |||
187 | def test_audio_from_file(): |
|
193 | def test_audio_from_file(): | |
188 | path = pjoin(dirname(__file__), 'test.wav') |
|
194 | path = pjoin(dirname(__file__), 'test.wav') | |
@@ -194,13 +200,13 b' class TestAudioDataWithNumpy(TestCase):' | |||||
194 | def test_audio_from_numpy_array(self): |
|
200 | def test_audio_from_numpy_array(self): | |
195 | test_tone = get_test_tone() |
|
201 | test_tone = get_test_tone() | |
196 | audio = display.Audio(test_tone, rate=44100) |
|
202 | audio = display.Audio(test_tone, rate=44100) | |
197 |
|
|
203 | assert len(read_wav(audio.data)) == len(test_tone) | |
198 |
|
204 | |||
199 | @skipif_not_numpy |
|
205 | @skipif_not_numpy | |
200 | def test_audio_from_list(self): |
|
206 | def test_audio_from_list(self): | |
201 | test_tone = get_test_tone() |
|
207 | test_tone = get_test_tone() | |
202 | audio = display.Audio(list(test_tone), rate=44100) |
|
208 | audio = display.Audio(list(test_tone), rate=44100) | |
203 |
|
|
209 | assert len(read_wav(audio.data)) == len(test_tone) | |
204 |
|
210 | |||
205 | @skipif_not_numpy |
|
211 | @skipif_not_numpy | |
206 | def test_audio_from_numpy_array_without_rate_raises(self): |
|
212 | def test_audio_from_numpy_array_without_rate_raises(self): | |
@@ -212,7 +218,7 b' class TestAudioDataWithNumpy(TestCase):' | |||||
212 | for scale in [1, 0.5, 2]: |
|
218 | for scale in [1, 0.5, 2]: | |
213 | audio = display.Audio(get_test_tone(scale), rate=44100) |
|
219 | audio = display.Audio(get_test_tone(scale), rate=44100) | |
214 | actual_max_value = numpy.max(numpy.abs(read_wav(audio.data))) |
|
220 | actual_max_value = numpy.max(numpy.abs(read_wav(audio.data))) | |
215 |
|
|
221 | assert actual_max_value == expected_max_value | |
216 |
|
222 | |||
217 | @skipif_not_numpy |
|
223 | @skipif_not_numpy | |
218 | def test_audio_data_without_normalization(self): |
|
224 | def test_audio_data_without_normalization(self): | |
@@ -223,7 +229,7 b' class TestAudioDataWithNumpy(TestCase):' | |||||
223 | expected_max_value = int(max_int16 * test_tone_max_abs) |
|
229 | expected_max_value = int(max_int16 * test_tone_max_abs) | |
224 | audio = display.Audio(test_tone, rate=44100, normalize=False) |
|
230 | audio = display.Audio(test_tone, rate=44100, normalize=False) | |
225 | actual_max_value = numpy.max(numpy.abs(read_wav(audio.data))) |
|
231 | actual_max_value = numpy.max(numpy.abs(read_wav(audio.data))) | |
226 |
|
|
232 | assert actual_max_value == expected_max_value | |
227 |
|
233 | |||
228 | def test_audio_data_without_normalization_raises_for_invalid_data(self): |
|
234 | def test_audio_data_without_normalization_raises_for_invalid_data(self): | |
229 | nt.assert_raises( |
|
235 | nt.assert_raises( |
@@ -2,8 +2,6 b'' | |||||
2 | import sys |
|
2 | import sys | |
3 | from unittest import mock |
|
3 | from unittest import mock | |
4 |
|
4 | |||
5 | import nose.tools as nt |
|
|||
6 |
|
||||
7 | from IPython import get_ipython |
|
5 | from IPython import get_ipython | |
8 | from IPython.lib import editorhooks |
|
6 | from IPython.lib import editorhooks | |
9 |
|
7 | |||
@@ -20,15 +18,15 b' def test_install_editor():' | |||||
20 | with mock.patch('subprocess.Popen', fake_popen): |
|
18 | with mock.patch('subprocess.Popen', fake_popen): | |
21 | get_ipython().hooks.editor('the file', 64) |
|
19 | get_ipython().hooks.editor('the file', 64) | |
22 |
|
20 | |||
23 |
|
|
21 | assert len(called) == 1 | |
24 |
args = called[0][ |
|
22 | args = called[0]["args"] | |
25 |
kwargs = called[0][ |
|
23 | kwargs = called[0]["kwargs"] | |
26 |
|
24 | |||
27 |
|
|
25 | assert kwargs == {"shell": True} | |
28 |
|
26 | |||
29 |
if sys.platform.startswith( |
|
27 | if sys.platform.startswith("win"): | |
30 |
expected = [ |
|
28 | expected = ["foo", "-l", "64", "-f", "the file"] | |
31 | else: |
|
29 | else: | |
32 | expected = "foo -l 64 -f 'the file'" |
|
30 | expected = "foo -l 64 -f 'the file'" | |
33 | cmd = args[0] |
|
31 | cmd = args[0] | |
34 |
|
|
32 | assert cmd == expected |
@@ -25,7 +25,7 b' def test_check_latex_to_png_dvipng_fails_when_no_cmd(command):' | |||||
25 | raise FindCmdError |
|
25 | raise FindCmdError | |
26 |
|
26 | |||
27 | with patch.object(latextools, "find_cmd", mock_find_cmd): |
|
27 | with patch.object(latextools, "find_cmd", mock_find_cmd): | |
28 |
assert latextools.latex_to_png_dvipng("whatever", True) |
|
28 | assert latextools.latex_to_png_dvipng("whatever", True) is None | |
29 |
|
29 | |||
30 |
|
30 | |||
31 | @contextmanager |
|
31 | @contextmanager |
@@ -81,7 +81,7 b' def test_indentation():' | |||||
81 | gotoutput = pretty.pretty(MyList(range(count))) |
|
81 | gotoutput = pretty.pretty(MyList(range(count))) | |
82 | expectedoutput = "MyList(\n" + ",\n".join(" %d" % i for i in range(count)) + ")" |
|
82 | expectedoutput = "MyList(\n" + ",\n".join(" %d" % i for i in range(count)) + ")" | |
83 |
|
83 | |||
84 |
|
|
84 | assert gotoutput == expectedoutput | |
85 |
|
85 | |||
86 |
|
86 | |||
87 | def test_dispatch(): |
|
87 | def test_dispatch(): | |
@@ -92,7 +92,7 b' def test_dispatch():' | |||||
92 | gotoutput = pretty.pretty(MyDict()) |
|
92 | gotoutput = pretty.pretty(MyDict()) | |
93 | expectedoutput = "MyDict(...)" |
|
93 | expectedoutput = "MyDict(...)" | |
94 |
|
94 | |||
95 |
|
|
95 | assert gotoutput == expectedoutput | |
96 |
|
96 | |||
97 |
|
97 | |||
98 | def test_callability_checking(): |
|
98 | def test_callability_checking(): | |
@@ -103,7 +103,7 b' def test_callability_checking():' | |||||
103 | gotoutput = pretty.pretty(Dummy2()) |
|
103 | gotoutput = pretty.pretty(Dummy2()) | |
104 | expectedoutput = "Dummy1(...)" |
|
104 | expectedoutput = "Dummy1(...)" | |
105 |
|
105 | |||
106 |
|
|
106 | assert gotoutput == expectedoutput | |
107 |
|
107 | |||
108 |
|
108 | |||
109 | @pytest.mark.parametrize( |
|
109 | @pytest.mark.parametrize( | |
@@ -135,7 +135,7 b' def test_sets(obj, expected_output):' | |||||
135 | Test that set and frozenset use Python 3 formatting. |
|
135 | Test that set and frozenset use Python 3 formatting. | |
136 | """ |
|
136 | """ | |
137 | got_output = pretty.pretty(obj) |
|
137 | got_output = pretty.pretty(obj) | |
138 |
|
|
138 | assert got_output == expected_output | |
139 |
|
139 | |||
140 |
|
140 | |||
141 | @skip_without('xxlimited') |
|
141 | @skip_without('xxlimited') | |
@@ -145,14 +145,16 b' def test_pprint_heap_allocated_type():' | |||||
145 | """ |
|
145 | """ | |
146 | import xxlimited |
|
146 | import xxlimited | |
147 | output = pretty.pretty(xxlimited.Null) |
|
147 | output = pretty.pretty(xxlimited.Null) | |
148 |
|
|
148 | assert output == "xxlimited.Null" | |
|
149 | ||||
149 |
|
150 | |||
150 | def test_pprint_nomod(): |
|
151 | def test_pprint_nomod(): | |
151 | """ |
|
152 | """ | |
152 | Test that pprint works for classes with no __module__. |
|
153 | Test that pprint works for classes with no __module__. | |
153 | """ |
|
154 | """ | |
154 | output = pretty.pretty(NoModule) |
|
155 | output = pretty.pretty(NoModule) | |
155 |
|
|
156 | assert output == "NoModule" | |
|
157 | ||||
156 |
|
158 | |||
157 | def test_pprint_break(): |
|
159 | def test_pprint_break(): | |
158 | """ |
|
160 | """ | |
@@ -160,7 +162,7 b' def test_pprint_break():' | |||||
160 | """ |
|
162 | """ | |
161 | output = pretty.pretty(Breaking()) |
|
163 | output = pretty.pretty(Breaking()) | |
162 | expected = "TG: Breaking(\n ):" |
|
164 | expected = "TG: Breaking(\n ):" | |
163 |
|
|
165 | assert output == expected | |
164 |
|
166 | |||
165 | def test_pprint_break_repr(): |
|
167 | def test_pprint_break_repr(): | |
166 | """ |
|
168 | """ | |
@@ -168,11 +170,11 b' def test_pprint_break_repr():' | |||||
168 | """ |
|
170 | """ | |
169 | output = pretty.pretty([[BreakingRepr()]]) |
|
171 | output = pretty.pretty([[BreakingRepr()]]) | |
170 | expected = "[[Breaking(\n )]]" |
|
172 | expected = "[[Breaking(\n )]]" | |
171 |
|
|
173 | assert output == expected | |
172 |
|
174 | |||
173 | output = pretty.pretty([[BreakingRepr()]*2]) |
|
175 | output = pretty.pretty([[BreakingRepr()]*2]) | |
174 | expected = "[[Breaking(\n ),\n Breaking(\n )]]" |
|
176 | expected = "[[Breaking(\n ),\n Breaking(\n )]]" | |
175 |
|
|
177 | assert output == expected | |
176 |
|
178 | |||
177 | def test_bad_repr(): |
|
179 | def test_bad_repr(): | |
178 | """Don't catch bad repr errors""" |
|
180 | """Don't catch bad repr errors""" | |
@@ -258,7 +260,7 b" ClassWithMeta = MetaClass('ClassWithMeta')" | |||||
258 |
|
260 | |||
259 | def test_metaclass_repr(): |
|
261 | def test_metaclass_repr(): | |
260 | output = pretty.pretty(ClassWithMeta) |
|
262 | output = pretty.pretty(ClassWithMeta) | |
261 |
|
|
263 | assert output == "[CUSTOM REPR FOR CLASS ClassWithMeta]" | |
262 |
|
264 | |||
263 |
|
265 | |||
264 | def test_unicode_repr(): |
|
266 | def test_unicode_repr(): | |
@@ -271,9 +273,9 b' def test_unicode_repr():' | |||||
271 |
|
273 | |||
272 | c = C() |
|
274 | c = C() | |
273 | p = pretty.pretty(c) |
|
275 | p = pretty.pretty(c) | |
274 |
|
|
276 | assert p == u | |
275 | p = pretty.pretty([c]) |
|
277 | p = pretty.pretty([c]) | |
276 |
|
|
278 | assert p == u"[%s]" % u | |
277 |
|
279 | |||
278 |
|
280 | |||
279 | def test_basic_class(): |
|
281 | def test_basic_class(): | |
@@ -290,10 +292,11 b' def test_basic_class():' | |||||
290 | printer.flush() |
|
292 | printer.flush() | |
291 | output = stream.getvalue() |
|
293 | output = stream.getvalue() | |
292 |
|
294 | |||
293 |
|
|
295 | assert output == "%s.MyObj" % __name__ | |
294 | nt.assert_true(type_pprint_wrapper.called) |
|
296 | nt.assert_true(type_pprint_wrapper.called) | |
295 |
|
297 | |||
296 |
|
298 | |||
|
299 | # TODO : pytest.mark.parametrise once nose is gone. | |||
297 | def test_collections_defaultdict(): |
|
300 | def test_collections_defaultdict(): | |
298 | # Create defaultdicts with cycles |
|
301 | # Create defaultdicts with cycles | |
299 | a = defaultdict() |
|
302 | a = defaultdict() | |
@@ -311,9 +314,10 b' def test_collections_defaultdict():' | |||||
311 | (b, "defaultdict(list, {'key': defaultdict(...)})"), |
|
314 | (b, "defaultdict(list, {'key': defaultdict(...)})"), | |
312 | ] |
|
315 | ] | |
313 | for obj, expected in cases: |
|
316 | for obj, expected in cases: | |
314 |
|
|
317 | assert pretty.pretty(obj) == expected | |
315 |
|
318 | |||
316 |
|
319 | |||
|
320 | # TODO : pytest.mark.parametrise once nose is gone. | |||
317 | def test_collections_ordereddict(): |
|
321 | def test_collections_ordereddict(): | |
318 | # Create OrderedDict with cycle |
|
322 | # Create OrderedDict with cycle | |
319 | a = OrderedDict() |
|
323 | a = OrderedDict() | |
@@ -335,9 +339,10 b' def test_collections_ordereddict():' | |||||
335 | (a, "OrderedDict([('key', OrderedDict(...))])"), |
|
339 | (a, "OrderedDict([('key', OrderedDict(...))])"), | |
336 | ] |
|
340 | ] | |
337 | for obj, expected in cases: |
|
341 | for obj, expected in cases: | |
338 |
|
|
342 | assert pretty.pretty(obj) == expected | |
339 |
|
343 | |||
340 |
|
344 | |||
|
345 | # TODO : pytest.mark.parametrise once nose is gone. | |||
341 | def test_collections_deque(): |
|
346 | def test_collections_deque(): | |
342 | # Create deque with cycle |
|
347 | # Create deque with cycle | |
343 | a = deque() |
|
348 | a = deque() | |
@@ -369,8 +374,10 b' def test_collections_deque():' | |||||
369 | (a, 'deque([deque(...)])'), |
|
374 | (a, 'deque([deque(...)])'), | |
370 | ] |
|
375 | ] | |
371 | for obj, expected in cases: |
|
376 | for obj, expected in cases: | |
372 |
|
|
377 | assert pretty.pretty(obj) == expected | |
373 |
|
378 | |||
|
379 | ||||
|
380 | # TODO : pytest.mark.parametrise once nose is gone. | |||
374 | def test_collections_counter(): |
|
381 | def test_collections_counter(): | |
375 | class MyCounter(Counter): |
|
382 | class MyCounter(Counter): | |
376 | pass |
|
383 | pass | |
@@ -380,8 +387,9 b' def test_collections_counter():' | |||||
380 | (MyCounter(a=1), "MyCounter({'a': 1})"), |
|
387 | (MyCounter(a=1), "MyCounter({'a': 1})"), | |
381 | ] |
|
388 | ] | |
382 | for obj, expected in cases: |
|
389 | for obj, expected in cases: | |
383 |
|
|
390 | assert pretty.pretty(obj) == expected | |
384 |
|
391 | |||
|
392 | # TODO : pytest.mark.parametrise once nose is gone. | |||
385 | def test_mappingproxy(): |
|
393 | def test_mappingproxy(): | |
386 | MP = types.MappingProxyType |
|
394 | MP = types.MappingProxyType | |
387 | underlying_dict = {} |
|
395 | underlying_dict = {} | |
@@ -424,9 +432,10 b' def test_mappingproxy():' | |||||
424 | "{2: mappingproxy({2: {...}, 3: {...}}), 3: {...}}"), |
|
432 | "{2: mappingproxy({2: {...}, 3: {...}}), 3: {...}}"), | |
425 | ] |
|
433 | ] | |
426 | for obj, expected in cases: |
|
434 | for obj, expected in cases: | |
427 |
|
|
435 | assert pretty.pretty(obj) == expected | |
428 |
|
436 | |||
429 |
|
437 | |||
|
438 | # TODO : pytest.mark.parametrise once nose is gone. | |||
430 | def test_simplenamespace(): |
|
439 | def test_simplenamespace(): | |
431 | SN = types.SimpleNamespace |
|
440 | SN = types.SimpleNamespace | |
432 |
|
441 | |||
@@ -444,7 +453,7 b' def test_simplenamespace():' | |||||
444 | (sn_recursive, "namespace(first=namespace(...), second=namespace(...))"), |
|
453 | (sn_recursive, "namespace(first=namespace(...), second=namespace(...))"), | |
445 | ] |
|
454 | ] | |
446 | for obj, expected in cases: |
|
455 | for obj, expected in cases: | |
447 |
|
|
456 | assert pretty.pretty(obj) == expected | |
448 |
|
457 | |||
449 |
|
458 | |||
450 | def test_pretty_environ(): |
|
459 | def test_pretty_environ(): | |
@@ -452,7 +461,7 b' def test_pretty_environ():' | |||||
452 | # reindent to align with 'environ' prefix |
|
461 | # reindent to align with 'environ' prefix | |
453 | dict_indented = dict_repr.replace('\n', '\n' + (' ' * len('environ'))) |
|
462 | dict_indented = dict_repr.replace('\n', '\n' + (' ' * len('environ'))) | |
454 | env_repr = pretty.pretty(os.environ) |
|
463 | env_repr = pretty.pretty(os.environ) | |
455 |
|
|
464 | assert env_repr == "environ" + dict_indented | |
456 |
|
465 | |||
457 |
|
466 | |||
458 | def test_function_pretty(): |
|
467 | def test_function_pretty(): | |
@@ -460,7 +469,8 b' def test_function_pretty():' | |||||
460 | # posixpath is a pure python module, its interface is consistent |
|
469 | # posixpath is a pure python module, its interface is consistent | |
461 | # across Python distributions |
|
470 | # across Python distributions | |
462 | import posixpath |
|
471 | import posixpath | |
463 | nt.assert_equal(pretty.pretty(posixpath.join), '<function posixpath.join(a, *p)>') |
|
472 | ||
|
473 | assert pretty.pretty(posixpath.join) == "<function posixpath.join(a, *p)>" | |||
464 |
|
474 | |||
465 | # custom function |
|
475 | # custom function | |
466 | def meaning_of_life(question=None): |
|
476 | def meaning_of_life(question=None): | |
@@ -489,4 +499,4 b' def test_custom_repr():' | |||||
489 | oc = OrderedCounter("abracadabra") |
|
499 | oc = OrderedCounter("abracadabra") | |
490 | nt.assert_in("OrderedCounter(OrderedDict", pretty.pretty(oc)) |
|
500 | nt.assert_in("OrderedCounter(OrderedDict", pretty.pretty(oc)) | |
491 |
|
501 | |||
492 |
|
|
502 | assert pretty.pretty(MySet()) == "mine" |
@@ -1,24 +1,25 b'' | |||||
1 | # coding: utf-8 |
|
1 | # coding: utf-8 | |
2 | from IPython.lib import passwd |
|
2 | from IPython.lib import passwd | |
3 | from IPython.lib.security import passwd_check, salt_len |
|
3 | from IPython.lib.security import passwd_check, salt_len | |
4 | import nose.tools as nt |
|
|||
5 |
|
4 | |||
6 | def test_passwd_structure(): |
|
5 | def test_passwd_structure(): | |
7 |
p = passwd( |
|
6 | p = passwd("passphrase") | |
8 |
algorithm, salt, hashed = p.split( |
|
7 | algorithm, salt, hashed = p.split(":") | |
9 |
|
|
8 | assert algorithm == "sha1" | |
10 |
|
|
9 | assert len(salt) == salt_len | |
11 |
|
|
10 | assert len(hashed) == 40 | |
12 |
|
11 | |||
13 | def test_roundtrip(): |
|
12 | def test_roundtrip(): | |
14 |
p = passwd( |
|
13 | p = passwd("passphrase") | |
15 |
|
|
14 | assert passwd_check(p, "passphrase") is True | |
|
15 | ||||
16 |
|
16 | |||
17 | def test_bad(): |
|
17 | def test_bad(): | |
18 | p = passwd('passphrase') |
|
18 | p = passwd('passphrase') | |
19 |
|
|
19 | assert passwd_check(p, p) is False | |
20 |
|
|
20 | assert passwd_check(p, "a:b:c:d") is False | |
21 |
|
|
21 | assert passwd_check(p, "a:b") is False | |
|
22 | ||||
22 |
|
23 | |||
23 | def test_passwd_check_unicode(): |
|
24 | def test_passwd_check_unicode(): | |
24 | # GH issue #4524 |
|
25 | # GH issue #4524 |
@@ -200,6 +200,7 b' from io import StringIO' | |||||
200 | # Third-party |
|
200 | # Third-party | |
201 | from docutils.parsers.rst import directives |
|
201 | from docutils.parsers.rst import directives | |
202 | from docutils.parsers.rst import Directive |
|
202 | from docutils.parsers.rst import Directive | |
|
203 | from sphinx.util import logging | |||
203 |
|
204 | |||
204 | # Our own |
|
205 | # Our own | |
205 | from traitlets.config import Config |
|
206 | from traitlets.config import Config | |
@@ -557,15 +558,20 b' class EmbeddedSphinxShell(object):' | |||||
557 | filename = self.directive.state.document.current_source |
|
558 | filename = self.directive.state.document.current_source | |
558 | lineno = self.directive.state.document.current_line |
|
559 | lineno = self.directive.state.document.current_line | |
559 |
|
560 | |||
|
561 | # Use sphinx logger for warnings | |||
|
562 | logger = logging.getLogger(__name__) | |||
|
563 | ||||
560 | # output any exceptions raised during execution to stdout |
|
564 | # output any exceptions raised during execution to stdout | |
561 | # unless :okexcept: has been specified. |
|
565 | # unless :okexcept: has been specified. | |
562 | if not is_okexcept and (("Traceback" in processed_output) or ("SyntaxError" in processed_output)): |
|
566 | if not is_okexcept and ( | |
563 | s = "\nException in %s at block ending on line %s\n" % (filename, lineno) |
|
567 | ("Traceback" in processed_output) or ("SyntaxError" in processed_output) | |
|
568 | ): | |||
|
569 | s = "\n>>>" + ("-" * 73) + "\n" | |||
|
570 | s += "Exception in %s at block ending on line %s\n" % (filename, lineno) | |||
564 | s += "Specify :okexcept: as an option in the ipython:: block to suppress this message\n" |
|
571 | s += "Specify :okexcept: as an option in the ipython:: block to suppress this message\n" | |
565 | sys.stdout.write('\n\n>>>' + ('-' * 73)) |
|
572 | s += processed_output + "\n" | |
566 | sys.stdout.write(s) |
|
573 | s += "<<<" + ("-" * 73) | |
567 | sys.stdout.write(processed_output) |
|
574 | logger.warning(s) | |
568 | sys.stdout.write('<<<' + ('-' * 73) + '\n\n') |
|
|||
569 | if self.warning_is_error: |
|
575 | if self.warning_is_error: | |
570 | raise RuntimeError('Non Expected exception in `{}` line {}'.format(filename, lineno)) |
|
576 | raise RuntimeError('Non Expected exception in `{}` line {}'.format(filename, lineno)) | |
571 |
|
577 | |||
@@ -573,15 +579,15 b' class EmbeddedSphinxShell(object):' | |||||
573 | # unless :okwarning: has been specified. |
|
579 | # unless :okwarning: has been specified. | |
574 | if not is_okwarning: |
|
580 | if not is_okwarning: | |
575 | for w in ws: |
|
581 | for w in ws: | |
576 | s = "\nWarning in %s at block ending on line %s\n" % (filename, lineno) |
|
582 | s = "\n>>>" + ("-" * 73) + "\n" | |
|
583 | s += "Warning in %s at block ending on line %s\n" % (filename, lineno) | |||
577 | s += "Specify :okwarning: as an option in the ipython:: block to suppress this message\n" |
|
584 | s += "Specify :okwarning: as an option in the ipython:: block to suppress this message\n" | |
578 | sys.stdout.write('\n\n>>>' + ('-' * 73)) |
|
585 | s += ("-" * 76) + "\n" | |
579 |
s |
|
586 | s += warnings.formatwarning( | |
580 | sys.stdout.write(('-' * 76) + '\n') |
|
587 | w.message, w.category, w.filename, w.lineno, w.line | |
581 | s=warnings.formatwarning(w.message, w.category, |
|
588 | ) | |
582 | w.filename, w.lineno, w.line) |
|
589 | s += "<<<" + ("-" * 73) | |
583 |
|
|
590 | logger.warning(s) | |
584 | sys.stdout.write('<<<' + ('-' * 73) + '\n') |
|
|||
585 | if self.warning_is_error: |
|
591 | if self.warning_is_error: | |
586 | raise RuntimeError('Non Expected warning in `{}` line {}'.format(filename, lineno)) |
|
592 | raise RuntimeError('Non Expected warning in `{}` line {}'.format(filename, lineno)) | |
587 |
|
593 | |||
@@ -1002,6 +1008,9 b' class IPythonDirective(Directive):' | |||||
1002 | lines = ['.. code-block:: ipython', ''] |
|
1008 | lines = ['.. code-block:: ipython', ''] | |
1003 | figures = [] |
|
1009 | figures = [] | |
1004 |
|
1010 | |||
|
1011 | # Use sphinx logger for warnings | |||
|
1012 | logger = logging.getLogger(__name__) | |||
|
1013 | ||||
1005 | for part in parts: |
|
1014 | for part in parts: | |
1006 | block = block_parser(part, rgxin, rgxout, promptin, promptout) |
|
1015 | block = block_parser(part, rgxin, rgxout, promptin, promptout) | |
1007 | if len(block): |
|
1016 | if len(block): | |
@@ -1020,7 +1029,7 b' class IPythonDirective(Directive):' | |||||
1020 | if self.shell.warning_is_error: |
|
1029 | if self.shell.warning_is_error: | |
1021 | raise RuntimeError(message) |
|
1030 | raise RuntimeError(message) | |
1022 | else: |
|
1031 | else: | |
1023 |
|
|
1032 | logger.warning(message) | |
1024 |
|
1033 | |||
1025 | for figure in figures: |
|
1034 | for figure in figures: | |
1026 | lines.append('') |
|
1035 | lines.append('') |
@@ -7,6 +7,7 b' from IPython.core.debugger import Pdb' | |||||
7 | from IPython.core.completer import IPCompleter |
|
7 | from IPython.core.completer import IPCompleter | |
8 | from .ptutils import IPythonPTCompleter |
|
8 | from .ptutils import IPythonPTCompleter | |
9 | from .shortcuts import create_ipython_shortcuts |
|
9 | from .shortcuts import create_ipython_shortcuts | |
|
10 | from . import embed | |||
10 |
|
11 | |||
11 | from pygments.token import Token |
|
12 | from pygments.token import Token | |
12 | from prompt_toolkit.shortcuts.prompt import PromptSession |
|
13 | from prompt_toolkit.shortcuts.prompt import PromptSession | |
@@ -131,6 +132,18 b' class TerminalPdb(Pdb):' | |||||
131 | except Exception: |
|
132 | except Exception: | |
132 | raise |
|
133 | raise | |
133 |
|
134 | |||
|
135 | def do_interact(self, arg): | |||
|
136 | ipshell = embed.InteractiveShellEmbed( | |||
|
137 | config=self.shell.config, | |||
|
138 | banner1="*interactive*", | |||
|
139 | exit_msg="*exiting interactive console...*", | |||
|
140 | ) | |||
|
141 | global_ns = self.curframe.f_globals | |||
|
142 | ipshell( | |||
|
143 | module=sys.modules.get(global_ns["__name__"], None), | |||
|
144 | local_ns=self.curframe_locals, | |||
|
145 | ) | |||
|
146 | ||||
134 |
|
147 | |||
135 | def set_trace(frame=None): |
|
148 | def set_trace(frame=None): | |
136 | """ |
|
149 | """ | |
@@ -148,6 +161,6 b" if __name__ == '__main__':" | |||||
148 | # happened after hitting "c", this is needed in order to |
|
161 | # happened after hitting "c", this is needed in order to | |
149 | # be able to quit the debugging session (see #9950). |
|
162 | # be able to quit the debugging session (see #9950). | |
150 | old_trace_dispatch = pdb.Pdb.trace_dispatch |
|
163 | old_trace_dispatch = pdb.Pdb.trace_dispatch | |
151 | pdb.Pdb = TerminalPdb |
|
164 | pdb.Pdb = TerminalPdb # type: ignore | |
152 | pdb.Pdb.trace_dispatch = old_trace_dispatch |
|
165 | pdb.Pdb.trace_dispatch = old_trace_dispatch # type: ignore | |
153 | pdb.main() |
|
166 | pdb.main() |
@@ -19,6 +19,8 b' from IPython.terminal.ipapp import load_default_config' | |||||
19 | from traitlets import Bool, CBool, Unicode |
|
19 | from traitlets import Bool, CBool, Unicode | |
20 | from IPython.utils.io import ask_yes_no |
|
20 | from IPython.utils.io import ask_yes_no | |
21 |
|
21 | |||
|
22 | from typing import Set | |||
|
23 | ||||
22 | class KillEmbedded(Exception):pass |
|
24 | class KillEmbedded(Exception):pass | |
23 |
|
25 | |||
24 | # kept for backward compatibility as IPython 6 was released with |
|
26 | # kept for backward compatibility as IPython 6 was released with | |
@@ -123,17 +125,17 b' class InteractiveShellEmbed(TerminalInteractiveShell):' | |||||
123 | help="Automatically set the terminal title" |
|
125 | help="Automatically set the terminal title" | |
124 | ).tag(config=True) |
|
126 | ).tag(config=True) | |
125 |
|
127 | |||
126 | _inactive_locations = set() |
|
128 | _inactive_locations: Set[str] = set() | |
|
129 | ||||
|
130 | def _disable_init_location(self): | |||
|
131 | """Disable the current Instance creation location""" | |||
|
132 | InteractiveShellEmbed._inactive_locations.add(self._init_location_id) | |||
127 |
|
133 | |||
128 | @property |
|
134 | @property | |
129 | def embedded_active(self): |
|
135 | def embedded_active(self): | |
130 | return (self._call_location_id not in InteractiveShellEmbed._inactive_locations)\ |
|
136 | return (self._call_location_id not in InteractiveShellEmbed._inactive_locations)\ | |
131 | and (self._init_location_id not in InteractiveShellEmbed._inactive_locations) |
|
137 | and (self._init_location_id not in InteractiveShellEmbed._inactive_locations) | |
132 |
|
138 | |||
133 | def _disable_init_location(self): |
|
|||
134 | """Disable the current Instance creation location""" |
|
|||
135 | InteractiveShellEmbed._inactive_locations.add(self._init_location_id) |
|
|||
136 |
|
||||
137 | @embedded_active.setter |
|
139 | @embedded_active.setter | |
138 | def embedded_active(self, value): |
|
140 | def embedded_active(self, value): | |
139 | if value: |
|
141 | if value: | |
@@ -334,7 +336,7 b' class InteractiveShellEmbed(TerminalInteractiveShell):' | |||||
334 | self.compile.flags = orig_compile_flags |
|
336 | self.compile.flags = orig_compile_flags | |
335 |
|
337 | |||
336 |
|
338 | |||
337 | def embed(**kwargs): |
|
339 | def embed(*, header="", compile_flags=None, **kwargs): | |
338 | """Call this to embed IPython at the current point in your program. |
|
340 | """Call this to embed IPython at the current point in your program. | |
339 |
|
341 | |||
340 | The first invocation of this will create an :class:`InteractiveShellEmbed` |
|
342 | The first invocation of this will create an :class:`InteractiveShellEmbed` | |
@@ -360,8 +362,6 b' def embed(**kwargs):' | |||||
360 | config argument. |
|
362 | config argument. | |
361 | """ |
|
363 | """ | |
362 | config = kwargs.get('config') |
|
364 | config = kwargs.get('config') | |
363 | header = kwargs.pop('header', u'') |
|
|||
364 | compile_flags = kwargs.pop('compile_flags', None) |
|
|||
365 | if config is None: |
|
365 | if config is None: | |
366 | config = load_default_config() |
|
366 | config = load_default_config() | |
367 | config.InteractiveShellEmbed = config.TerminalInteractiveShell |
|
367 | config.InteractiveShellEmbed = config.TerminalInteractiveShell |
@@ -204,9 +204,8 b' class TerminalInteractiveShell(InteractiveShell):' | |||||
204 |
|
204 | |||
205 | @observe('editing_mode') |
|
205 | @observe('editing_mode') | |
206 | def _editing_mode(self, change): |
|
206 | def _editing_mode(self, change): | |
207 | u_mode = change.new.upper() |
|
|||
208 | if self.pt_app: |
|
207 | if self.pt_app: | |
209 |
self.pt_app.editing_mode = |
|
208 | self.pt_app.editing_mode = getattr(EditingMode, change.new.upper()) | |
210 |
|
209 | |||
211 | @observe('autoformatter') |
|
210 | @observe('autoformatter') | |
212 | def _autoformatter_changed(self, change): |
|
211 | def _autoformatter_changed(self, change): | |
@@ -615,6 +614,13 b' class TerminalInteractiveShell(InteractiveShell):' | |||||
615 |
|
614 | |||
616 | self.restore_term_title() |
|
615 | self.restore_term_title() | |
617 |
|
616 | |||
|
617 | # try to call some at-exit operation optimistically as some things can't | |||
|
618 | # be done during interpreter shutdown. this is technically inaccurate as | |||
|
619 | # this make mainlool not re-callable, but that should be a rare if not | |||
|
620 | # in existent use case. | |||
|
621 | ||||
|
622 | self._atexit_once() | |||
|
623 | ||||
618 |
|
624 | |||
619 | _inputhook = None |
|
625 | _inputhook = None | |
620 | def inputhook(self, context): |
|
626 | def inputhook(self, context): |
@@ -7,13 +7,20 b' aliases = {' | |||||
7 | } |
|
7 | } | |
8 |
|
8 | |||
9 | backends = [ |
|
9 | backends = [ | |
10 | 'qt', 'qt4', 'qt5', |
|
10 | "qt", | |
11 | 'gtk', 'gtk2', 'gtk3', |
|
11 | "qt4", | |
12 |
|
|
12 | "qt5", | |
13 | 'wx', |
|
13 | "qt6", | |
14 | 'pyglet', 'glut', |
|
14 | "gtk", | |
15 | 'osx', |
|
15 | "gtk2", | |
16 | 'asyncio' |
|
16 | "gtk3", | |
|
17 | "gtk4", | |||
|
18 | "tk", | |||
|
19 | "wx", | |||
|
20 | "pyglet", | |||
|
21 | "glut", | |||
|
22 | "osx", | |||
|
23 | "asyncio", | |||
17 | ] |
|
24 | ] | |
18 |
|
25 | |||
19 | registered = {} |
|
26 | registered = {} | |
@@ -22,6 +29,7 b' def register(name, inputhook):' | |||||
22 | """Register the function *inputhook* as an event loop integration.""" |
|
29 | """Register the function *inputhook* as an event loop integration.""" | |
23 | registered[name] = inputhook |
|
30 | registered[name] = inputhook | |
24 |
|
31 | |||
|
32 | ||||
25 | class UnknownBackend(KeyError): |
|
33 | class UnknownBackend(KeyError): | |
26 | def __init__(self, name): |
|
34 | def __init__(self, name): | |
27 | self.name = name |
|
35 | self.name = name | |
@@ -31,6 +39,7 b' class UnknownBackend(KeyError):' | |||||
31 | "Supported event loops are: {}").format(self.name, |
|
39 | "Supported event loops are: {}").format(self.name, | |
32 | ', '.join(backends + sorted(registered))) |
|
40 | ', '.join(backends + sorted(registered))) | |
33 |
|
41 | |||
|
42 | ||||
34 | def get_inputhook_name_and_func(gui): |
|
43 | def get_inputhook_name_and_func(gui): | |
35 | if gui in registered: |
|
44 | if gui in registered: | |
36 | return gui, registered[gui] |
|
45 | return gui, registered[gui] | |
@@ -42,9 +51,12 b' def get_inputhook_name_and_func(gui):' | |||||
42 | return get_inputhook_name_and_func(aliases[gui]) |
|
51 | return get_inputhook_name_and_func(aliases[gui]) | |
43 |
|
52 | |||
44 | gui_mod = gui |
|
53 | gui_mod = gui | |
45 |
if gui == |
|
54 | if gui == "qt5": | |
46 |
os.environ[ |
|
55 | os.environ["QT_API"] = "pyqt5" | |
47 |
gui_mod = |
|
56 | gui_mod = "qt" | |
|
57 | elif gui == "qt6": | |||
|
58 | os.environ["QT_API"] = "pyqt6" | |||
|
59 | gui_mod = "qt" | |||
48 |
|
60 | |||
49 | mod = importlib.import_module('IPython.terminal.pt_inputhooks.'+gui_mod) |
|
61 | mod = importlib.import_module('IPython.terminal.pt_inputhooks.'+gui_mod) | |
50 | return gui, mod.inputhook |
|
62 | return gui, mod.inputhook |
@@ -9,7 +9,7 b' import ctypes' | |||||
9 | import ctypes.util |
|
9 | import ctypes.util | |
10 | from threading import Event |
|
10 | from threading import Event | |
11 |
|
11 | |||
12 |
objc = ctypes.cdll.LoadLibrary(ctypes.util.find_library( |
|
12 | objc = ctypes.cdll.LoadLibrary(ctypes.util.find_library("objc")) # type: ignore | |
13 |
|
13 | |||
14 | void_p = ctypes.c_void_p |
|
14 | void_p = ctypes.c_void_p | |
15 |
|
15 | |||
@@ -37,7 +37,7 b' def C(classname):' | |||||
37 | # end obj-c boilerplate from appnope |
|
37 | # end obj-c boilerplate from appnope | |
38 |
|
38 | |||
39 | # CoreFoundation C-API calls we will use: |
|
39 | # CoreFoundation C-API calls we will use: | |
40 |
CoreFoundation = ctypes.cdll.LoadLibrary(ctypes.util.find_library( |
|
40 | CoreFoundation = ctypes.cdll.LoadLibrary(ctypes.util.find_library("CoreFoundation")) # type: ignore | |
41 |
|
41 | |||
42 | CFFileDescriptorCreate = CoreFoundation.CFFileDescriptorCreate |
|
42 | CFFileDescriptorCreate = CoreFoundation.CFFileDescriptorCreate | |
43 | CFFileDescriptorCreate.restype = void_p |
|
43 | CFFileDescriptorCreate.restype = void_p |
@@ -1,6 +1,6 b'' | |||||
1 | import sys |
|
1 | import sys | |
2 | import os |
|
2 | import os | |
3 | from IPython.external.qt_for_kernel import QtCore, QtGui |
|
3 | from IPython.external.qt_for_kernel import QtCore, QtGui, enum_helper | |
4 | from IPython import get_ipython |
|
4 | from IPython import get_ipython | |
5 |
|
5 | |||
6 | # If we create a QApplication, keep a reference to it so that it doesn't get |
|
6 | # If we create a QApplication, keep a reference to it so that it doesn't get | |
@@ -9,6 +9,11 b' _appref = None' | |||||
9 | _already_warned = False |
|
9 | _already_warned = False | |
10 |
|
10 | |||
11 |
|
11 | |||
|
12 | def _exec(obj): | |||
|
13 | # exec on PyQt6, exec_ elsewhere. | |||
|
14 | obj.exec() if hasattr(obj, "exec") else obj.exec_() | |||
|
15 | ||||
|
16 | ||||
12 | def _reclaim_excepthook(): |
|
17 | def _reclaim_excepthook(): | |
13 | shell = get_ipython() |
|
18 | shell = get_ipython() | |
14 | if shell is not None: |
|
19 | if shell is not None: | |
@@ -32,7 +37,16 b' def inputhook(context):' | |||||
32 | 'variable. Deactivate Qt5 code.' |
|
37 | 'variable. Deactivate Qt5 code.' | |
33 | ) |
|
38 | ) | |
34 | return |
|
39 | return | |
35 | QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling) |
|
40 | try: | |
|
41 | QtCore.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling) | |||
|
42 | except AttributeError: # Only for Qt>=5.6, <6. | |||
|
43 | pass | |||
|
44 | try: | |||
|
45 | QtCore.QApplication.setHighDpiScaleFactorRoundingPolicy( | |||
|
46 | QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough | |||
|
47 | ) | |||
|
48 | except AttributeError: # Only for Qt>=5.14. | |||
|
49 | pass | |||
36 | _appref = app = QtGui.QApplication([" "]) |
|
50 | _appref = app = QtGui.QApplication([" "]) | |
37 |
|
51 | |||
38 | # "reclaim" IPython sys.excepthook after event loop starts |
|
52 | # "reclaim" IPython sys.excepthook after event loop starts | |
@@ -55,8 +69,9 b' def inputhook(context):' | |||||
55 | else: |
|
69 | else: | |
56 | # On POSIX platforms, we can use a file descriptor to quit the event |
|
70 | # On POSIX platforms, we can use a file descriptor to quit the event | |
57 | # loop when there is input ready to read. |
|
71 | # loop when there is input ready to read. | |
58 |
notifier = QtCore.QSocketNotifier( |
|
72 | notifier = QtCore.QSocketNotifier( | |
59 | QtCore.QSocketNotifier.Read) |
|
73 | context.fileno(), enum_helper("QtCore.QSocketNotifier.Type").Read | |
|
74 | ) | |||
60 | try: |
|
75 | try: | |
61 | # connect the callback we care about before we turn it on |
|
76 | # connect the callback we care about before we turn it on | |
62 | # lambda is necessary as PyQT inspect the function signature to know |
|
77 | # lambda is necessary as PyQT inspect the function signature to know | |
@@ -65,6 +80,6 b' def inputhook(context):' | |||||
65 | notifier.setEnabled(True) |
|
80 | notifier.setEnabled(True) | |
66 | # only start the event loop we are not already flipped |
|
81 | # only start the event loop we are not already flipped | |
67 | if not context.input_is_ready(): |
|
82 | if not context.input_is_ready(): | |
68 |
event_loop |
|
83 | _exec(event_loop) | |
69 | finally: |
|
84 | finally: | |
70 | notifier.setEnabled(False) |
|
85 | notifier.setEnabled(False) |
@@ -52,10 +52,8 b' def process_handler(cmd, callback, stderr=subprocess.PIPE):' | |||||
52 | A command to be executed by the system, using :class:`subprocess.Popen`. |
|
52 | A command to be executed by the system, using :class:`subprocess.Popen`. | |
53 | If a string is passed, it will be run in the system shell. If a list is |
|
53 | If a string is passed, it will be run in the system shell. If a list is | |
54 | passed, it will be used directly as arguments. |
|
54 | passed, it will be used directly as arguments. | |
55 |
|
||||
56 | callback : callable |
|
55 | callback : callable | |
57 | A one-argument function that will be called with the Popen object. |
|
56 | A one-argument function that will be called with the Popen object. | |
58 |
|
||||
59 | stderr : file descriptor number, optional |
|
57 | stderr : file descriptor number, optional | |
60 | By default this is set to ``subprocess.PIPE``, but you can also pass the |
|
58 | By default this is set to ``subprocess.PIPE``, but you can also pass the | |
61 | value ``subprocess.STDOUT`` to force the subprocess' stderr to go into |
|
59 | value ``subprocess.STDOUT`` to force the subprocess' stderr to go into |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file |
@@ -28,12 +28,10 b' def extract_vars(*names,**kw):' | |||||
28 | *names : str |
|
28 | *names : str | |
29 | One or more variable names which will be extracted from the caller's |
|
29 | One or more variable names which will be extracted from the caller's | |
30 | frame. |
|
30 | frame. | |
31 |
|
31 | **kw : integer, optional | ||
32 | depth : integer, optional |
|
|||
33 | How many frames in the stack to walk when looking for your variables. |
|
32 | How many frames in the stack to walk when looking for your variables. | |
34 | The default is 0, which will use the frame where the call was made. |
|
33 | The default is 0, which will use the frame where the call was made. | |
35 |
|
34 | |||
36 |
|
||||
37 | Examples |
|
35 | Examples | |
38 | -------- |
|
36 | -------- | |
39 | :: |
|
37 | :: |
@@ -22,7 +22,6 b' def complete_object(obj, prev_completions):' | |||||
22 | The object to complete. |
|
22 | The object to complete. | |
23 | prev_completions : list |
|
23 | prev_completions : list | |
24 | List of attributes discovered so far. |
|
24 | List of attributes discovered so far. | |
25 |
|
||||
26 | This should return the list of attributes in obj. If you only wish to |
|
25 | This should return the list of attributes in obj. If you only wish to | |
27 | add to the attributes already discovered normally, return |
|
26 | add to the attributes already discovered normally, return | |
28 | own_attrs + prev_completions. |
|
27 | own_attrs + prev_completions. |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file |
@@ -114,10 +114,8 b' class Tee(object):' | |||||
114 | ---------- |
|
114 | ---------- | |
115 | file_or_name : filename or open filehandle (writable) |
|
115 | file_or_name : filename or open filehandle (writable) | |
116 | File that will be duplicated |
|
116 | File that will be duplicated | |
117 |
|
||||
118 | mode : optional, valid mode for open(). |
|
117 | mode : optional, valid mode for open(). | |
119 | If a filename was give, open with this mode. |
|
118 | If a filename was give, open with this mode. | |
120 |
|
||||
121 | channel : str, one of ['stdout', 'stderr'] |
|
119 | channel : str, one of ['stdout', 'stderr'] | |
122 | """ |
|
120 | """ | |
123 | if channel not in ['stdout', 'stderr']: |
|
121 | if channel not in ['stdout', 'stderr']: | |
@@ -196,7 +194,6 b" def temp_pyfile(src, ext='.py'):" | |||||
196 | ---------- |
|
194 | ---------- | |
197 | src : string or list of strings (no need for ending newlines if list) |
|
195 | src : string or list of strings (no need for ending newlines if list) | |
198 | Source code to be written to the file. |
|
196 | Source code to be written to the file. | |
199 |
|
||||
200 | ext : optional, string |
|
197 | ext : optional, string | |
201 | Extension for the generated file. |
|
198 | Extension for the generated file. | |
202 |
|
199 |
@@ -43,14 +43,13 b' class Struct(dict):' | |||||
43 |
|
43 | |||
44 | Parameters |
|
44 | Parameters | |
45 | ---------- |
|
45 | ---------- | |
46 | args : dict, Struct |
|
46 | *args : dict, Struct | |
47 | Initialize with one dict or Struct |
|
47 | Initialize with one dict or Struct | |
48 | kw : dict |
|
48 | **kw : dict | |
49 | Initialize with key, value pairs. |
|
49 | Initialize with key, value pairs. | |
50 |
|
50 | |||
51 | Examples |
|
51 | Examples | |
52 | -------- |
|
52 | -------- | |
53 |
|
||||
54 | >>> s = Struct(a=10,b=30) |
|
53 | >>> s = Struct(a=10,b=30) | |
55 | >>> s.a |
|
54 | >>> s.a | |
56 | 10 |
|
55 | 10 | |
@@ -68,7 +67,6 b' class Struct(dict):' | |||||
68 |
|
67 | |||
69 | Examples |
|
68 | Examples | |
70 | -------- |
|
69 | -------- | |
71 |
|
||||
72 | >>> s = Struct() |
|
70 | >>> s = Struct() | |
73 | >>> s['a'] = 10 |
|
71 | >>> s['a'] = 10 | |
74 | >>> s.allow_new_attr(False) |
|
72 | >>> s.allow_new_attr(False) | |
@@ -95,7 +93,6 b' class Struct(dict):' | |||||
95 |
|
93 | |||
96 | Examples |
|
94 | Examples | |
97 | -------- |
|
95 | -------- | |
98 |
|
||||
99 | >>> s = Struct() |
|
96 | >>> s = Struct() | |
100 | >>> s.a = 10 |
|
97 | >>> s.a = 10 | |
101 | >>> s.a |
|
98 | >>> s.a | |
@@ -130,7 +127,6 b' class Struct(dict):' | |||||
130 |
|
127 | |||
131 | Examples |
|
128 | Examples | |
132 | -------- |
|
129 | -------- | |
133 |
|
||||
134 | >>> s = Struct(a=10) |
|
130 | >>> s = Struct(a=10) | |
135 | >>> s.a |
|
131 | >>> s.a | |
136 | 10 |
|
132 | 10 | |
@@ -155,7 +151,6 b' class Struct(dict):' | |||||
155 |
|
151 | |||
156 | Examples |
|
152 | Examples | |
157 | -------- |
|
153 | -------- | |
158 |
|
||||
159 | >>> s = Struct(a=10,b=30) |
|
154 | >>> s = Struct(a=10,b=30) | |
160 | >>> s2 = Struct(a=20,c=40) |
|
155 | >>> s2 = Struct(a=20,c=40) | |
161 | >>> s += s2 |
|
156 | >>> s += s2 | |
@@ -170,7 +165,6 b' class Struct(dict):' | |||||
170 |
|
165 | |||
171 | Examples |
|
166 | Examples | |
172 | -------- |
|
167 | -------- | |
173 |
|
||||
174 | >>> s1 = Struct(a=10,b=30) |
|
168 | >>> s1 = Struct(a=10,b=30) | |
175 | >>> s2 = Struct(a=20,c=40) |
|
169 | >>> s2 = Struct(a=20,c=40) | |
176 | >>> s = s1 + s2 |
|
170 | >>> s = s1 + s2 | |
@@ -186,7 +180,6 b' class Struct(dict):' | |||||
186 |
|
180 | |||
187 | Examples |
|
181 | Examples | |
188 | -------- |
|
182 | -------- | |
189 |
|
||||
190 | >>> s1 = Struct(a=10,b=30) |
|
183 | >>> s1 = Struct(a=10,b=30) | |
191 | >>> s2 = Struct(a=40) |
|
184 | >>> s2 = Struct(a=40) | |
192 | >>> s = s1 - s2 |
|
185 | >>> s = s1 - s2 | |
@@ -202,7 +195,6 b' class Struct(dict):' | |||||
202 |
|
195 | |||
203 | Examples |
|
196 | Examples | |
204 | -------- |
|
197 | -------- | |
205 |
|
||||
206 | >>> s1 = Struct(a=10,b=30) |
|
198 | >>> s1 = Struct(a=10,b=30) | |
207 | >>> s2 = Struct(a=40) |
|
199 | >>> s2 = Struct(a=40) | |
208 | >>> s1 -= s2 |
|
200 | >>> s1 -= s2 | |
@@ -236,7 +228,6 b' class Struct(dict):' | |||||
236 |
|
228 | |||
237 | Examples |
|
229 | Examples | |
238 | -------- |
|
230 | -------- | |
239 |
|
||||
240 | >>> s = Struct(a=10,b=30) |
|
231 | >>> s = Struct(a=10,b=30) | |
241 | >>> s2 = s.copy() |
|
232 | >>> s2 = s.copy() | |
242 | >>> type(s2) is Struct |
|
233 | >>> type(s2) is Struct | |
@@ -251,7 +242,6 b' class Struct(dict):' | |||||
251 |
|
242 | |||
252 | Examples |
|
243 | Examples | |
253 | -------- |
|
244 | -------- | |
254 |
|
||||
255 | >>> s = Struct(a=10) |
|
245 | >>> s = Struct(a=10) | |
256 | >>> s.hasattr('a') |
|
246 | >>> s.hasattr('a') | |
257 | True |
|
247 | True | |
@@ -284,7 +274,7 b' class Struct(dict):' | |||||
284 |
|
274 | |||
285 | Parameters |
|
275 | Parameters | |
286 | ---------- |
|
276 | ---------- | |
287 | __loc_data : dict, Struct |
|
277 | __loc_data__ : dict, Struct | |
288 | The data to merge into self |
|
278 | The data to merge into self | |
289 | __conflict_solve : dict |
|
279 | __conflict_solve : dict | |
290 | The conflict policy dict. The keys are binary functions used to |
|
280 | The conflict policy dict. The keys are binary functions used to | |
@@ -292,12 +282,11 b' class Struct(dict):' | |||||
292 | the keys the conflict resolution function applies to. Instead of |
|
282 | the keys the conflict resolution function applies to. Instead of | |
293 | a list of strings a space separated string can be used, like |
|
283 | a list of strings a space separated string can be used, like | |
294 | 'a b c'. |
|
284 | 'a b c'. | |
295 | kw : dict |
|
285 | **kw : dict | |
296 | Additional key, value pairs to merge in |
|
286 | Additional key, value pairs to merge in | |
297 |
|
287 | |||
298 | Notes |
|
288 | Notes | |
299 | ----- |
|
289 | ----- | |
300 |
|
||||
301 | The `__conflict_solve` dict is a dictionary of binary functions which will be used to |
|
290 | The `__conflict_solve` dict is a dictionary of binary functions which will be used to | |
302 | solve key conflicts. Here is an example:: |
|
291 | solve key conflicts. Here is an example:: | |
303 |
|
292 | |||
@@ -338,7 +327,6 b' class Struct(dict):' | |||||
338 |
|
327 | |||
339 | Examples |
|
328 | Examples | |
340 | -------- |
|
329 | -------- | |
341 |
|
||||
342 | This show the default policy: |
|
330 | This show the default policy: | |
343 |
|
331 | |||
344 | >>> s = Struct(a=10,b=30) |
|
332 | >>> s = Struct(a=10,b=30) |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file |
@@ -109,7 +109,7 b' def get_py_filename(name, force_win32=None):' | |||||
109 | raise IOError('File `%r` not found.' % name) |
|
109 | raise IOError('File `%r` not found.' % name) | |
110 |
|
110 | |||
111 |
|
111 | |||
112 | def filefind(filename, path_dirs=None): |
|
112 | def filefind(filename: str, path_dirs=None) -> str: | |
113 | """Find a file by looking through a sequence of paths. |
|
113 | """Find a file by looking through a sequence of paths. | |
114 |
|
114 | |||
115 | This iterates through a sequence of paths looking for a file and returns |
|
115 | This iterates through a sequence of paths looking for a file and returns | |
@@ -139,7 +139,12 b' def filefind(filename, path_dirs=None):' | |||||
139 |
|
139 | |||
140 | Returns |
|
140 | Returns | |
141 | ------- |
|
141 | ------- | |
142 | Raises :exc:`IOError` or returns absolute path to file. |
|
142 | path : str | |
|
143 | returns absolute path to file. | |||
|
144 | ||||
|
145 | Raises | |||
|
146 | ------ | |||
|
147 | IOError | |||
143 | """ |
|
148 | """ | |
144 |
|
149 | |||
145 | # If paths are quoted, abspath gets confused, strip them... |
|
150 | # If paths are quoted, abspath gets confused, strip them... | |
@@ -178,7 +183,6 b' def get_home_dir(require_writable=False) -> str:' | |||||
178 |
|
183 | |||
179 | Parameters |
|
184 | Parameters | |
180 | ---------- |
|
185 | ---------- | |
181 |
|
||||
182 | require_writable : bool [default: False] |
|
186 | require_writable : bool [default: False] | |
183 | if True: |
|
187 | if True: | |
184 | guarantees the return value is a writable directory, otherwise |
|
188 | guarantees the return value is a writable directory, otherwise |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file |
@@ -15,7 +15,6 b'' | |||||
15 |
|
15 | |||
16 | import sys |
|
16 | import sys | |
17 |
|
17 | |||
18 | import nose.tools as nt |
|
|||
19 | import pytest |
|
18 | import pytest | |
20 |
|
19 | |||
21 | from IPython.testing.decorators import skip_iptest_but_not_pytest |
|
20 | from IPython.testing.decorators import skip_iptest_but_not_pytest | |
@@ -75,18 +74,18 b' def test_rich_output_empty(method_mime):' | |||||
75 | """RichOutput with no args""" |
|
74 | """RichOutput with no args""" | |
76 | rich = capture.RichOutput() |
|
75 | rich = capture.RichOutput() | |
77 | method, mime = method_mime |
|
76 | method, mime = method_mime | |
78 |
|
|
77 | assert getattr(rich, method)() is None | |
79 |
|
78 | |||
80 | def test_rich_output(): |
|
79 | def test_rich_output(): | |
81 | """test RichOutput basics""" |
|
80 | """test RichOutput basics""" | |
82 | data = basic_data |
|
81 | data = basic_data | |
83 | metadata = basic_metadata |
|
82 | metadata = basic_metadata | |
84 | rich = capture.RichOutput(data=data, metadata=metadata) |
|
83 | rich = capture.RichOutput(data=data, metadata=metadata) | |
85 |
|
|
84 | assert rich._repr_html_() == data["text/html"] | |
86 |
|
|
85 | assert rich._repr_png_() == (data["image/png"], metadata["image/png"]) | |
87 |
|
|
86 | assert rich._repr_latex_() is None | |
88 |
|
|
87 | assert rich._repr_javascript_() is None | |
89 |
|
|
88 | assert rich._repr_svg_() is None | |
90 |
|
89 | |||
91 |
|
90 | |||
92 | @skip_iptest_but_not_pytest |
|
91 | @skip_iptest_but_not_pytest | |
@@ -96,7 +95,7 b' def test_rich_output_no_metadata(method_mime):' | |||||
96 | data = full_data |
|
95 | data = full_data | |
97 | rich = capture.RichOutput(data=data) |
|
96 | rich = capture.RichOutput(data=data) | |
98 | method, mime = method_mime |
|
97 | method, mime = method_mime | |
99 |
|
|
98 | assert getattr(rich, method)() == data[mime] | |
100 |
|
99 | |||
101 |
|
100 | |||
102 | @skip_iptest_but_not_pytest |
|
101 | @skip_iptest_but_not_pytest | |
@@ -107,7 +106,7 b' def test_rich_output_metadata(method_mime):' | |||||
107 | metadata = full_metadata |
|
106 | metadata = full_metadata | |
108 | rich = capture.RichOutput(data=data, metadata=metadata) |
|
107 | rich = capture.RichOutput(data=data, metadata=metadata) | |
109 | method, mime = method_mime |
|
108 | method, mime = method_mime | |
110 |
|
|
109 | assert getattr(rich, method)() == (data[mime], metadata[mime]) | |
111 |
|
110 | |||
112 | def test_rich_output_display(): |
|
111 | def test_rich_output_display(): | |
113 | """test RichOutput.display |
|
112 | """test RichOutput.display | |
@@ -119,10 +118,10 b' def test_rich_output_display():' | |||||
119 | rich = capture.RichOutput(data=data) |
|
118 | rich = capture.RichOutput(data=data) | |
120 | with capture.capture_output() as cap: |
|
119 | with capture.capture_output() as cap: | |
121 | rich.display() |
|
120 | rich.display() | |
122 |
|
|
121 | assert len(cap.outputs) == 1 | |
123 | rich2 = cap.outputs[0] |
|
122 | rich2 = cap.outputs[0] | |
124 |
|
|
123 | assert rich2.data == rich.data | |
125 |
|
|
124 | assert rich2.metadata == rich.metadata | |
126 |
|
125 | |||
127 | def test_capture_output(): |
|
126 | def test_capture_output(): | |
128 | """capture_output works""" |
|
127 | """capture_output works""" | |
@@ -131,8 +130,8 b' def test_capture_output():' | |||||
131 | print(hello_stdout, end="") |
|
130 | print(hello_stdout, end="") | |
132 | print(hello_stderr, end="", file=sys.stderr) |
|
131 | print(hello_stderr, end="", file=sys.stderr) | |
133 | rich.display() |
|
132 | rich.display() | |
134 |
|
|
133 | assert hello_stdout == cap.stdout | |
135 |
|
|
134 | assert hello_stderr == cap.stderr | |
136 |
|
135 | |||
137 |
|
136 | |||
138 | def test_capture_output_no_stdout(): |
|
137 | def test_capture_output_no_stdout(): | |
@@ -142,9 +141,9 b' def test_capture_output_no_stdout():' | |||||
142 | print(hello_stdout, end="") |
|
141 | print(hello_stdout, end="") | |
143 | print(hello_stderr, end="", file=sys.stderr) |
|
142 | print(hello_stderr, end="", file=sys.stderr) | |
144 | rich.display() |
|
143 | rich.display() | |
145 |
|
|
144 | assert "" == cap.stdout | |
146 |
|
|
145 | assert hello_stderr == cap.stderr | |
147 |
|
|
146 | assert len(cap.outputs) == 1 | |
148 |
|
147 | |||
149 |
|
148 | |||
150 | def test_capture_output_no_stderr(): |
|
149 | def test_capture_output_no_stderr(): | |
@@ -155,9 +154,9 b' def test_capture_output_no_stderr():' | |||||
155 | print(hello_stdout, end="") |
|
154 | print(hello_stdout, end="") | |
156 | print(hello_stderr, end="", file=sys.stderr) |
|
155 | print(hello_stderr, end="", file=sys.stderr) | |
157 | rich.display() |
|
156 | rich.display() | |
158 |
|
|
157 | assert hello_stdout == cap.stdout | |
159 |
|
|
158 | assert "" == cap.stderr | |
160 |
|
|
159 | assert len(cap.outputs) == 1 | |
161 |
|
160 | |||
162 |
|
161 | |||
163 | def test_capture_output_no_display(): |
|
162 | def test_capture_output_no_display(): | |
@@ -167,6 +166,6 b' def test_capture_output_no_display():' | |||||
167 | print(hello_stdout, end="") |
|
166 | print(hello_stdout, end="") | |
168 | print(hello_stderr, end="", file=sys.stderr) |
|
167 | print(hello_stderr, end="", file=sys.stderr) | |
169 | rich.display() |
|
168 | rich.display() | |
170 |
|
|
169 | assert hello_stdout == cap.stdout | |
171 |
|
|
170 | assert hello_stderr == cap.stderr | |
172 |
|
|
171 | assert cap.outputs == [] |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file |
@@ -113,7 +113,7 b' def test_get_home_dir_1():' | |||||
113 | IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py")) |
|
113 | IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py")) | |
114 |
|
114 | |||
115 | home_dir = path.get_home_dir() |
|
115 | home_dir = path.get_home_dir() | |
116 |
|
|
116 | assert home_dir == unfrozen | |
117 |
|
117 | |||
118 |
|
118 | |||
119 | @skip_if_not_win32 |
|
119 | @skip_if_not_win32 | |
@@ -127,7 +127,7 b' def test_get_home_dir_2():' | |||||
127 | IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower() |
|
127 | IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower() | |
128 |
|
128 | |||
129 | home_dir = path.get_home_dir(True) |
|
129 | home_dir = path.get_home_dir(True) | |
130 |
|
|
130 | assert home_dir == unfrozen | |
131 |
|
131 | |||
132 |
|
132 | |||
133 | @skip_win32_py38 |
|
133 | @skip_win32_py38 | |
@@ -137,7 +137,7 b' def test_get_home_dir_3():' | |||||
137 | env["HOME"] = HOME_TEST_DIR |
|
137 | env["HOME"] = HOME_TEST_DIR | |
138 | home_dir = path.get_home_dir(True) |
|
138 | home_dir = path.get_home_dir(True) | |
139 | # get_home_dir expands symlinks |
|
139 | # get_home_dir expands symlinks | |
140 |
|
|
140 | assert home_dir == os.path.realpath(env["HOME"]) | |
141 |
|
141 | |||
142 |
|
142 | |||
143 | @with_environment |
|
143 | @with_environment | |
@@ -181,7 +181,7 b' def test_get_home_dir_8():' | |||||
181 | with patch.object(wreg, 'OpenKey', return_value=key()), \ |
|
181 | with patch.object(wreg, 'OpenKey', return_value=key()), \ | |
182 | patch.object(wreg, 'QueryValueEx', return_value=[abspath(HOME_TEST_DIR)]): |
|
182 | patch.object(wreg, 'QueryValueEx', return_value=[abspath(HOME_TEST_DIR)]): | |
183 | home_dir = path.get_home_dir() |
|
183 | home_dir = path.get_home_dir() | |
184 |
|
|
184 | assert home_dir == abspath(HOME_TEST_DIR) | |
185 |
|
185 | |||
186 | @with_environment |
|
186 | @with_environment | |
187 | def test_get_xdg_dir_0(): |
|
187 | def test_get_xdg_dir_0(): | |
@@ -195,7 +195,7 b' def test_get_xdg_dir_0():' | |||||
195 | env.pop('IPYTHONDIR', None) |
|
195 | env.pop('IPYTHONDIR', None) | |
196 | env.pop('XDG_CONFIG_HOME', None) |
|
196 | env.pop('XDG_CONFIG_HOME', None) | |
197 |
|
197 | |||
198 |
|
|
198 | assert path.get_xdg_dir() == os.path.join("somewhere", ".config") | |
199 |
|
199 | |||
200 |
|
200 | |||
201 | @with_environment |
|
201 | @with_environment | |
@@ -208,7 +208,7 b' def test_get_xdg_dir_1():' | |||||
208 | env.pop('IPYTHON_DIR', None) |
|
208 | env.pop('IPYTHON_DIR', None) | |
209 | env.pop('IPYTHONDIR', None) |
|
209 | env.pop('IPYTHONDIR', None) | |
210 | env.pop('XDG_CONFIG_HOME', None) |
|
210 | env.pop('XDG_CONFIG_HOME', None) | |
211 |
|
|
211 | assert path.get_xdg_dir() is None | |
212 |
|
212 | |||
213 | @with_environment |
|
213 | @with_environment | |
214 | def test_get_xdg_dir_2(): |
|
214 | def test_get_xdg_dir_2(): | |
@@ -224,7 +224,7 b' def test_get_xdg_dir_2():' | |||||
224 | if not os.path.exists(cfgdir): |
|
224 | if not os.path.exists(cfgdir): | |
225 | os.makedirs(cfgdir) |
|
225 | os.makedirs(cfgdir) | |
226 |
|
226 | |||
227 |
|
|
227 | assert path.get_xdg_dir() == cfgdir | |
228 |
|
228 | |||
229 | @with_environment |
|
229 | @with_environment | |
230 | def test_get_xdg_dir_3(): |
|
230 | def test_get_xdg_dir_3(): | |
@@ -240,7 +240,7 b' def test_get_xdg_dir_3():' | |||||
240 | if not os.path.exists(cfgdir): |
|
240 | if not os.path.exists(cfgdir): | |
241 | os.makedirs(cfgdir) |
|
241 | os.makedirs(cfgdir) | |
242 |
|
242 | |||
243 |
|
|
243 | assert path.get_xdg_dir() is None | |
244 |
|
244 | |||
245 | def test_filefind(): |
|
245 | def test_filefind(): | |
246 | """Various tests for filefind""" |
|
246 | """Various tests for filefind""" | |
@@ -263,13 +263,13 b' def test_get_long_path_name_win32():' | |||||
263 | # Test to see if the short path evaluates correctly. |
|
263 | # Test to see if the short path evaluates correctly. | |
264 | short_path = os.path.join(tmpdir, 'THISIS~1') |
|
264 | short_path = os.path.join(tmpdir, 'THISIS~1') | |
265 | evaluated_path = path.get_long_path_name(short_path) |
|
265 | evaluated_path = path.get_long_path_name(short_path) | |
266 |
|
|
266 | assert evaluated_path.lower() == long_path.lower() | |
267 |
|
267 | |||
268 |
|
268 | |||
269 | @dec.skip_win32 |
|
269 | @dec.skip_win32 | |
270 | def test_get_long_path_name(): |
|
270 | def test_get_long_path_name(): | |
271 |
p = path.get_long_path_name( |
|
271 | p = path.get_long_path_name("/usr/local") | |
272 |
|
|
272 | assert p == "/usr/local" | |
273 |
|
273 | |||
274 |
|
274 | |||
275 | class TestRaiseDeprecation(unittest.TestCase): |
|
275 | class TestRaiseDeprecation(unittest.TestCase): | |
@@ -300,18 +300,18 b' class TestRaiseDeprecation(unittest.TestCase):' | |||||
300 | @with_environment |
|
300 | @with_environment | |
301 | def test_get_py_filename(): |
|
301 | def test_get_py_filename(): | |
302 | os.chdir(TMP_TEST_DIR) |
|
302 | os.chdir(TMP_TEST_DIR) | |
303 |
with make_tempfile( |
|
303 | with make_tempfile("foo.py"): | |
304 |
|
|
304 | assert path.get_py_filename("foo.py") == "foo.py" | |
305 |
|
|
305 | assert path.get_py_filename("foo") == "foo.py" | |
306 |
with make_tempfile( |
|
306 | with make_tempfile("foo"): | |
307 |
|
|
307 | assert path.get_py_filename("foo") == "foo" | |
308 |
nt.assert_raises(IOError, path.get_py_filename, |
|
308 | nt.assert_raises(IOError, path.get_py_filename, "foo.py") | |
309 |
nt.assert_raises(IOError, path.get_py_filename, |
|
309 | nt.assert_raises(IOError, path.get_py_filename, "foo") | |
310 |
nt.assert_raises(IOError, path.get_py_filename, |
|
310 | nt.assert_raises(IOError, path.get_py_filename, "foo.py") | |
311 |
true_fn = |
|
311 | true_fn = "foo with spaces.py" | |
312 | with make_tempfile(true_fn): |
|
312 | with make_tempfile(true_fn): | |
313 |
|
|
313 | assert path.get_py_filename("foo with spaces") == true_fn | |
314 |
|
|
314 | assert path.get_py_filename("foo with spaces.py") == true_fn | |
315 | nt.assert_raises(IOError, path.get_py_filename, '"foo with spaces.py"') |
|
315 | nt.assert_raises(IOError, path.get_py_filename, '"foo with spaces.py"') | |
316 | nt.assert_raises(IOError, path.get_py_filename, "'foo with spaces.py'") |
|
316 | nt.assert_raises(IOError, path.get_py_filename, "'foo with spaces.py'") | |
317 |
|
317 | |||
@@ -361,8 +361,7 b' class TestShellGlob(unittest.TestCase):' | |||||
361 | def check_match(self, patterns, matches): |
|
361 | def check_match(self, patterns, matches): | |
362 | with self.in_tempdir(): |
|
362 | with self.in_tempdir(): | |
363 | # glob returns unordered list. that's why sorted is required. |
|
363 | # glob returns unordered list. that's why sorted is required. | |
364 |
|
|
364 | assert sorted(path.shellglob(patterns)) == sorted(matches) | |
365 | sorted(matches)) |
|
|||
366 |
|
365 | |||
367 | def common_cases(self): |
|
366 | def common_cases(self): | |
368 | return [ |
|
367 | return [ | |
@@ -397,12 +396,13 b' class TestShellGlob(unittest.TestCase):' | |||||
397 | yield (self.check_match, patterns, matches) |
|
396 | yield (self.check_match, patterns, matches) | |
398 |
|
397 | |||
399 |
|
398 | |||
|
399 | # TODO : pytest.mark.parametrise once nose is gone. | |||
400 | def test_unescape_glob(): |
|
400 | def test_unescape_glob(): | |
401 |
|
|
401 | assert path.unescape_glob(r"\*\[\!\]\?") == "*[!]?" | |
402 |
|
|
402 | assert path.unescape_glob(r"\\*") == r"\*" | |
403 |
|
|
403 | assert path.unescape_glob(r"\\\*") == r"\*" | |
404 |
|
|
404 | assert path.unescape_glob(r"\\a") == r"\a" | |
405 |
|
|
405 | assert path.unescape_glob(r"\a") == r"\a" | |
406 |
|
406 | |||
407 |
|
407 | |||
408 | @onlyif_unicode_paths |
|
408 | @onlyif_unicode_paths | |
@@ -431,17 +431,19 b' class TestLinkOrCopy(unittest.TestCase):' | |||||
431 | return os.path.join(self.tempdir.name, *args) |
|
431 | return os.path.join(self.tempdir.name, *args) | |
432 |
|
432 | |||
433 | def assert_inode_not_equal(self, a, b): |
|
433 | def assert_inode_not_equal(self, a, b): | |
434 | nt.assert_not_equal(os.stat(a).st_ino, os.stat(b).st_ino, |
|
434 | assert ( | |
435 | "%r and %r do reference the same indoes" %(a, b)) |
|
435 | os.stat(a).st_ino != os.stat(b).st_ino | |
|
436 | ), "%r and %r do reference the same indoes" % (a, b) | |||
436 |
|
437 | |||
437 | def assert_inode_equal(self, a, b): |
|
438 | def assert_inode_equal(self, a, b): | |
438 | nt.assert_equal(os.stat(a).st_ino, os.stat(b).st_ino, |
|
439 | assert ( | |
439 | "%r and %r do not reference the same indoes" %(a, b)) |
|
440 | os.stat(a).st_ino == os.stat(b).st_ino | |
|
441 | ), "%r and %r do not reference the same indoes" % (a, b) | |||
440 |
|
442 | |||
441 | def assert_content_equal(self, a, b): |
|
443 | def assert_content_equal(self, a, b): | |
442 | with open(a) as a_f: |
|
444 | with open(a) as a_f: | |
443 | with open(b) as b_f: |
|
445 | with open(b) as b_f: | |
444 |
|
|
446 | assert a_f.read() == b_f.read() | |
445 |
|
447 | |||
446 | @skip_win32 |
|
448 | @skip_win32 | |
447 | def test_link_successful(self): |
|
449 | def test_link_successful(self): | |
@@ -489,4 +491,4 b' class TestLinkOrCopy(unittest.TestCase):' | |||||
489 | path.link_or_copy(self.src, dst) |
|
491 | path.link_or_copy(self.src, dst) | |
490 | path.link_or_copy(self.src, dst) |
|
492 | path.link_or_copy(self.src, dst) | |
491 | self.assert_inode_equal(self.src, dst) |
|
493 | self.assert_inode_equal(self.src, dst) | |
492 |
|
|
494 | assert sorted(os.listdir(self.tempdir.name)) == ["src", "target"] |
@@ -67,6 +67,7 b' def test_find_cmd_fail():' | |||||
67 | nt.assert_raises(FindCmdError,find_cmd,'asdfasdf') |
|
67 | nt.assert_raises(FindCmdError,find_cmd,'asdfasdf') | |
68 |
|
68 | |||
69 |
|
69 | |||
|
70 | # TODO: move to pytest.mark.parametrize once nose gone | |||
70 | @dec.skip_win32 |
|
71 | @dec.skip_win32 | |
71 | def test_arg_split(): |
|
72 | def test_arg_split(): | |
72 | """Ensure that argument lines are correctly split like in a shell.""" |
|
73 | """Ensure that argument lines are correctly split like in a shell.""" | |
@@ -80,8 +81,10 b' def test_arg_split():' | |||||
80 | ['something "with quotes"', ['something', '"with quotes"']], |
|
81 | ['something "with quotes"', ['something', '"with quotes"']], | |
81 | ] |
|
82 | ] | |
82 | for argstr, argv in tests: |
|
83 | for argstr, argv in tests: | |
83 |
|
|
84 | assert arg_split(argstr) == argv | |
84 |
|
85 | |||
|
86 | ||||
|
87 | # TODO: move to pytest.mark.parametrize once nose gone | |||
85 | @dec.skip_if_not_win32 |
|
88 | @dec.skip_if_not_win32 | |
86 | def test_arg_split_win32(): |
|
89 | def test_arg_split_win32(): | |
87 | """Ensure that argument lines are correctly split like in a shell.""" |
|
90 | """Ensure that argument lines are correctly split like in a shell.""" | |
@@ -92,7 +95,7 b' def test_arg_split_win32():' | |||||
92 | ['something "with quotes"', ['something', 'with quotes']], |
|
95 | ['something "with quotes"', ['something', 'with quotes']], | |
93 | ] |
|
96 | ] | |
94 | for argstr, argv in tests: |
|
97 | for argstr, argv in tests: | |
95 |
|
|
98 | assert arg_split(argstr) == argv | |
96 |
|
99 | |||
97 |
|
100 | |||
98 | class SubProcessTestCase(tt.TempFileMixin): |
|
101 | class SubProcessTestCase(tt.TempFileMixin): |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file |
@@ -32,31 +32,31 b' def test_columnize():' | |||||
32 | items = [l*size for l in 'abcd'] |
|
32 | items = [l*size for l in 'abcd'] | |
33 |
|
33 | |||
34 | out = text.columnize(items, displaywidth=80) |
|
34 | out = text.columnize(items, displaywidth=80) | |
35 |
|
|
35 | assert out == "aaaaa bbbbb ccccc ddddd\n" | |
36 | out = text.columnize(items, displaywidth=25) |
|
36 | out = text.columnize(items, displaywidth=25) | |
37 |
|
|
37 | assert out == "aaaaa ccccc\nbbbbb ddddd\n" | |
38 | out = text.columnize(items, displaywidth=12) |
|
38 | out = text.columnize(items, displaywidth=12) | |
39 |
|
|
39 | assert out == "aaaaa ccccc\nbbbbb ddddd\n" | |
40 | out = text.columnize(items, displaywidth=10) |
|
40 | out = text.columnize(items, displaywidth=10) | |
41 |
|
|
41 | assert out == "aaaaa\nbbbbb\nccccc\nddddd\n" | |
42 |
|
42 | |||
43 | out = text.columnize(items, row_first=True, displaywidth=80) |
|
43 | out = text.columnize(items, row_first=True, displaywidth=80) | |
44 |
|
|
44 | assert out == "aaaaa bbbbb ccccc ddddd\n" | |
45 | out = text.columnize(items, row_first=True, displaywidth=25) |
|
45 | out = text.columnize(items, row_first=True, displaywidth=25) | |
46 |
|
|
46 | assert out == "aaaaa bbbbb\nccccc ddddd\n" | |
47 | out = text.columnize(items, row_first=True, displaywidth=12) |
|
47 | out = text.columnize(items, row_first=True, displaywidth=12) | |
48 |
|
|
48 | assert out == "aaaaa bbbbb\nccccc ddddd\n" | |
49 | out = text.columnize(items, row_first=True, displaywidth=10) |
|
49 | out = text.columnize(items, row_first=True, displaywidth=10) | |
50 |
|
|
50 | assert out == "aaaaa\nbbbbb\nccccc\nddddd\n" | |
51 |
|
51 | |||
52 | out = text.columnize(items, displaywidth=40, spread=True) |
|
52 | out = text.columnize(items, displaywidth=40, spread=True) | |
53 |
|
|
53 | assert out == "aaaaa bbbbb ccccc ddddd\n" | |
54 | out = text.columnize(items, displaywidth=20, spread=True) |
|
54 | out = text.columnize(items, displaywidth=20, spread=True) | |
55 |
|
|
55 | assert out == "aaaaa ccccc\nbbbbb ddddd\n" | |
56 | out = text.columnize(items, displaywidth=12, spread=True) |
|
56 | out = text.columnize(items, displaywidth=12, spread=True) | |
57 |
|
|
57 | assert out == "aaaaa ccccc\nbbbbb ddddd\n" | |
58 | out = text.columnize(items, displaywidth=10, spread=True) |
|
58 | out = text.columnize(items, displaywidth=10, spread=True) | |
59 |
|
|
59 | assert out == "aaaaa\nbbbbb\nccccc\nddddd\n" | |
60 |
|
60 | |||
61 |
|
61 | |||
62 | def test_columnize_random(): |
|
62 | def test_columnize_random(): | |
@@ -77,38 +77,43 b' def test_columnize_random():' | |||||
77 | print("size of each element :\n %s" % rand_len) |
|
77 | print("size of each element :\n %s" % rand_len) | |
78 | assert False, "row_first={0}".format(row_first) |
|
78 | assert False, "row_first={0}".format(row_first) | |
79 |
|
79 | |||
|
80 | ||||
|
81 | # TODO: pytest mark.parametrize once nose removed. | |||
80 | def test_columnize_medium(): |
|
82 | def test_columnize_medium(): | |
81 | """Test with inputs than shouldn't be wider than 80""" |
|
83 | """Test with inputs than shouldn't be wider than 80""" | |
82 | size = 40 |
|
84 | size = 40 | |
83 | items = [l*size for l in 'abc'] |
|
85 | items = [l*size for l in 'abc'] | |
84 | for row_first in [True, False]: |
|
86 | for row_first in [True, False]: | |
85 | out = text.columnize(items, row_first=row_first, displaywidth=80) |
|
87 | out = text.columnize(items, row_first=row_first, displaywidth=80) | |
86 |
|
|
88 | assert out == "\n".join(items + [""]), "row_first={0}".format(row_first) | |
|
89 | ||||
87 |
|
90 | |||
|
91 | # TODO: pytest mark.parametrize once nose removed. | |||
88 | def test_columnize_long(): |
|
92 | def test_columnize_long(): | |
89 | """Test columnize with inputs longer than the display window""" |
|
93 | """Test columnize with inputs longer than the display window""" | |
90 | size = 11 |
|
94 | size = 11 | |
91 | items = [l*size for l in 'abc'] |
|
95 | items = [l*size for l in 'abc'] | |
92 | for row_first in [True, False]: |
|
96 | for row_first in [True, False]: | |
93 | out = text.columnize(items, row_first=row_first, displaywidth=size-1) |
|
97 | out = text.columnize(items, row_first=row_first, displaywidth=size - 1) | |
94 |
|
|
98 | assert out == "\n".join(items + [""]), "row_first={0}".format(row_first) | |
|
99 | ||||
95 |
|
100 | |||
96 | def eval_formatter_check(f): |
|
101 | def eval_formatter_check(f): | |
97 | ns = dict(n=12, pi=math.pi, stuff='hello there', os=os, u=u"café", b="café") |
|
102 | ns = dict(n=12, pi=math.pi, stuff='hello there', os=os, u=u"café", b="café") | |
98 | s = f.format("{n} {n//4} {stuff.split()[0]}", **ns) |
|
103 | s = f.format("{n} {n//4} {stuff.split()[0]}", **ns) | |
99 |
|
|
104 | assert s == "12 3 hello" | |
100 | s = f.format(' '.join(['{n//%i}'%i for i in range(1,8)]), **ns) |
|
105 | s = f.format(' '.join(['{n//%i}'%i for i in range(1,8)]), **ns) | |
101 |
|
|
106 | assert s == "12 6 4 3 2 2 1" | |
102 | s = f.format('{[n//i for i in range(1,8)]}', **ns) |
|
107 | s = f.format('{[n//i for i in range(1,8)]}', **ns) | |
103 |
|
|
108 | assert s == "[12, 6, 4, 3, 2, 2, 1]" | |
104 | s = f.format("{stuff!s}", **ns) |
|
109 | s = f.format("{stuff!s}", **ns) | |
105 |
|
|
110 | assert s == ns["stuff"] | |
106 | s = f.format("{stuff!r}", **ns) |
|
111 | s = f.format("{stuff!r}", **ns) | |
107 |
|
|
112 | assert s == repr(ns["stuff"]) | |
108 |
|
113 | |||
109 | # Check with unicode: |
|
114 | # Check with unicode: | |
110 | s = f.format("{u}", **ns) |
|
115 | s = f.format("{u}", **ns) | |
111 |
|
|
116 | assert s == ns["u"] | |
112 | # This decodes in a platform dependent manner, but it shouldn't error out |
|
117 | # This decodes in a platform dependent manner, but it shouldn't error out | |
113 | s = f.format("{b}", **ns) |
|
118 | s = f.format("{b}", **ns) | |
114 |
|
119 | |||
@@ -117,11 +122,11 b' def eval_formatter_check(f):' | |||||
117 | def eval_formatter_slicing_check(f): |
|
122 | def eval_formatter_slicing_check(f): | |
118 | ns = dict(n=12, pi=math.pi, stuff='hello there', os=os) |
|
123 | ns = dict(n=12, pi=math.pi, stuff='hello there', os=os) | |
119 | s = f.format(" {stuff.split()[:]} ", **ns) |
|
124 | s = f.format(" {stuff.split()[:]} ", **ns) | |
120 |
|
|
125 | assert s == " ['hello', 'there'] " | |
121 | s = f.format(" {stuff.split()[::-1]} ", **ns) |
|
126 | s = f.format(" {stuff.split()[::-1]} ", **ns) | |
122 |
|
|
127 | assert s == " ['there', 'hello'] " | |
123 | s = f.format("{stuff[::2]}", **ns) |
|
128 | s = f.format("{stuff[::2]}", **ns) | |
124 |
|
|
129 | assert s == ns["stuff"][::2] | |
125 |
|
130 | |||
126 | nt.assert_raises(SyntaxError, f.format, "{n:x}", **ns) |
|
131 | nt.assert_raises(SyntaxError, f.format, "{n:x}", **ns) | |
127 |
|
132 | |||
@@ -129,13 +134,13 b' def eval_formatter_no_slicing_check(f):' | |||||
129 | ns = dict(n=12, pi=math.pi, stuff='hello there', os=os) |
|
134 | ns = dict(n=12, pi=math.pi, stuff='hello there', os=os) | |
130 |
|
135 | |||
131 | s = f.format('{n:x} {pi**2:+f}', **ns) |
|
136 | s = f.format('{n:x} {pi**2:+f}', **ns) | |
132 |
|
|
137 | assert s == "c +9.869604" | |
133 |
|
138 | |||
134 |
s = f.format( |
|
139 | s = f.format("{stuff[slice(1,4)]}", **ns) | |
135 |
|
|
140 | assert s == "ell" | |
136 |
|
141 | |||
137 | s = f.format("{a[:]}", a=[1, 2]) |
|
142 | s = f.format("{a[:]}", a=[1, 2]) | |
138 |
|
|
143 | assert s == "[1, 2]" | |
139 |
|
144 | |||
140 | def test_eval_formatter(): |
|
145 | def test_eval_formatter(): | |
141 | f = text.EvalFormatter() |
|
146 | f = text.EvalFormatter() | |
@@ -154,29 +159,16 b' def test_dollar_formatter():' | |||||
154 |
|
159 | |||
155 | ns = dict(n=12, pi=math.pi, stuff='hello there', os=os) |
|
160 | ns = dict(n=12, pi=math.pi, stuff='hello there', os=os) | |
156 | s = f.format("$n", **ns) |
|
161 | s = f.format("$n", **ns) | |
157 |
|
|
162 | assert s == "12" | |
158 | s = f.format("$n.real", **ns) |
|
163 | s = f.format("$n.real", **ns) | |
159 |
|
|
164 | assert s == "12" | |
160 | s = f.format("$n/{stuff[:5]}", **ns) |
|
165 | s = f.format("$n/{stuff[:5]}", **ns) | |
161 |
|
|
166 | assert s == "12/hello" | |
162 | s = f.format("$n $$HOME", **ns) |
|
167 | s = f.format("$n $$HOME", **ns) | |
163 |
|
|
168 | assert s == "12 $HOME" | |
164 | s = f.format("${foo}", foo="HOME") |
|
169 | s = f.format("${foo}", foo="HOME") | |
165 |
|
|
170 | assert s == "$HOME" | |
166 |
|
||||
167 |
|
||||
168 | def test_long_substr(): |
|
|||
169 | data = ['hi'] |
|
|||
170 | nt.assert_equal(text.long_substr(data), 'hi') |
|
|||
171 |
|
||||
172 |
|
||||
173 | def test_long_substr2(): |
|
|||
174 | data = ['abc', 'abd', 'abf', 'ab'] |
|
|||
175 | nt.assert_equal(text.long_substr(data), 'ab') |
|
|||
176 |
|
171 | |||
177 | def test_long_substr_empty(): |
|
|||
178 | data = [] |
|
|||
179 | nt.assert_equal(text.long_substr(data), '') |
|
|||
180 |
|
172 | |||
181 | def test_strip_email(): |
|
173 | def test_strip_email(): | |
182 | src = """\ |
|
174 | src = """\ | |
@@ -189,25 +181,25 b' def test_strip_email():' | |||||
189 | ... return x+1 |
|
181 | ... return x+1 | |
190 | ... |
|
182 | ... | |
191 | >>> zz = f(2.5)""" |
|
183 | >>> zz = f(2.5)""" | |
192 |
|
|
184 | assert text.strip_email_quotes(src) == cln | |
193 |
|
185 | |||
194 |
|
186 | |||
195 | def test_strip_email2(): |
|
187 | def test_strip_email2(): | |
196 | src = '> > > list()' |
|
188 | src = '> > > list()' | |
197 | cln = 'list()' |
|
189 | cln = 'list()' | |
198 |
|
|
190 | assert text.strip_email_quotes(src) == cln | |
199 |
|
191 | |||
200 | def test_LSString(): |
|
192 | def test_LSString(): | |
201 | lss = text.LSString("abc\ndef") |
|
193 | lss = text.LSString("abc\ndef") | |
202 |
|
|
194 | assert lss.l == ["abc", "def"] | |
203 |
|
|
195 | assert lss.s == "abc def" | |
204 | lss = text.LSString(os.getcwd()) |
|
196 | lss = text.LSString(os.getcwd()) | |
205 | nt.assert_is_instance(lss.p[0], Path) |
|
197 | nt.assert_is_instance(lss.p[0], Path) | |
206 |
|
198 | |||
207 | def test_SList(): |
|
199 | def test_SList(): | |
208 |
sl = text.SList([ |
|
200 | sl = text.SList(["a 11", "b 1", "a 2"]) | |
209 |
|
|
201 | assert sl.n == "a 11\nb 1\na 2" | |
210 |
|
|
202 | assert sl.s == "a 11 b 1 a 2" | |
211 |
|
|
203 | assert sl.grep(lambda x: x.startswith("a")) == text.SList(["a 11", "a 2"]) | |
212 |
|
|
204 | assert sl.fields(0) == text.SList(["a", "b", "a"]) | |
213 |
|
|
205 | assert sl.sort(field=1, nums=True) == text.SList(["b 1", "a 2", "a 11"]) |
@@ -10,6 +10,7 b' Inheritance diagram:' | |||||
10 |
|
10 | |||
11 | import os |
|
11 | import os | |
12 | import re |
|
12 | import re | |
|
13 | import string | |||
13 | import sys |
|
14 | import sys | |
14 | import textwrap |
|
15 | import textwrap | |
15 | from string import Formatter |
|
16 | from string import Formatter | |
@@ -252,7 +253,6 b' def indent(instr,nspaces=4, ntabs=0, flatten=False):' | |||||
252 |
|
253 | |||
253 | Parameters |
|
254 | Parameters | |
254 | ---------- |
|
255 | ---------- | |
255 |
|
||||
256 | instr : basestring |
|
256 | instr : basestring | |
257 | The string to be indented. |
|
257 | The string to be indented. | |
258 | nspaces : int (default: 4) |
|
258 | nspaces : int (default: 4) | |
@@ -266,7 +266,6 b' def indent(instr,nspaces=4, ntabs=0, flatten=False):' | |||||
266 |
|
266 | |||
267 | Returns |
|
267 | Returns | |
268 | ------- |
|
268 | ------- | |
269 |
|
||||
270 | str|unicode : string indented by ntabs and nspaces. |
|
269 | str|unicode : string indented by ntabs and nspaces. | |
271 |
|
270 | |||
272 | """ |
|
271 | """ | |
@@ -390,7 +389,6 b' def wrap_paragraphs(text, ncols=80):' | |||||
390 |
|
389 | |||
391 | Returns |
|
390 | Returns | |
392 | ------- |
|
391 | ------- | |
393 |
|
||||
394 | list of complete paragraphs, wrapped to fill `ncols` columns. |
|
392 | list of complete paragraphs, wrapped to fill `ncols` columns. | |
395 | """ |
|
393 | """ | |
396 | paragraph_re = re.compile(r'\n(\s*\n)+', re.MULTILINE) |
|
394 | paragraph_re = re.compile(r'\n(\s*\n)+', re.MULTILINE) | |
@@ -408,22 +406,6 b' def wrap_paragraphs(text, ncols=80):' | |||||
408 | return out_ps |
|
406 | return out_ps | |
409 |
|
407 | |||
410 |
|
408 | |||
411 | def long_substr(data): |
|
|||
412 | """Return the longest common substring in a list of strings. |
|
|||
413 |
|
||||
414 | Credit: http://stackoverflow.com/questions/2892931/longest-common-substring-from-more-than-two-strings-python |
|
|||
415 | """ |
|
|||
416 | substr = '' |
|
|||
417 | if len(data) > 1 and len(data[0]) > 0: |
|
|||
418 | for i in range(len(data[0])): |
|
|||
419 | for j in range(len(data[0])-i+1): |
|
|||
420 | if j > len(substr) and all(data[0][i:i+j] in x for x in data): |
|
|||
421 | substr = data[0][i:i+j] |
|
|||
422 | elif len(data) == 1: |
|
|||
423 | substr = data[0] |
|
|||
424 | return substr |
|
|||
425 |
|
||||
426 |
|
||||
427 | def strip_email_quotes(text): |
|
409 | def strip_email_quotes(text): | |
428 | """Strip leading email quotation characters ('>'). |
|
410 | """Strip leading email quotation characters ('>'). | |
429 |
|
411 | |||
@@ -457,20 +439,23 b' def strip_email_quotes(text):' | |||||
457 | Out[5]: '> > text\\n> > more\\nlast different' |
|
439 | Out[5]: '> > text\\n> > more\\nlast different' | |
458 | """ |
|
440 | """ | |
459 | lines = text.splitlines() |
|
441 | lines = text.splitlines() | |
460 | matches = set() |
|
442 | strip_len = 0 | |
461 | for line in lines: |
|
443 | ||
462 | prefix = re.match(r'^(\s*>[ >]*)', line) |
|
444 | for characters in zip(*lines): | |
463 | if prefix: |
|
445 | # Check if all characters in this position are the same | |
464 | matches.add(prefix.group(1)) |
|
446 | if len(set(characters)) > 1: | |
465 | else: |
|
|||
466 | break |
|
447 | break | |
|
448 | prefix_char = characters[0] | |||
|
449 | ||||
|
450 | if prefix_char in string.whitespace or prefix_char == ">": | |||
|
451 | strip_len += 1 | |||
467 | else: |
|
452 | else: | |
468 | prefix = long_substr(list(matches)) |
|
453 | break | |
469 | if prefix: |
|
454 | ||
470 | strip = len(prefix) |
|
455 | text = "\n".join([ln[strip_len:] for ln in lines]) | |
471 | text = '\n'.join([ ln[strip:] for ln in lines]) |
|
|||
472 | return text |
|
456 | return text | |
473 |
|
457 | |||
|
458 | ||||
474 | def strip_ansi(source): |
|
459 | def strip_ansi(source): | |
475 | """ |
|
460 | """ | |
476 | Remove ansi escape codes from text. |
|
461 | Remove ansi escape codes from text. | |
@@ -651,7 +636,6 b' def compute_item_matrix(items, row_first=False, empty=None, *args, **kwargs) :' | |||||
651 |
|
636 | |||
652 | Parameters |
|
637 | Parameters | |
653 | ---------- |
|
638 | ---------- | |
654 |
|
||||
655 | items |
|
639 | items | |
656 | list of strings to columize |
|
640 | list of strings to columize | |
657 | row_first : (default False) |
|
641 | row_first : (default False) | |
@@ -666,14 +650,11 b' def compute_item_matrix(items, row_first=False, empty=None, *args, **kwargs) :' | |||||
666 |
|
650 | |||
667 | Returns |
|
651 | Returns | |
668 | ------- |
|
652 | ------- | |
669 |
|
||||
670 | strings_matrix |
|
653 | strings_matrix | |
671 |
|
||||
672 | nested list of string, the outer most list contains as many list as |
|
654 | nested list of string, the outer most list contains as many list as | |
673 | rows, the innermost lists have each as many element as columns. If the |
|
655 | rows, the innermost lists have each as many element as columns. If the | |
674 | total number of elements in `items` does not equal the product of |
|
656 | total number of elements in `items` does not equal the product of | |
675 | rows*columns, the last element of some lists are filled with `None`. |
|
657 | rows*columns, the last element of some lists are filled with `None`. | |
676 |
|
||||
677 | dict_info |
|
658 | dict_info | |
678 | some info to make columnize easier: |
|
659 | some info to make columnize easier: | |
679 |
|
660 | |||
@@ -713,14 +694,11 b" def columnize(items, row_first=False, separator=' ', displaywidth=80, spread=Fa" | |||||
713 | ---------- |
|
694 | ---------- | |
714 | items : sequence of strings |
|
695 | items : sequence of strings | |
715 | The strings to process. |
|
696 | The strings to process. | |
716 |
|
||||
717 | row_first : (default False) |
|
697 | row_first : (default False) | |
718 | Whether to compute columns for a row-first matrix instead of |
|
698 | Whether to compute columns for a row-first matrix instead of | |
719 | column-first (default). |
|
699 | column-first (default). | |
720 |
|
||||
721 | separator : str, optional [default is two spaces] |
|
700 | separator : str, optional [default is two spaces] | |
722 | The string that separates columns. |
|
701 | The string that separates columns. | |
723 |
|
||||
724 | displaywidth : int, optional [default is 80] |
|
702 | displaywidth : int, optional [default is 80] | |
725 | Width of the display in number of characters. |
|
703 | Width of the display in number of characters. | |
726 |
|
704 |
@@ -23,6 +23,11 b' import time' | |||||
23 | # If possible (Unix), use the resource module instead of time.clock() |
|
23 | # If possible (Unix), use the resource module instead of time.clock() | |
24 | try: |
|
24 | try: | |
25 | import resource |
|
25 | import resource | |
|
26 | except ImportError: | |||
|
27 | resource = None | |||
|
28 | ||||
|
29 | # Some implementations (like jyputerlite) don't have getrusage | |||
|
30 | if resource is not None and hasattr(resource, "getrusage"): | |||
26 | def clocku(): |
|
31 | def clocku(): | |
27 | """clocku() -> floating point number |
|
32 | """clocku() -> floating point number | |
28 |
|
33 | |||
@@ -56,7 +61,9 b' try:' | |||||
56 |
|
61 | |||
57 | Similar to clock(), but return a tuple of user/system times.""" |
|
62 | Similar to clock(), but return a tuple of user/system times.""" | |
58 | return resource.getrusage(resource.RUSAGE_SELF)[:2] |
|
63 | return resource.getrusage(resource.RUSAGE_SELF)[:2] | |
59 | except ImportError: |
|
64 | ||
|
65 | ||||
|
66 | else: | |||
60 | # There is no distinction of user/system time under windows, so we just use |
|
67 | # There is no distinction of user/system time under windows, so we just use | |
61 | # time.perff_counter() for everything... |
|
68 | # time.perff_counter() for everything... | |
62 | clocku = clocks = clock = time.perf_counter |
|
69 | clocku = clocks = clock = time.perf_counter |
@@ -28,7 +28,6 b' def line_at_cursor(cell, cursor_pos=0):' | |||||
28 |
|
28 | |||
29 | Parameters |
|
29 | Parameters | |
30 | ---------- |
|
30 | ---------- | |
31 |
|
||||
32 | cell: str |
|
31 | cell : str | |
33 | multiline block of text |
|
32 | multiline block of text | |
34 | cursor_pos: integer |
|
33 | cursor_pos : integer | |
@@ -36,7 +35,6 b' def line_at_cursor(cell, cursor_pos=0):' | |||||
36 |
|
35 | |||
37 | Returns |
|
36 | Returns | |
38 | ------- |
|
37 | ------- | |
39 |
|
||||
40 | (line, offset): (string, integer) |
|
38 | (line, offset): (string, integer) | |
41 | The line with the current cursor, and the character offset of the start of the line. |
|
39 | The line with the current cursor, and the character offset of the start of the line. | |
42 | """ |
|
40 | """ | |
@@ -66,7 +64,6 b' def token_at_cursor(cell, cursor_pos=0):' | |||||
66 |
|
64 | |||
67 | Parameters |
|
65 | Parameters | |
68 | ---------- |
|
66 | ---------- | |
69 |
|
||||
70 | cell : unicode |
|
67 | cell : unicode | |
71 | A block of Python code |
|
68 | A block of Python code | |
72 | cursor_pos : int |
|
69 | cursor_pos : int |
@@ -4,8 +4,8 b'' | |||||
4 | .. image:: https://img.shields.io/pypi/v/IPython.svg |
|
4 | .. image:: https://img.shields.io/pypi/v/IPython.svg | |
5 | :target: https://pypi.python.org/pypi/ipython |
|
5 | :target: https://pypi.python.org/pypi/ipython | |
6 |
|
6 | |||
7 | .. image:: https://img.shields.io/travis/ipython/ipython.svg |
|
7 | .. image:: https://github.com/ipython/ipython/actions/workflows/test.yml/badge.svg | |
8 |
:target: https:// |
|
8 | :target: https://github.com/ipython/ipython/actions/workflows/test.yml) | |
9 |
|
9 | |||
10 | .. image:: https://www.codetriage.com/ipython/ipython/badges/users.svg |
|
10 | .. image:: https://www.codetriage.com/ipython/ipython/badges/users.svg | |
11 | :target: https://www.codetriage.com/ipython/ipython/ |
|
11 | :target: https://www.codetriage.com/ipython/ipython/ |
@@ -1,4 +1,4 b'' | |||||
1 | -e . |
|
1 | -e .[test] | |
2 | ipykernel |
|
2 | ipykernel | |
3 | setuptools>=18.5 |
|
3 | setuptools>=18.5 | |
4 | sphinx |
|
4 | sphinx | |
@@ -6,3 +6,4 b' sphinx-rtd-theme' | |||||
6 | docrepr |
|
6 | docrepr | |
7 | matplotlib |
|
7 | matplotlib | |
8 | stack_data |
|
8 | stack_data | |
|
9 | pytest |
@@ -18,7 +18,7 b'' | |||||
18 | import sys, os |
|
18 | import sys, os | |
19 | from pathlib import Path |
|
19 | from pathlib import Path | |
20 |
|
20 | |||
21 | # http://read-the-docs.readthedocs.io/en/latest/faq.html |
|
21 | # https://read-the-docs.readthedocs.io/en/latest/faq.html | |
22 | ON_RTD = os.environ.get('READTHEDOCS', None) == 'True' |
|
22 | ON_RTD = os.environ.get('READTHEDOCS', None) == 'True' | |
23 |
|
23 | |||
24 | if ON_RTD: |
|
24 | if ON_RTD: |
@@ -151,7 +151,7 b' Complete Example' | |||||
151 |
|
151 | |||
152 | Here is a full example of a magic package. You can distribute magics using |
|
152 | Here is a full example of a magic package. You can distribute magics using | |
153 | setuptools, distutils, or any other distribution tools like `flit |
|
153 | setuptools, distutils, or any other distribution tools like `flit | |
154 | <http://flit.readthedocs.io>`_ for pure Python packages. |
|
154 | <https://flit.readthedocs.io>`_ for pure Python packages. | |
155 |
|
155 | |||
156 | When distributing magics as part of a package, recommended best practice is to |
|
156 | When distributing magics as part of a package, recommended best practice is to | |
157 | execute the registration inside the `load_ipython_extension` as demonstrated in |
|
157 | execute the registration inside the `load_ipython_extension` as demonstrated in |
@@ -7,9 +7,9 b' loop, so you can use both a GUI and an interactive prompt together. IPython' | |||||
7 | supports a number of common GUI toolkits, but from IPython 3.0, it is possible |
|
7 | supports a number of common GUI toolkits, but from IPython 3.0, it is possible | |
8 | to integrate other event loops without modifying IPython itself. |
|
8 | to integrate other event loops without modifying IPython itself. | |
9 |
|
9 | |||
10 |
Supported event loops include ``qt4``, ``qt5``, ``gtk2``, ``gtk3``, `` |
|
10 | Supported event loops include ``qt4``, ``qt5``, ``gtk2``, ``gtk3``, ``gtk4``, | |
11 |
``osx`` and ``tk``. Make sure the event loop you specify matches the |
|
11 | ``wx``, ``osx`` and ``tk``. Make sure the event loop you specify matches the | |
12 | toolkit used by your own code. |
|
12 | GUI toolkit used by your own code. | |
13 |
|
13 | |||
14 | To make IPython GUI event loop integration occur automatically at every |
|
14 | To make IPython GUI event loop integration occur automatically at every | |
15 | startup, set the ``c.InteractiveShellApp.gui`` configuration key in your |
|
15 | startup, set the ``c.InteractiveShellApp.gui`` configuration key in your |
@@ -24,14 +24,59 b' returns a list of objects which are possible keys in a subscript expression' | |||||
24 | Rich display |
|
24 | Rich display | |
25 | ============ |
|
25 | ============ | |
26 |
|
26 | |||
27 | The notebook and the Qt console can display richer representations of objects. |
|
27 | Custom methods | |
28 | To use this, you can define any of a number of ``_repr_*_()`` methods. Note that |
|
28 | ---------------------- | |
29 | these are surrounded by single, not double underscores. |
|
29 | IPython can display richer representations of objects. | |
30 |
|
30 | To do this, you can define ``_ipython_display_()``, or any of a number of | ||
31 | Both the notebook and the Qt console can display ``svg``, ``png`` and ``jpeg`` |
|
31 | ``_repr_*_()`` methods. | |
32 | representations. The notebook can also display ``html``, ``javascript``, |
|
32 | Note that these are surrounded by single, not double underscores. | |
33 | ``markdown`` and ``latex``. If the methods don't exist, or return ``None``, it |
|
33 | ||
34 | falls back to a standard ``repr()``. |
|
34 | .. list-table:: Supported ``_repr_*_`` methods | |
|
35 | :widths: 20 15 15 15 | |||
|
36 | :header-rows: 1 | |||
|
37 | ||||
|
38 | * - Format | |||
|
39 | - REPL | |||
|
40 | - Notebook | |||
|
41 | - Qt Console | |||
|
42 | * - ``_repr_pretty_`` | |||
|
43 | - yes | |||
|
44 | - yes | |||
|
45 | - yes | |||
|
46 | * - ``_repr_svg_`` | |||
|
47 | - no | |||
|
48 | - yes | |||
|
49 | - yes | |||
|
50 | * - ``_repr_png_`` | |||
|
51 | - no | |||
|
52 | - yes | |||
|
53 | - yes | |||
|
54 | * - ``_repr_jpeg_`` | |||
|
55 | - no | |||
|
56 | - yes | |||
|
57 | - yes | |||
|
58 | * - ``_repr_html_`` | |||
|
59 | - no | |||
|
60 | - yes | |||
|
61 | - no | |||
|
62 | * - ``_repr_javascript_`` | |||
|
63 | - no | |||
|
64 | - yes | |||
|
65 | - no | |||
|
66 | * - ``_repr_markdown_`` | |||
|
67 | - no | |||
|
68 | - yes | |||
|
69 | - no | |||
|
70 | * - ``_repr_latex_`` | |||
|
71 | - no | |||
|
72 | - yes | |||
|
73 | - no | |||
|
74 | * - ``_repr_mimebundle_`` | |||
|
75 | - no | |||
|
76 | - ? | |||
|
77 | - ? | |||
|
78 | ||||
|
79 | If the methods don't exist, or return ``None``, the standard ``repr()`` is used. | |||
35 |
|
80 | |||
36 | For example:: |
|
81 | For example:: | |
37 |
|
82 | |||
@@ -42,43 +87,61 b' For example::' | |||||
42 | def _repr_html_(self): |
|
87 | def _repr_html_(self): | |
43 | return "<h1>" + self.text + "</h1>" |
|
88 | return "<h1>" + self.text + "</h1>" | |
44 |
|
89 | |||
45 | We often want to provide frontends with guidance on how to display the data. To |
|
|||
46 | support this, ``_repr_*_()`` methods can also return a ``(data, metadata)`` |
|
|||
47 | tuple where ``metadata`` is a dictionary containing arbitrary key-value pairs for |
|
|||
48 | the frontend to interpret. An example use case is ``_repr_jpeg_()``, which can |
|
|||
49 | be set to return a jpeg image and a ``{'height': 400, 'width': 600}`` dictionary |
|
|||
50 | to inform the frontend how to size the image. |
|
|||
51 |
|
|
90 | ||
52 | There are also two more powerful display methods: |
|
91 | Special methods | |
|
92 | ^^^^^^^^^^^^^^^ | |||
|
93 | ||||
|
94 | Pretty printing | |||
|
95 | """"""""""""""" | |||
|
96 | ||||
|
97 | To customize how your object is pretty-printed, add a ``_repr_pretty_`` method | |||
|
98 | to the class. | |||
|
99 | The method should accept a pretty printer, and a boolean that indicates whether | |||
|
100 | the printer detected a cycle. | |||
|
101 | The method should act on the printer to produce your customized pretty output. | |||
|
102 | Here is an example:: | |||
|
103 | ||||
|
104 | class MyObject(object): | |||
|
105 | ||||
|
106 | def _repr_pretty_(self, p, cycle): | |||
|
107 | if cycle: | |||
|
108 | p.text('MyObject(...)') | |||
|
109 | else: | |||
|
110 | p.text('MyObject[...]') | |||
|
111 | ||||
|
112 | For details on how to use the pretty printer, see :py:mod:`IPython.lib.pretty`. | |||
|
113 | ||||
|
114 | More powerful methods | |||
|
115 | """"""""""""""""""""" | |||
53 |
|
116 | |||
54 | .. class:: MyObject |
|
117 | .. class:: MyObject | |
55 |
|
118 | |||
56 | .. method:: _repr_mimebundle_(include=None, exclude=None) |
|
119 | .. method:: _repr_mimebundle_(include=None, exclude=None) | |
57 |
|
120 | |||
58 | Should return a dictionary of multiple formats, keyed by mimetype, or a tuple |
|
121 | Should return a dictionary of multiple formats, keyed by mimetype, or a tuple | |
59 |
of two dictionaries: *data, metadata* |
|
122 | of two dictionaries: *data, metadata* (see :ref:`Metadata`). | |
60 | ``_repr_*_`` methods are ignored. The method should take keyword arguments |
|
123 | If this returns something, other ``_repr_*_`` methods are ignored. | |
61 | ``include`` and ``exclude``, though it is not required to respect them. |
|
124 | The method should take keyword arguments ``include`` and ``exclude``, though | |
|
125 | it is not required to respect them. | |||
62 |
|
126 | |||
63 | .. method:: _ipython_display_() |
|
127 | .. method:: _ipython_display_() | |
64 |
|
128 | |||
65 | Displays the object as a side effect; the return value is ignored. If this |
|
129 | Displays the object as a side effect; the return value is ignored. If this | |
66 | is defined, all other display methods are ignored. |
|
130 | is defined, all other display methods are ignored. | |
|
131 | This method is ignored in the REPL. | |||
67 |
|
132 | |||
68 | To customize how the REPL pretty-prints your object, add a `_repr_pretty_` |
|
|||
69 | method to the class. The method should accept a pretty printer, and a boolean |
|
|||
70 | that indicates whether the printer detected a cycle. The method should act on |
|
|||
71 | the printer to produce your customized pretty output. Here is an example:: |
|
|||
72 |
|
133 | |||
73 | class MyObject(object): |
|
134 | Metadata | |
|
135 | ^^^^^^^^ | |||
|
136 | ||||
|
137 | We often want to provide frontends with guidance on how to display the data. To | |||
|
138 | support this, ``_repr_*_()`` methods (except `_repr_pretty_``?) can also return a ``(data, metadata)`` | |||
|
139 | tuple where ``metadata`` is a dictionary containing arbitrary key-value pairs for | |||
|
140 | the frontend to interpret. An example use case is ``_repr_jpeg_()``, which can | |||
|
141 | be set to return a jpeg image and a ``{'height': 400, 'width': 600}`` dictionary | |||
|
142 | to inform the frontend how to size the image. | |||
74 |
|
143 | |||
75 | def _repr_pretty_(self, p, cycle): |
|
|||
76 | if cycle: |
|
|||
77 | p.text('MyObject(...)') |
|
|||
78 | else: |
|
|||
79 | p.text('MyObject[...]') |
|
|||
80 |
|
144 | |||
81 | For details, see :py:mod:`IPython.lib.pretty`. |
|
|||
82 |
|
145 | |||
83 | Formatters for third-party types |
|
146 | Formatters for third-party types | |
84 | -------------------------------- |
|
147 | -------------------------------- | |
@@ -103,7 +166,7 b' Rarely, you might want to display a custom traceback when reporting an' | |||||
103 | exception. To do this, define the custom traceback using |
|
166 | exception. To do this, define the custom traceback using | |
104 | `_render_traceback_(self)` method which returns a list of strings, one string |
|
167 | `_render_traceback_(self)` method which returns a list of strings, one string | |
105 | for each line of the traceback. For example, the `ipyparallel |
|
168 | for each line of the traceback. For example, the `ipyparallel | |
106 | <http://ipyparallel.readthedocs.io/>`__ a parallel computing framework for |
|
169 | <https://ipyparallel.readthedocs.io/>`__ a parallel computing framework for | |
107 | IPython, does this to display errors from multiple engines. |
|
170 | IPython, does this to display errors from multiple engines. | |
108 |
|
171 | |||
109 | Please be conservative in using this feature; by replacing the default traceback |
|
172 | Please be conservative in using this feature; by replacing the default traceback |
@@ -119,11 +119,6 b' which adds a directory called ``profile_<name>`` to your IPython directory. Then' | |||||
119 | you can load this profile by adding ``--profile=<name>`` to your command line |
|
119 | you can load this profile by adding ``--profile=<name>`` to your command line | |
120 | options. Profiles are supported by all IPython applications. |
|
120 | options. Profiles are supported by all IPython applications. | |
121 |
|
121 | |||
122 | IPython ships with some sample profiles in :file:`IPython/config/profile`. If |
|
|||
123 | you create profiles with the name of one of our shipped profiles, these config |
|
|||
124 | files will be copied over instead of starting with the automatically generated |
|
|||
125 | config files. |
|
|||
126 |
|
||||
127 | IPython extends the config loader for Python files so that you can inherit |
|
122 | IPython extends the config loader for Python files so that you can inherit | |
128 | config from another profile. To do this, use a line like this in your Python |
|
123 | config from another profile. To do this, use a line like this in your Python | |
129 | config file: |
|
124 | config file: |
@@ -5,4 +5,4 b' Connection Diagrams of The IPython ZMQ Cluster' | |||||
5 | ============================================== |
|
5 | ============================================== | |
6 |
|
6 | |||
7 | IPython parallel has moved to ipyparallel - |
|
7 | IPython parallel has moved to ipyparallel - | |
8 |
see :ref:`ipyparallel: |
|
8 | see :ref:`ipyparallel:/reference/connections.md` for the documentation. |
@@ -5,4 +5,4 b' Messaging for Parallel Computing' | |||||
5 | ================================ |
|
5 | ================================ | |
6 |
|
6 | |||
7 | IPython parallel has moved to ipyparallel - |
|
7 | IPython parallel has moved to ipyparallel - | |
8 |
see :ref:`ipyparallel: |
|
8 | see :ref:`ipyparallel:/reference/messages.md` for the documentation. |
@@ -7,7 +7,7 b' You can now re-use the kernel machinery in IPython to easily make new kernels.' | |||||
7 | This is useful for languages that have Python bindings, such as `Octave |
|
7 | This is useful for languages that have Python bindings, such as `Octave | |
8 | <http://www.gnu.org/software/octave/>`_ (via |
|
8 | <http://www.gnu.org/software/octave/>`_ (via | |
9 | `Oct2Py <http://blink1073.github.io/oct2py/>`_), or languages |
|
9 | `Oct2Py <http://blink1073.github.io/oct2py/>`_), or languages | |
10 | where the REPL can be controlled in a tty using `pexpect <http://pexpect.readthedocs.io/en/latest/>`_, |
|
10 | where the REPL can be controlled in a tty using `pexpect <https://pexpect.readthedocs.io/en/latest/>`_, | |
11 | such as bash. |
|
11 | such as bash. | |
12 |
|
12 | |||
13 | .. seealso:: |
|
13 | .. seealso:: |
@@ -57,7 +57,7 b' features:' | |||||
57 |
|
57 | |||
58 | The Command line interface inherits the above functionality and adds |
|
58 | The Command line interface inherits the above functionality and adds | |
59 |
|
59 | |||
60 | * real multi-line editing thanks to `prompt_toolkit <http://python-prompt-toolkit.readthedocs.io/en/stable/>`_. |
|
60 | * real multi-line editing thanks to `prompt_toolkit <https://python-prompt-toolkit.readthedocs.io/en/stable/>`_. | |
61 |
|
61 | |||
62 | * syntax highlighting as you type. |
|
62 | * syntax highlighting as you type. | |
63 |
|
63 | |||
@@ -69,7 +69,7 b' it allows:' | |||||
69 | * the object to create a rich display of Html, Images, Latex, Sound and |
|
69 | * the object to create a rich display of Html, Images, Latex, Sound and | |
70 | Video. |
|
70 | Video. | |
71 |
|
71 | |||
72 | * interactive widgets with the use of the `ipywidgets <http://ipywidgets.readthedocs.io/en/stable/>`_ package. |
|
72 | * interactive widgets with the use of the `ipywidgets <https://ipywidgets.readthedocs.io/en/stable/>`_ package. | |
73 |
|
73 | |||
74 |
|
74 | |||
75 | This documentation will walk you through most of the features of the IPython |
|
75 | This documentation will walk you through most of the features of the IPython | |
@@ -102,9 +102,9 b' repository <http://github.com/ipython/ipython>`_.' | |||||
102 |
|
102 | |||
103 | .. seealso:: |
|
103 | .. seealso:: | |
104 |
|
104 | |||
105 | `Jupyter documentation <http://jupyter.readthedocs.io/en/latest/>`__ |
|
105 | `Jupyter documentation <https://jupyter.readthedocs.io/en/latest/>`__ | |
106 | The Jupyter documentation provides information about the Notebook code and other Jupyter sub-projects. |
|
106 | The Jupyter documentation provides information about the Notebook code and other Jupyter sub-projects. | |
107 | `ipyparallel documentation <http://ipyparallel.readthedocs.io/en/latest/>`__ |
|
107 | `ipyparallel documentation <https://ipyparallel.readthedocs.io/en/latest/>`__ | |
108 | Formerly ``IPython.parallel``. |
|
108 | Formerly ``IPython.parallel``. | |
109 |
|
109 | |||
110 |
|
110 |
@@ -51,7 +51,7 b' for more help see' | |||||
51 |
|
51 | |||
52 | .. seealso:: |
|
52 | .. seealso:: | |
53 |
|
53 | |||
54 | `Installing Jupyter <http://jupyter.readthedocs.io/en/latest/install.html>`__ |
|
54 | `Installing Jupyter <https://jupyter.readthedocs.io/en/latest/install.html>`__ | |
55 | The Notebook, nbconvert, and many other former pieces of IPython are now |
|
55 | The Notebook, nbconvert, and many other former pieces of IPython are now | |
56 | part of Project Jupyter. |
|
56 | part of Project Jupyter. | |
57 |
|
57 |
@@ -29,4 +29,4 b' done some work in the classic Python REPL.' | |||||
29 | .. seealso:: |
|
29 | .. seealso:: | |
30 |
|
30 | |||
31 | `A Qt Console for Jupyter <https://jupyter.org/qtconsole/>`__ |
|
31 | `A Qt Console for Jupyter <https://jupyter.org/qtconsole/>`__ | |
32 | `The Jupyter Notebook <http://jupyter-notebook.readthedocs.io/en/latest/>`__ |
|
32 | `The Jupyter Notebook <https://jupyter-notebook.readthedocs.io/en/latest/>`__ |
@@ -3,20 +3,20 b' Python vs IPython' | |||||
3 | ================= |
|
3 | ================= | |
4 |
|
4 | |||
5 | This document is meant to highlight the main differences between the Python |
|
5 | This document is meant to highlight the main differences between the Python | |
6 | language and what are the specific construct you can do only in IPython. |
|
6 | language and what are the specific constructs you can do only in IPython. | |
7 |
|
7 | |||
8 | Unless expressed otherwise all of the construct you will see here will raise a |
|
8 | Unless expressed otherwise all of the constructs you will see here will raise a | |
9 | ``SyntaxError`` if run in a pure Python shell, or if executing in a Python |
|
9 | ``SyntaxError`` if run in a pure Python shell, or if executing in a Python | |
10 | script. |
|
10 | script. | |
11 |
|
11 | |||
12 |
Each of these features |
|
12 | Each of these features is described more in detail in the further parts of the documentation. | |
13 |
|
13 | |||
14 |
|
14 | |||
15 | Quick overview: |
|
15 | Quick overview: | |
16 | =============== |
|
16 | =============== | |
17 |
|
17 | |||
18 |
|
18 | |||
19 | All the following construct are valid IPython syntax: |
|
19 | All the following constructs are valid IPython syntax: | |
20 |
|
20 | |||
21 | .. code-block:: ipython |
|
21 | .. code-block:: ipython | |
22 |
|
22 | |||
@@ -46,9 +46,9 b' All the following construct are valid IPython syntax:' | |||||
46 | .. code-block:: ipython |
|
46 | .. code-block:: ipython | |
47 |
|
47 | |||
48 | In [1]: my_files = !ls ~/ |
|
48 | In [1]: my_files = !ls ~/ | |
49 | In [1]: for i,file in enumerate(my_file): |
|
49 | In [1]: for i, file in enumerate(my_files): | |
50 | ...: raw = !echo $file |
|
50 | ...: raw = !echo $file | |
51 |
...: !echo {file |
|
51 | ...: !echo {file[0].upper()} $raw | |
52 |
|
52 | |||
53 |
|
53 | |||
54 | .. code-block:: ipython |
|
54 | .. code-block:: ipython | |
@@ -58,8 +58,8 b' All the following construct are valid IPython syntax:' | |||||
58 | ...: print $months[0]; |
|
58 | ...: print $months[0]; | |
59 |
|
59 | |||
60 |
|
60 | |||
61 | Each of these construct is compiled by IPython into valid python code and will |
|
61 | Each of these constructs is compiled by IPython into valid python code and will | |
62 | do most of the time what you expect it will do. Let see each of these example |
|
62 | do most of the time what you expect it will do. Let's see each of these examples | |
63 | in more detail. |
|
63 | in more detail. | |
64 |
|
64 | |||
65 |
|
65 | |||
@@ -89,7 +89,7 b' shortcut to get help. A question mark alone will bring up the IPython help:' | |||||
89 | ------------- |
|
89 | ------------- | |
90 | ... |
|
90 | ... | |
91 |
|
91 | |||
92 |
A single question mark before |
|
92 | A single question mark before or after an object available in the current | |
93 | namespace will show help relative to this object: |
|
93 | namespace will show help relative to this object: | |
94 |
|
94 | |||
95 | .. code-block:: ipython |
|
95 | .. code-block:: ipython | |
@@ -127,7 +127,7 b' and if possible display the python source code of this object.' | |||||
127 |
|
127 | |||
128 |
|
128 | |||
129 | If you are looking for an object, the use of wildcards ``*`` in conjunction |
|
129 | If you are looking for an object, the use of wildcards ``*`` in conjunction | |
130 | with question mark will allow you to search current namespace for object with |
|
130 | with a question mark will allow you to search the current namespace for objects with | |
131 | matching names: |
|
131 | matching names: | |
132 |
|
132 | |||
133 | .. code-block:: ipython |
|
133 | .. code-block:: ipython | |
@@ -142,10 +142,10 b' Shell Assignment' | |||||
142 | ================ |
|
142 | ================ | |
143 |
|
143 | |||
144 |
|
144 | |||
145 |
When doing interactive computing it is common |
|
145 | When doing interactive computing it is a common need to access the underlying shell. | |
146 | This is doable through the use of the exclamation mark ``!`` (or bang). |
|
146 | This is doable through the use of the exclamation mark ``!`` (or bang). | |
147 |
|
147 | |||
148 | This allow to execute simple command when present in beginning of line: |
|
148 | This allows to execute simple commands when present in beginning of the line: | |
149 |
|
149 | |||
150 | .. code-block:: ipython |
|
150 | .. code-block:: ipython | |
151 |
|
151 | |||
@@ -167,7 +167,7 b' Or edit file:' | |||||
167 |
|
167 | |||
168 | The line after the bang can call any program installed in the underlying |
|
168 | The line after the bang can call any program installed in the underlying | |
169 | shell, and support variable expansion in the form of ``$variable`` or ``{variable}``. |
|
169 | shell, and support variable expansion in the form of ``$variable`` or ``{variable}``. | |
170 | The later form of expansion supports arbitrary python expression: |
|
170 | The later form of expansion supports arbitrary python expressions: | |
171 |
|
171 | |||
172 | .. code-block:: ipython |
|
172 | .. code-block:: ipython | |
173 |
|
173 | |||
@@ -176,25 +176,24 b' The later form of expansion supports arbitrary python expression:' | |||||
176 | In[2]: !mv $file {file.upper()} |
|
176 | In[2]: !mv $file {file.upper()} | |
177 |
|
177 | |||
178 |
|
178 | |||
179 |
The bang can also be present |
|
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 |
|
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 |
|
181 | standard output of the command after the bang will be split out into lines | |
182 | in a list-like object and assign to the left hand side. |
|
182 | in a list-like object and assigned to the left hand side. | |
183 |
|
183 | |||
184 | This allow you for example to put the list of files of the current working directory in a variable: |
|
184 | This allows you, for example, to put the list of files of the current working directory in a variable: | |
185 |
|
185 | |||
186 | .. code-block:: ipython |
|
186 | .. code-block:: ipython | |
187 |
|
187 | |||
188 | In[1]: my_files = !ls |
|
188 | In[1]: my_files = !ls | |
189 |
|
189 | |||
190 |
|
190 | |||
191 | You can combine the different possibilities in for loops, condition, functions...: |
|
191 | You can combine the different possibilities in for loops, conditions, functions...: | |
192 |
|
192 | |||
193 | .. code-block:: ipython |
|
193 | .. code-block:: ipython | |
194 |
|
194 | |||
195 | my_files = !ls ~/ |
|
195 | my_files = !ls ~/ | |
196 | b = "backup file" |
|
196 | for i, file in enumerate(my_files): | |
197 | for i,file in enumerate(my_file): |
|
|||
198 | raw = !echo $backup $file |
|
197 | raw = !echo $backup $file | |
199 | !cp $file {file.split('.')[0]+'.bak'} |
|
198 | !cp $file {file.split('.')[0] + '.bak'} | |
200 |
|
199 | |||
@@ -202,19 +201,19 b' You can combine the different possibilities in for loops, condition, functions..' | |||||
202 | Magics |
|
201 | Magics | |
203 | ------ |
|
202 | ------ | |
204 |
|
203 | |||
205 |
Magic |
|
204 | Magic functions (magics) are often present in the form of shell-like syntax, but they are | |
206 |
|
|
205 | python functions under the hood. The syntax and assignment possibilities are | |
207 | similar to the one with the bang (``!``) syntax, but with more flexibility and |
|
206 | similar to the one with the bang (``!``) syntax, but with more flexibility and | |
208 | power. Magic function start with a percent sign (``%``) or double percent (``%%``). |
|
207 | power. Magic functions start with a percent sign (``%``) or double percent signs (``%%``). | |
209 |
|
208 | |||
210 |
A magic call with a si |
|
209 | A magic call with a single percent sign will act only on one line: | |
211 |
|
210 | |||
212 | .. code-block:: ipython |
|
211 | .. code-block:: ipython | |
213 |
|
212 | |||
214 | In[1]: %xmode |
|
213 | In[1]: %xmode | |
215 | Exception reporting mode: Verbose |
|
214 | Exception reporting mode: Verbose | |
216 |
|
215 | |||
217 |
|
|
216 | Magics support assignment: | |
218 |
|
217 | |||
219 | .. code-block:: ipython |
|
218 | .. code-block:: ipython | |
220 |
|
219 | |||
@@ -224,7 +223,7 b' And support assignment:' | |||||
224 | In [2]: results |
|
223 | In [2]: results | |
225 | Out[2]: <TimeitResult : 1 loops, best of 1: 21.1 µs per loop> |
|
224 | Out[2]: <TimeitResult : 1 loops, best of 1: 21.1 µs per loop> | |
226 |
|
225 | |||
227 |
Magic with |
|
226 | Magics with double percent signs (``%%``) can spread over multiple lines, but they do not support assignments: | |
228 |
|
227 | |||
229 | .. code-block:: ipython |
|
228 | .. code-block:: ipython | |
230 |
|
229 | |||
@@ -239,11 +238,3 b' Magic with two percent sign can spread over multiple lines, but does not support' | |||||
239 | devfs 190Ki 190Ki 0Bi 100% 656 0 100% /dev |
|
238 | devfs 190Ki 190Ki 0Bi 100% 656 0 100% /dev | |
240 | map -hosts 0Bi 0Bi 0Bi 100% 0 0 100% /net |
|
239 | map -hosts 0Bi 0Bi 0Bi 100% 0 0 100% /net | |
241 | map auto_home 0Bi 0Bi 0Bi 100% 0 0 100% /hom |
|
240 | map auto_home 0Bi 0Bi 0Bi 100% 0 0 100% /hom | |
242 |
|
||||
243 |
|
||||
244 | Combining it all |
|
|||
245 | ---------------- |
|
|||
246 |
|
||||
247 | :: |
|
|||
248 |
|
||||
249 | find a snippet that combine all that into one thing! |
|
@@ -11,8 +11,11 b' You start IPython with the command::' | |||||
11 |
|
11 | |||
12 | $ ipython [options] files |
|
12 | $ ipython [options] files | |
13 |
|
13 | |||
14 |
If invoked with no options, it executes |
|
14 | If invoked with no options, it executes the file and exits, passing the | |
15 | exits. If you add the ``-i`` flag, it drops you into the interpreter while still |
|
15 | remaining arguments to the script, just as if you had specified the same | |
|
16 | command with python. You may need to specify `--` before args to be passed | |||
|
17 | to the script, to prevent IPython from attempting to parse them. | |||
|
18 | If you add the ``-i`` flag, it drops you into the interpreter while still | |||
16 | acknowledging any options you may have set in your ``ipython_config.py``. This |
|
19 | acknowledging any options you may have set in your ``ipython_config.py``. This | |
17 | behavior is different from standard Python, which when called as python ``-i`` |
|
20 | behavior is different from standard Python, which when called as python ``-i`` | |
18 | will only execute one file and ignore your configuration setup. |
|
21 | will only execute one file and ignore your configuration setup. | |
@@ -41,7 +44,7 b' the command-line by passing the full class name and a corresponding value; type' | |||||
41 | <...snip...> |
|
44 | <...snip...> | |
42 | --matplotlib=<CaselessStrEnum> (InteractiveShellApp.matplotlib) |
|
45 | --matplotlib=<CaselessStrEnum> (InteractiveShellApp.matplotlib) | |
43 | Default: None |
|
46 | Default: None | |
44 | Choices: ['auto', 'gtk', 'gtk3', 'inline', 'nbagg', 'notebook', 'osx', 'qt', 'qt4', 'qt5', 'tk', 'wx'] |
|
47 | Choices: ['auto', 'gtk', 'gtk3', 'gtk4', 'inline', 'nbagg', 'notebook', 'osx', 'qt', 'qt4', 'qt5', 'tk', 'wx'] | |
45 | Configure matplotlib for interactive use with the default matplotlib |
|
48 | Configure matplotlib for interactive use with the default matplotlib | |
46 | backend. |
|
49 | backend. | |
47 | <...snip...> |
|
50 | <...snip...> | |
@@ -899,7 +902,8 b' For users, enabling GUI event loop integration is simple. You simple use the' | |||||
899 | %gui [GUINAME] |
|
902 | %gui [GUINAME] | |
900 |
|
903 | |||
901 | With no arguments, ``%gui`` removes all GUI support. Valid ``GUINAME`` |
|
904 | With no arguments, ``%gui`` removes all GUI support. Valid ``GUINAME`` | |
902 |
arguments include ``wx``, ``qt``, ``qt5``, ``gtk``, ``gtk3`` |
|
905 | arguments include ``wx``, ``qt``, ``qt5``, ``gtk``, ``gtk3`` ``gtk4``, and | |
|
906 | ``tk``. | |||
903 |
|
907 | |||
904 | Thus, to use wxPython interactively and create a running :class:`wx.App` |
|
908 | Thus, to use wxPython interactively and create a running :class:`wx.App` | |
905 | object, do:: |
|
909 | object, do:: |
@@ -1,5 +1,11 b'' | |||||
1 | .. _ipython_as_shell: |
|
1 | .. _ipython_as_shell: | |
2 |
|
2 | |||
|
3 | .. note:: | |||
|
4 | ||||
|
5 | This page has been kept for historical reason. You most likely want to use | |||
|
6 | `Xonsh <https://xon.sh/>`__ instead of this. | |||
|
7 | ||||
|
8 | ||||
3 | ========================= |
|
9 | ========================= | |
4 | IPython as a system shell |
|
10 | IPython as a system shell | |
5 | ========================= |
|
11 | ========================= | |
@@ -44,6 +50,10 b' so you should be able to type any normal system command and have it executed.' | |||||
44 | See ``%alias?`` and ``%unalias?`` for details on the alias facilities. See also |
|
50 | See ``%alias?`` and ``%unalias?`` for details on the alias facilities. See also | |
45 | ``%rehashx?`` for details on the mechanism used to load $PATH. |
|
51 | ``%rehashx?`` for details on the mechanism used to load $PATH. | |
46 |
|
52 | |||
|
53 | .. warning:: | |||
|
54 | ||||
|
55 | See info at the top of the page. You most likely want to use | |||
|
56 | `Xonsh <https://xon.sh/>`__ instead of this. | |||
47 |
|
57 | |||
48 | Directory management |
|
58 | Directory management | |
49 | ==================== |
|
59 | ==================== | |
@@ -196,3 +206,9 b' provide a convenient ways to use contained text in different formats:' | |||||
196 | * ``.s`` returns string with lines separated by single space (for |
|
206 | * ``.s`` returns string with lines separated by single space (for | |
197 | convenient passing to system commands) |
|
207 | convenient passing to system commands) | |
198 | * ``.p`` returns list of "path" objects from detected file names |
|
208 | * ``.p`` returns list of "path" objects from detected file names | |
|
209 | ||||
|
210 | .. error:: | |||
|
211 | ||||
|
212 | You went too far scroll back up. You most likely want to use | |||
|
213 | `Xonsh <https://xon.sh/>`__ instead of this. | |||
|
214 |
@@ -90,7 +90,7 b' completion also works on file and directory names.' | |||||
90 | Starting with IPython 6.0, if ``jedi`` is installed, IPython will try to pull |
|
90 | Starting with IPython 6.0, if ``jedi`` is installed, IPython will try to pull | |
91 | completions from Jedi as well. This allows to not only inspect currently |
|
91 | completions from Jedi as well. This allows to not only inspect currently | |
92 | existing objects, but also to infer completion statically without executing |
|
92 | existing objects, but also to infer completion statically without executing | |
93 | code. There is nothing particular need to get this to work, simply use tab |
|
93 | code. There is nothing particular needed to get this to work, simply use tab | |
94 | completion on more complex expressions like the following:: |
|
94 | completion on more complex expressions like the following:: | |
95 |
|
95 | |||
96 | >>> data = ['Number of users', 123456] |
|
96 | >>> data = ['Number of users', 123456] |
@@ -219,7 +219,7 b' different numbers which correspond to the Process ID of the kernel.' | |||||
219 |
|
219 | |||
220 | You can read more about using `jupyter qtconsole |
|
220 | You can read more about using `jupyter qtconsole | |
221 | <https://jupyter.org/qtconsole/>`_, and |
|
221 | <https://jupyter.org/qtconsole/>`_, and | |
222 | `jupyter notebook <http://jupyter-notebook.readthedocs.io/en/latest/>`_. There |
|
222 | `jupyter notebook <https://jupyter-notebook.readthedocs.io/en/latest/>`_. There | |
223 | is also a :ref:`message spec <messaging>` which documents the protocol for |
|
223 | is also a :ref:`message spec <messaging>` which documents the protocol for | |
224 | communication between kernels |
|
224 | communication between kernels | |
225 | and clients. |
|
225 | and clients. | |
@@ -234,7 +234,7 b' Interactive parallel computing' | |||||
234 |
|
234 | |||
235 |
|
235 | |||
236 | This functionality is optional and now part of the `ipyparallel |
|
236 | This functionality is optional and now part of the `ipyparallel | |
237 | <http://ipyparallel.readthedocs.io/>`_ project. |
|
237 | <https://ipyparallel.readthedocs.io/>`_ project. | |
238 |
|
238 | |||
239 | Portability and Python requirements |
|
239 | Portability and Python requirements | |
240 | ----------------------------------- |
|
240 | ----------------------------------- |
@@ -152,6 +152,24 b' and "??", in much the same way it can be done when using the IPython prompt::' | |||||
152 |
|
152 | |||
153 | Previously, "pinfo" or "pinfo2" command had to be used for this purpose. |
|
153 | Previously, "pinfo" or "pinfo2" command had to be used for this purpose. | |
154 |
|
154 | |||
|
155 | ||||
|
156 | Autoreload 3 feature | |||
|
157 | ==================== | |||
|
158 | ||||
|
159 | Example: When an IPython session is ran with the 'autoreload' extension loaded, | |||
|
160 | you will now have the option '3' to select which means the following: | |||
|
161 | ||||
|
162 | 1. replicate all functionality from option 2 | |||
|
163 | 2. autoload all new funcs/classes/enums/globals from the module when they're added | |||
|
164 | 3. autoload all newly imported funcs/classes/enums/globals from external modules | |||
|
165 | ||||
|
166 | Try ``%autoreload 3`` in an IPython session after running ``%load_ext autoreload`` | |||
|
167 | ||||
|
168 | For more information please see unit test - | |||
|
169 | extensions/tests/test_autoreload.py : 'test_autoload_newly_added_objects' | |||
|
170 | ||||
|
171 | ======= | |||
|
172 | ||||
155 | .. DO NOT EDIT THIS LINE BEFORE RELEASE. FEATURE INSERTION POINT. |
|
173 | .. DO NOT EDIT THIS LINE BEFORE RELEASE. FEATURE INSERTION POINT. | |
156 |
|
174 | |||
157 | As a reminder, IPython master has diverged from the 7.x branch, thus master may |
|
175 | As a reminder, IPython master has diverged from the 7.x branch, thus master may |
@@ -1099,7 +1099,7 b' Pull Requests (793):' | |||||
1099 | * :ghpull:`2274`: CLN: Use name to id mapping of notebooks instead of searching. |
|
1099 | * :ghpull:`2274`: CLN: Use name to id mapping of notebooks instead of searching. | |
1100 | * :ghpull:`2270`: SSHLauncher tweaks |
|
1100 | * :ghpull:`2270`: SSHLauncher tweaks | |
1101 | * :ghpull:`2269`: add missing location when disambiguating controller IP |
|
1101 | * :ghpull:`2269`: add missing location when disambiguating controller IP | |
1102 | * :ghpull:`2263`: Allow docs to build on http://readthedocs.io/ |
|
1102 | * :ghpull:`2263`: Allow docs to build on https://readthedocs.io/ | |
1103 | * :ghpull:`2256`: Adding data publication example notebook. |
|
1103 | * :ghpull:`2256`: Adding data publication example notebook. | |
1104 | * :ghpull:`2255`: better flush iopub with AsyncResults |
|
1104 | * :ghpull:`2255`: better flush iopub with AsyncResults | |
1105 | * :ghpull:`2261`: Fix: longest_substr([]) -> '' |
|
1105 | * :ghpull:`2261`: Fix: longest_substr([]) -> '' |
@@ -309,7 +309,7 b' be started by calling ``ipython qtconsole``. The protocol is :ref:`documented' | |||||
309 | <messaging>`. |
|
309 | <messaging>`. | |
310 |
|
310 | |||
311 | The parallel computing framework has also been rewritten using ZMQ. The |
|
311 | The parallel computing framework has also been rewritten using ZMQ. The | |
312 |
protocol is described :ref:`here <parallel |
|
312 | protocol is described :ref:`here <ipyparallel:/reference/messages.md>`, and the code is in the | |
313 | new :mod:`IPython.parallel` module. |
|
313 | new :mod:`IPython.parallel` module. | |
314 |
|
314 | |||
315 | .. _python3_011: |
|
315 | .. _python3_011: |
@@ -307,7 +307,7 b" and tab completions that don't clutter up your history." | |||||
307 | :target: ../_images/ptshell_features.png |
|
307 | :target: ../_images/ptshell_features.png | |
308 |
|
308 | |||
309 | These features are provided by the Python library `prompt_toolkit |
|
309 | These features are provided by the Python library `prompt_toolkit | |
310 | <http://python-prompt-toolkit.readthedocs.io/en/stable/>`__, which replaces |
|
310 | <https://python-prompt-toolkit.readthedocs.io/en/stable/>`__, which replaces | |
311 | ``readline`` throughout our terminal interface. |
|
311 | ``readline`` throughout our terminal interface. | |
312 |
|
312 | |||
313 | Relying on this pure-Python, cross platform module also makes it simpler to |
|
313 | Relying on this pure-Python, cross platform module also makes it simpler to |
@@ -2,6 +2,309 b'' | |||||
2 | 7.x Series |
|
2 | 7.x Series | |
3 | ============ |
|
3 | ============ | |
4 |
|
4 | |||
|
5 | .. _version 7.28: | |||
|
6 | ||||
|
7 | IPython 7.28 | |||
|
8 | ============ | |||
|
9 | ||||
|
10 | ||||
|
11 | IPython 7.28 is again a minor release that mostly bring bugfixes, and couple of | |||
|
12 | improvement. Many thanks to MrMino, who again did all the work this month, and | |||
|
13 | made a number of documentation improvements. | |||
|
14 | ||||
|
15 | Here is a non-exhaustive list of changes, | |||
|
16 | ||||
|
17 | Fixes: | |||
|
18 | ||||
|
19 | - async with doesn't allow newlines :ghpull:`13090` | |||
|
20 | - Dynamically changing to vi mode via %config magic) :ghpull:`13091` | |||
|
21 | ||||
|
22 | Virtualenv handling fixes: | |||
|
23 | ||||
|
24 | - init_virtualenv now uses Pathlib :ghpull:`12548` | |||
|
25 | - Fix Improper path comparison of virtualenv directories :ghpull:`13140` | |||
|
26 | - Fix virtual environment user warning for lower case pathes :ghpull:`13094` | |||
|
27 | - Adapt to all sorts of drive names for cygwin :ghpull:`13153` | |||
|
28 | ||||
|
29 | New Features: | |||
|
30 | ||||
|
31 | - enable autoplay in embed YouTube player :ghpull:`13133` | |||
|
32 | ||||
|
33 | Documentation: | |||
|
34 | ||||
|
35 | - Fix formatting for the core.interactiveshell documentation :ghpull:`13118` | |||
|
36 | - Fix broken ipyparallel's refs :ghpull:`13138` | |||
|
37 | - Improve formatting of %time documentation :ghpull:`13125` | |||
|
38 | - Reword the YouTubeVideo autoplay WN :ghpull:`13147` | |||
|
39 | ||||
|
40 | ||||
|
41 | Thanks | |||
|
42 | ------ | |||
|
43 | ||||
|
44 | Many thanks to all the contributors to this release. You can find all individual | |||
|
45 | contributions to this milestone `on github | |||
|
46 | <https://github.com/ipython/ipython/milestone/92>`__. | |||
|
47 | ||||
|
48 | Thanks as well to the `D. E. Shaw group <https://deshaw.com/>`__ for sponsoring | |||
|
49 | work on IPython and related libraries. | |||
|
50 | ||||
|
51 | ||||
|
52 | .. _version 7.27: | |||
|
53 | ||||
|
54 | IPython 7.27 | |||
|
55 | ============ | |||
|
56 | ||||
|
57 | IPython 7.27 is a minor release that fixes a couple of issues and compatibility. | |||
|
58 | ||||
|
59 | - Add support for GTK4 :ghpull:`131011` | |||
|
60 | - Add support for Qt6 :ghpull:`13085` | |||
|
61 | - Fix an issue with pip magic on windows :ghpull:`13093` | |||
|
62 | ||||
|
63 | Thanks | |||
|
64 | ------ | |||
|
65 | ||||
|
66 | Many thanks to all the contributors to this release. You can find all individual | |||
|
67 | contributions to this milestone `on github | |||
|
68 | <https://github.com/ipython/ipython/milestone/91>`__. | |||
|
69 | ||||
|
70 | Thanks as well to the `D. E. Shaw group <https://deshaw.com/>`__ for sponsoring | |||
|
71 | work on IPython and related libraries. | |||
|
72 | ||||
|
73 | .. _version 7.26: | |||
|
74 | ||||
|
75 | IPython 7.26 | |||
|
76 | ============ | |||
|
77 | ||||
|
78 | IPython 7.26 is a minor release that fixes a couple of issues, updates in API | |||
|
79 | and Copyright/Licenses issues around various part of the codebase. | |||
|
80 | ||||
|
81 | We'll highlight `this issue <https://github.com/ipython/ipython/issues/13039>` | |||
|
82 | pointing out we were including and refereeing to code from Stack Overflow which | |||
|
83 | was CC-BY-SA, hence incompatible with the BSD license of IPython. This lead us | |||
|
84 | to a rewriting of the corresponding logic which in our case was done in a more | |||
|
85 | efficient way (in our case we were searching string prefixes instead of full | |||
|
86 | strings). | |||
|
87 | ||||
|
88 | You will notice also a number of documentation improvements and cleanup. | |||
|
89 | ||||
|
90 | Of particular interest are the following Pull-requests: | |||
|
91 | ||||
|
92 | ||||
|
93 | - The IPython directive now uses Sphinx logging for warnings. :ghpull:`13030`. | |||
|
94 | - Add expiry days option to pastebin magic and change http protocol to https. | |||
|
95 | :ghpull:`13056` | |||
|
96 | - Make Ipython.utils.timing work with jupyterlite :ghpull:`13050`. | |||
|
97 | ||||
|
98 | ||||
|
99 | ||||
|
100 | Thanks | |||
|
101 | ------ | |||
|
102 | ||||
|
103 | Many thanks to all the contributors to this release and in particular MrMino who | |||
|
104 | is doing most of the work those days. You can find all individual contributions | |||
|
105 | to this milestone `on github <https://github.com/ipython/ipython/milestone/90>`__. | |||
|
106 | ||||
|
107 | Thanks as well to the `D. E. Shaw group <https://deshaw.com/>`__ for sponsoring | |||
|
108 | work on IPython and related libraries. | |||
|
109 | ||||
|
110 | ||||
|
111 | .. _version 7.25: | |||
|
112 | ||||
|
113 | IPython 7.25 | |||
|
114 | ============ | |||
|
115 | ||||
|
116 | IPython 7.25 is a minor release that contains a single bugfix, which is highly | |||
|
117 | recommended for all users of ipdb, ipython debugger %debug magic and similar. | |||
|
118 | ||||
|
119 | Issuing commands like ``where`` from within the debugger would reset the | |||
|
120 | local variables changes made by the user. It is interesting to look at the root | |||
|
121 | cause of the issue as accessing an attribute (``frame.f_locals``) would trigger | |||
|
122 | this side effects. | |||
|
123 | ||||
|
124 | Thanks in particular to the patience from the reporters at D.E. Shaw for their | |||
|
125 | initial bug report that was due to a similar coding oversight in an extension, | |||
|
126 | and who took time to debug and narrow down the problem. | |||
|
127 | ||||
|
128 | Thanks | |||
|
129 | ------ | |||
|
130 | ||||
|
131 | Many thanks to all the contributors to this release you can find all individual | |||
|
132 | contributions to this milestone `on github <https://github.com/ipython/ipython/milestone/89>`__. | |||
|
133 | ||||
|
134 | Thanks as well to the `D. E. Shaw group <https://deshaw.com/>`__ for sponsoring | |||
|
135 | work on IPython and related libraries. | |||
|
136 | ||||
|
137 | ||||
|
138 | .. _version 7.24: | |||
|
139 | ||||
|
140 | IPython 7.24 | |||
|
141 | ============ | |||
|
142 | ||||
|
143 | Third release of IPython for 2021, mostly containing bug fixes. A couple of not | |||
|
144 | typical updates: | |||
|
145 | ||||
|
146 | Misc | |||
|
147 | ---- | |||
|
148 | ||||
|
149 | ||||
|
150 | - Fix an issue where ``%recall`` would both succeeded and print an error message | |||
|
151 | it failed. :ghpull:`12952` | |||
|
152 | - Drop support for NumPy 1.16 – practically has no effect beyond indicating in | |||
|
153 | package metadata that we do not support it. :ghpull:`12937` | |||
|
154 | ||||
|
155 | Debugger improvements | |||
|
156 | --------------------- | |||
|
157 | ||||
|
158 | The debugger (and ``%debug`` magic) have been improved and can skip or hide frames | |||
|
159 | originating from files that are not writable to the user, as these are less | |||
|
160 | likely to be the source of errors, or be part of system files this can be a useful | |||
|
161 | addition when debugging long errors. | |||
|
162 | ||||
|
163 | In addition to the global ``skip_hidden True|False`` command, the debugger has | |||
|
164 | gained finer grained control of predicates as to whether to a frame should be | |||
|
165 | considered hidden. So far 3 predicates are available : | |||
|
166 | ||||
|
167 | - ``tbhide``: frames containing the local variable ``__tracebackhide__`` set to | |||
|
168 | True. | |||
|
169 | - ``readonly``: frames originating from readonly files, set to False. | |||
|
170 | - ``ipython_internal``: frames that are likely to be from IPython internal | |||
|
171 | code, set to True. | |||
|
172 | ||||
|
173 | You can toggle individual predicates during a session with | |||
|
174 | ||||
|
175 | .. code-block:: | |||
|
176 | ||||
|
177 | ipdb> skip_predicates readonly True | |||
|
178 | ||||
|
179 | Read-only files will now be considered hidden frames. | |||
|
180 | ||||
|
181 | ||||
|
182 | You can call ``skip_predicates`` without arguments to see the states of current | |||
|
183 | predicates: | |||
|
184 | ||||
|
185 | .. code-block:: | |||
|
186 | ||||
|
187 | ipdb> skip_predicates | |||
|
188 | current predicates: | |||
|
189 | tbhide : True | |||
|
190 | readonly : False | |||
|
191 | ipython_internal : True | |||
|
192 | ||||
|
193 | If all predicates are set to ``False``, ``skip_hidden`` will practically have | |||
|
194 | no effect. We attempt to warn you when all predicates are False. | |||
|
195 | ||||
|
196 | Note that the ``readonly`` predicate may increase disk access as we check for | |||
|
197 | file access permission for all frames on many command invocation, but is usually | |||
|
198 | cached by operating systems. Let us know if you encounter any issues. | |||
|
199 | ||||
|
200 | As the IPython debugger does not use the traitlets infrastructure for | |||
|
201 | configuration, by editing your ``.pdbrc`` files and appending commands you would | |||
|
202 | like to be executed just before entering the interactive prompt. For example: | |||
|
203 | ||||
|
204 | ||||
|
205 | .. code:: | |||
|
206 | ||||
|
207 | # file : ~/.pdbrc | |||
|
208 | skip_predicates readonly True | |||
|
209 | skip_predicates tbhide False | |||
|
210 | ||||
|
211 | Will hide read only frames by default and show frames marked with | |||
|
212 | ``__tracebackhide__``. | |||
|
213 | ||||
|
214 | ||||
|
215 | ||||
|
216 | ||||
|
217 | Thanks | |||
|
218 | ------ | |||
|
219 | ||||
|
220 | Many thanks to all the contributors to this release you can find all individual | |||
|
221 | contributions to this milestone `on github <https://github.com/ipython/ipython/milestone/87>`__. | |||
|
222 | ||||
|
223 | Thanks as well to the `D. E. Shaw group <https://deshaw.com/>`__ for sponsoring | |||
|
224 | work on IPython and related libraries, in particular above mentioned | |||
|
225 | improvements to the debugger. | |||
|
226 | ||||
|
227 | ||||
|
228 | ||||
|
229 | ||||
|
230 | .. _version 7.23: | |||
|
231 | ||||
|
232 | IPython 7.23 and 7.23.1 | |||
|
233 | ======================= | |||
|
234 | ||||
|
235 | ||||
|
236 | Third release of IPython for 2021, mostly containing bug fixes. A couple of not | |||
|
237 | typical updates: | |||
|
238 | ||||
|
239 | - We moved to GitHub actions away from Travis-CI, the transition may not be | |||
|
240 | 100% complete (not testing on nightly anymore), but as we ran out of | |||
|
241 | Travis-Ci hours on the IPython organisation that was a necessary step. | |||
|
242 | :ghpull:`12900`. | |||
|
243 | ||||
|
244 | - We have a new dependency: ``matplotlib-inline``, which try to extract | |||
|
245 | matplotlib inline backend specific behavior. It is available on PyPI and | |||
|
246 | conda-forge thus should not be a problem to upgrade to this version. If you | |||
|
247 | are a package maintainer that might be an extra dependency to package first. | |||
|
248 | :ghpull:`12817` (IPython 7.23.1 fix a typo that made this change fail) | |||
|
249 | ||||
|
250 | In the addition/new feature category, ``display()`` now have a ``clear=True`` | |||
|
251 | option to clear the display if any further outputs arrives, allowing users to | |||
|
252 | avoid having to use ``clear_output()`` directly. :ghpull:`12823`. | |||
|
253 | ||||
|
254 | In bug fixes category, this release fix an issue when printing tracebacks | |||
|
255 | containing Unicode characters :ghpull:`12758`. | |||
|
256 | ||||
|
257 | In code cleanup category :ghpull:`12932` remove usage of some deprecated | |||
|
258 | functionality for compatibility with Python 3.10. | |||
|
259 | ||||
|
260 | ||||
|
261 | ||||
|
262 | Thanks | |||
|
263 | ------ | |||
|
264 | ||||
|
265 | Many thanks to all the contributors to this release you can find all individual | |||
|
266 | contributions to this milestone `on github <https://github.com/ipython/ipython/milestone/86>`__. | |||
|
267 | In particular MrMino for responding to almost all new issues, and triaging many | |||
|
268 | of the old ones, as well as takluyver, minrk, willingc for reacting quikly when | |||
|
269 | we ran out of CI Hours. | |||
|
270 | ||||
|
271 | Thanks as well to organisations, QuantStack (martinRenou and SylvainCorlay) for | |||
|
272 | extracting matplotlib inline backend into its own package, and the `D. E. Shaw group | |||
|
273 | <https://deshaw.com/>`__ for sponsoring work on IPython and related libraries. | |||
|
274 | ||||
|
275 | ||||
|
276 | .. _version 7.22: | |||
|
277 | ||||
|
278 | IPython 7.22 | |||
|
279 | ============ | |||
|
280 | ||||
|
281 | Second release of IPython for 2021, mostly containing bug fixes. Here is a quick | |||
|
282 | rundown of the few changes. | |||
|
283 | ||||
|
284 | - Fix some ``sys.excepthook`` shenanigan when embedding with qt, recommended if | |||
|
285 | you – for example – use `napari <https://napari.org>`__. :ghpull:`12842`. | |||
|
286 | - Fix bug when using the new ipdb ``%context`` magic :ghpull:`12844` | |||
|
287 | - Couples of deprecation cleanup :ghpull:`12868` | |||
|
288 | - Update for new dpast.com api if you use the ``%pastbin`` magic. :ghpull:`12712` | |||
|
289 | - Remove support for numpy before 1.16. :ghpull:`12836` | |||
|
290 | ||||
|
291 | ||||
|
292 | Thanks | |||
|
293 | ------ | |||
|
294 | ||||
|
295 | We have a new team member that you should see more often on the IPython | |||
|
296 | repository, Błażej Michalik (@MrMino) have been doing regular contributions to | |||
|
297 | IPython, and spent time replying to many issues and guiding new users to the | |||
|
298 | codebase; they now have triage permissions to the IPython repository and we'll | |||
|
299 | work toward giving them more permission in the future. | |||
|
300 | ||||
|
301 | Many thanks to all the contributors to this release you can find all individual | |||
|
302 | contributions to this milestone `on github <https://github.com/ipython/ipython/milestone/84>`__. | |||
|
303 | ||||
|
304 | Thanks as well to organisations, QuantStack for working on debugger | |||
|
305 | compatibility for Xeus_python, and the `D. E. Shaw group | |||
|
306 | <https://deshaw.com/>`__ for sponsoring work on IPython and related libraries. | |||
|
307 | ||||
5 | .. _version 721: |
|
308 | .. _version 721: | |
6 |
|
309 | |||
7 | IPython 7.21 |
|
310 | IPython 7.21 | |
@@ -33,7 +336,7 b' Thanks' | |||||
33 | ------ |
|
336 | ------ | |
34 |
|
337 | |||
35 | Many thanks to all the contributors to this release you can find all individual |
|
338 | Many thanks to all the contributors to this release you can find all individual | |
36 | contribution to this milestone `on github <https://github.com/ipython/ipython/milestone/83>`_. |
|
339 | contribution to this milestone `on github <https://github.com/ipython/ipython/milestone/83>`__. | |
37 |
|
340 | |||
38 |
|
341 | |||
39 | .. _version 720: |
|
342 | .. _version 720: | |
@@ -82,14 +385,14 b' was exceptionally no release last month.' | |||||
82 | - Docs docs formatting that make the install commands work on zsh |
|
385 | - Docs docs formatting that make the install commands work on zsh | |
83 | :ghpull:`12587` |
|
386 | :ghpull:`12587` | |
84 | - Always display the last frame in tracebacks even if hidden with |
|
387 | - Always display the last frame in tracebacks even if hidden with | |
85 |
``__traceback |
|
388 | ``__tracebackhide__`` :ghpull:`12601` | |
86 | - Avoid an issue where a callback can be registered multiple times. |
|
389 | - Avoid an issue where a callback can be registered multiple times. | |
87 | :ghpull:`12625` |
|
390 | :ghpull:`12625` | |
88 | - Avoid an issue in debugger mode where frames changes could be lost. |
|
391 | - Avoid an issue in debugger mode where frames changes could be lost. | |
89 | :ghpull:`12627` |
|
392 | :ghpull:`12627` | |
90 |
|
393 | |||
91 | - Never hide the frames that invoke a debugger, even if marked as hidden by |
|
394 | - Never hide the frames that invoke a debugger, even if marked as hidden by | |
92 |
``__traceback |
|
395 | ``__tracebackhide__`` :ghpull:`12631` | |
93 | - Fix calling the debugger in a recursive manner :ghpull:`12659` |
|
396 | - Fix calling the debugger in a recursive manner :ghpull:`12659` | |
94 |
|
397 | |||
95 |
|
398 | |||
@@ -240,7 +543,7 b' Change of API and exposed objects automatically detected using `frappuccino' | |||||
240 | <https://pypi.org/project/frappuccino/>`_ (still in beta): |
|
543 | <https://pypi.org/project/frappuccino/>`_ (still in beta): | |
241 |
|
544 | |||
242 |
|
545 | |||
243 |
The following items are new and mostly related to understanding ``__tracebackb |
|
546 | The following items are new and mostly related to understanding ``__tracebackbide__``:: | |
244 |
|
547 | |||
245 | + IPython.core.debugger.Pdb.do_down(self, arg) |
|
548 | + IPython.core.debugger.Pdb.do_down(self, arg) | |
246 | + IPython.core.debugger.Pdb.do_skip_hidden(self, arg) |
|
549 | + IPython.core.debugger.Pdb.do_skip_hidden(self, arg) |
@@ -150,6 +150,7 b'' | |||||
150 | " <a href='gui/gui-glut.py' target='_blank'>gui-glut.py</a><br>\n", |
|
150 | " <a href='gui/gui-glut.py' target='_blank'>gui-glut.py</a><br>\n", | |
151 | " <a href='gui/gui-gtk.py' target='_blank'>gui-gtk.py</a><br>\n", |
|
151 | " <a href='gui/gui-gtk.py' target='_blank'>gui-gtk.py</a><br>\n", | |
152 | " <a href='gui/gui-gtk3.py' target='_blank'>gui-gtk3.py</a><br>\n", |
|
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 | " <a href='gui/gui-pyglet.py' target='_blank'>gui-pyglet.py</a><br>\n", |
|
154 | " <a href='gui/gui-pyglet.py' target='_blank'>gui-pyglet.py</a><br>\n", | |
154 | " <a href='gui/gui-qt.py' target='_blank'>gui-qt.py</a><br>\n", |
|
155 | " <a href='gui/gui-qt.py' target='_blank'>gui-qt.py</a><br>\n", | |
155 | " <a href='gui/gui-tk.py' target='_blank'>gui-tk.py</a><br>\n", |
|
156 | " <a href='gui/gui-tk.py' target='_blank'>gui-tk.py</a><br>\n", | |
@@ -160,6 +161,7 b'' | |||||
160 | " gui-glut.py\n", |
|
161 | " gui-glut.py\n", | |
161 | " gui-gtk.py\n", |
|
162 | " gui-gtk.py\n", | |
162 | " gui-gtk3.py\n", |
|
163 | " gui-gtk3.py\n", | |
|
164 | " gui-gtk4.py\n", | |||
163 | " gui-pyglet.py\n", |
|
165 | " gui-pyglet.py\n", | |
164 | " gui-qt.py\n", |
|
166 | " gui-qt.py\n", | |
165 | " gui-tk.py\n", |
|
167 | " gui-tk.py\n", |
@@ -3180,6 +3180,7 b'' | |||||
3180 | " <a href='./gui/gui-glut.py' target='_blank'>gui-glut.py</a><br>\n", |
|
3180 | " <a href='./gui/gui-glut.py' target='_blank'>gui-glut.py</a><br>\n", | |
3181 | " <a href='./gui/gui-gtk.py' target='_blank'>gui-gtk.py</a><br>\n", |
|
3181 | " <a href='./gui/gui-gtk.py' target='_blank'>gui-gtk.py</a><br>\n", | |
3182 | " <a href='./gui/gui-gtk3.py' target='_blank'>gui-gtk3.py</a><br>\n", |
|
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 | " <a href='./gui/gui-pyglet.py' target='_blank'>gui-pyglet.py</a><br>\n", |
|
3184 | " <a href='./gui/gui-pyglet.py' target='_blank'>gui-pyglet.py</a><br>\n", | |
3184 | " <a href='./gui/gui-qt.py' target='_blank'>gui-qt.py</a><br>\n", |
|
3185 | " <a href='./gui/gui-qt.py' target='_blank'>gui-qt.py</a><br>\n", | |
3185 | " <a href='./gui/gui-tk.py' target='_blank'>gui-tk.py</a><br>\n", |
|
3186 | " <a href='./gui/gui-tk.py' target='_blank'>gui-tk.py</a><br>\n", | |
@@ -3230,6 +3231,7 b'' | |||||
3230 | " gui-glut.py\n", |
|
3231 | " gui-glut.py\n", | |
3231 | " gui-gtk.py\n", |
|
3232 | " gui-gtk.py\n", | |
3232 | " gui-gtk3.py\n", |
|
3233 | " gui-gtk3.py\n", | |
|
3234 | " gui-gtk4.py\n", | |||
3233 | " gui-pyglet.py\n", |
|
3235 | " gui-pyglet.py\n", | |
3234 | " gui-qt.py\n", |
|
3236 | " gui-qt.py\n", | |
3235 | " gui-tk.py\n", |
|
3237 | " gui-tk.py\n", |
@@ -1,2 +1,7 b'' | |||||
1 | [metadata] |
|
1 | [metadata] | |
2 | license_file = LICENSE |
|
2 | license_file = LICENSE | |
|
3 | ||||
|
4 | [velin] | |||
|
5 | ignore_patterns = | |||
|
6 | IPython/core/tests, | |||
|
7 | IPython/testing |
@@ -171,27 +171,36 b' setuptools_extra_args = {}' | |||||
171 | # setuptools requirements |
|
171 | # setuptools requirements | |
172 |
|
172 | |||
173 | extras_require = dict( |
|
173 | extras_require = dict( | |
174 |
parallel |
|
174 | parallel=["ipyparallel"], | |
175 |
qtconsole |
|
175 | qtconsole=["qtconsole"], | |
176 |
doc |
|
176 | doc=["Sphinx>=1.3"], | |
177 | test = ['nose>=0.10.1', 'requests', 'testpath', 'pygments', 'nbformat', 'ipykernel', 'numpy>=1.14'], |
|
177 | test=[ | |
|
178 | "nose>=0.10.1", | |||
|
179 | "requests", | |||
|
180 | "testpath", | |||
|
181 | "pygments", | |||
|
182 | "nbformat", | |||
|
183 | "ipykernel", | |||
|
184 | "numpy>=1.17", | |||
|
185 | ], | |||
178 |
terminal |
|
186 | terminal=[], | |
179 |
kernel |
|
187 | kernel=["ipykernel"], | |
180 |
nbformat |
|
188 | nbformat=["nbformat"], | |
181 |
notebook |
|
189 | notebook=["notebook", "ipywidgets"], | |
182 |
nbconvert |
|
190 | nbconvert=["nbconvert"], | |
183 | ) |
|
191 | ) | |
184 |
|
192 | |||
185 | install_requires = [ |
|
193 | install_requires = [ | |
186 |
|
|
194 | "setuptools>=18.5", | |
187 |
|
|
195 | "jedi>=0.16", | |
188 |
|
|
196 | "decorator", | |
189 |
|
|
197 | "pickleshare", | |
190 |
|
|
198 | "traitlets>=4.2", | |
191 |
|
|
199 | "prompt_toolkit>=2.0.0,<3.1.0,!=3.0.0,!=3.0.1", | |
192 |
|
|
200 | "pygments", | |
193 |
|
|
201 | "backcall", | |
194 |
|
|
202 | "stack_data", | |
|
203 | "matplotlib-inline", | |||
195 | ] |
|
204 | ] | |
196 |
|
205 | |||
197 | # Platform-specific dependencies: |
|
206 | # Platform-specific dependencies: |
@@ -21,6 +21,7 b' WHITE=$(tput setaf 7)' | |||||
21 | NOR=$(tput sgr0) |
|
21 | NOR=$(tput sgr0) | |
22 |
|
22 | |||
23 |
|
23 | |||
|
24 | echo "Will use $EDITOR to edit files when necessary" | |||
24 | echo -n "PREV_RELEASE (X.y.z) [$PREV_RELEASE]: " |
|
25 | echo -n "PREV_RELEASE (X.y.z) [$PREV_RELEASE]: " | |
25 | read input |
|
26 | read input | |
26 | PREV_RELEASE=${input:-$PREV_RELEASE} |
|
27 | PREV_RELEASE=${input:-$PREV_RELEASE} | |
@@ -47,6 +48,18 b' ask_section(){' | |||||
47 | } |
|
48 | } | |
48 |
|
49 | |||
49 |
|
50 | |||
|
51 | maybe_edit(){ | |||
|
52 | echo | |||
|
53 | echo $BLUE"$1"$NOR | |||
|
54 | echo -n $GREEN"Press e to Edit $1, any other keys to skip: "$NOR | |||
|
55 | read -n1 value | |||
|
56 | echo | |||
|
57 | if [ $value = 'e' ] ; then | |||
|
58 | $EDITOR $1 | |||
|
59 | fi | |||
|
60 | } | |||
|
61 | ||||
|
62 | ||||
50 |
|
63 | |||
51 | echo |
|
64 | echo | |
52 | if ask_section "Updating what's new with informations from docs/source/whatsnew/pr" |
|
65 | if ask_section "Updating what's new with informations from docs/source/whatsnew/pr" | |
@@ -104,6 +117,7 b' echo $GREEN"I tried ${RED}sed -i bkp -e \'/Uncomment/s/^# //g\' IPython/core/relea' | |||||
104 | sed -i bkp -e '/Uncomment/s/^# //g' IPython/core/release.py |
|
117 | sed -i bkp -e '/Uncomment/s/^# //g' IPython/core/release.py | |
105 | rm IPython/core/release.pybkp |
|
118 | rm IPython/core/release.pybkp | |
106 | git diff |
|
119 | git diff | |
|
120 | maybe_edit IPython/core/release.py | |||
107 |
|
121 | |||
108 | echo $GREEN"Press enter to continue"$NOR |
|
122 | echo $GREEN"Press enter to continue"$NOR | |
109 | read |
|
123 | read | |
@@ -151,6 +165,7 b' then' | |||||
151 | rm IPython/core/release.pybkp |
|
165 | rm IPython/core/release.pybkp | |
152 | git diff |
|
166 | git diff | |
153 | echo $GREEN"Please bump ${RED}the minor version number${NOR}" |
|
167 | echo $GREEN"Please bump ${RED}the minor version number${NOR}" | |
|
168 | maybe_edit IPython/core/release.py | |||
154 | echo ${BLUE}"Do not commit yet – we'll do it later."$NOR |
|
169 | echo ${BLUE}"Do not commit yet – we'll do it later."$NOR | |
155 |
|
170 | |||
156 |
|
171 |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now